From 960e9729404520447352b184b91ed41d67365ac3 Mon Sep 17 00:00:00 2001 From: grandeljay Date: Fri, 1 Dec 2023 15:08:54 +0100 Subject: [PATCH] chore(dependency): add conventional changelog --- composer.json | 15 +- composer.lock | 791 +++- package.json | 15 +- vendor/composer/autoload_classmap.php | 1 + vendor/composer/autoload_files.php | 6 + vendor/composer/autoload_psr4.php | 9 + vendor/composer/autoload_static.php | 52 + vendor/composer/installed.json | 829 ++++ vendor/composer/installed.php | 100 +- .../php-conventional-changelog/.changelog | 52 + .../.devcontainer/Dockerfile | 15 + .../.devcontainer/devcontainer.json | 55 + .../php-conventional-changelog/.gitattributes | 1 + .../.github/workflows/php.yml | 42 + .../.github/workflows/pr.yml | 12 + .../php-conventional-changelog/.gitignore | 5 + .../.php-cs-fixer.php | 47 + .../php-conventional-changelog/CHANGELOG.md | 840 ++++ .../php-conventional-changelog/LICENSE | 674 +++ .../php-conventional-changelog/README.md | 212 + .../php-conventional-changelog/TODO.md | 3 + .../php-conventional-changelog/autoload.php | 14 + .../php-conventional-changelog/composer.json | 77 + .../php-conventional-changelog/composer.lock | 4086 +++++++++++++++++ .../conventional-changelog | 33 + .../php-conventional-changelog/docs/config.md | 168 + .../docs/images/logo.png | Bin 0 -> 77910 bytes .../docs/images/usage.gif | Bin 0 -> 112523 bytes .../php-conventional-changelog/phpunit.xml | 17 + .../php-conventional-changelog/renovate.json | 6 + .../src/Changelog.php | 694 +++ .../src/Configuration.php | 933 ++++ .../src/DefaultCommand.php | 210 + .../src/Git/Commit.php | 399 ++ .../src/Git/Commit/Body.php | 23 + .../src/Git/Commit/Description.php | 23 + .../src/Git/Commit/Footer.php | 103 + .../src/Git/Commit/Mention.php | 28 + .../src/Git/Commit/Reference.php | 45 + .../src/Git/Commit/Scope.php | 37 + .../src/Git/Commit/Subject.php | 23 + .../src/Git/Commit/Type.php | 23 + .../src/Git/ConventionalCommit.php | 222 + .../src/Git/Repository.php | 363 ++ .../src/Helper/Formatter.php | 16 + .../src/Helper/SemanticVersion.php | 157 + .../src/Helper/ShellCommand.php | 40 + .../src/PackageBump/ComposerJson.php | 55 + .../src/PackageBump/PackageJson.php | 39 + .../src/Type/PackageBump.php | 195 + .../src/Type/Stringable.php | 13 + .../tests/ChangelogTest.php | 113 + 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/symfony/console/Application.php | 1331 ++++++ .../symfony/console/Attribute/AsCommand.php | 39 + vendor/symfony/console/CHANGELOG.md | 261 ++ .../console/CI/GithubActionReporter.php | 99 + vendor/symfony/console/Color.php | 133 + vendor/symfony/console/Command/Command.php | 725 +++ .../console/Command/CompleteCommand.php | 223 + .../console/Command/DumpCompletionCommand.php | 161 + .../symfony/console/Command/HelpCommand.php | 82 + .../symfony/console/Command/LazyCommand.php | 207 + .../symfony/console/Command/ListCommand.php | 75 + .../symfony/console/Command/LockableTrait.php | 68 + .../Command/SignalableCommandInterface.php | 34 + .../console/Command/TraceableCommand.php | 356 ++ .../CommandLoader/CommandLoaderInterface.php | 38 + .../CommandLoader/ContainerCommandLoader.php | 55 + .../CommandLoader/FactoryCommandLoader.php | 54 + .../console/Completion/CompletionInput.php | 248 + .../Completion/CompletionSuggestions.php | 97 + .../Output/BashCompletionOutput.php | 33 + .../Output/CompletionOutputInterface.php | 25 + .../Output/FishCompletionOutput.php | 33 + .../Completion/Output/ZshCompletionOutput.php | 36 + .../symfony/console/Completion/Suggestion.php | 41 + vendor/symfony/console/ConsoleEvents.php | 72 + vendor/symfony/console/Cursor.php | 204 + .../DataCollector/CommandDataCollector.php | 234 + vendor/symfony/console/Debug/CliRequest.php | 70 + .../AddConsoleCommandPass.php | 134 + .../Descriptor/ApplicationDescription.php | 139 + .../symfony/console/Descriptor/Descriptor.php | 74 + .../Descriptor/DescriptorInterface.php | 27 + .../console/Descriptor/JsonDescriptor.php | 166 + .../console/Descriptor/MarkdownDescriptor.php | 173 + .../Descriptor/ReStructuredTextDescriptor.php | 272 ++ .../console/Descriptor/TextDescriptor.php | 317 ++ .../console/Descriptor/XmlDescriptor.php | 232 + .../console/Event/ConsoleCommandEvent.php | 54 + .../console/Event/ConsoleErrorEvent.php | 57 + vendor/symfony/console/Event/ConsoleEvent.php | 61 + .../console/Event/ConsoleSignalEvent.php | 56 + .../console/Event/ConsoleTerminateEvent.php | 50 + .../console/EventListener/ErrorListener.php | 101 + .../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 + .../Exception/RunCommandFailedException.php | 29 + .../console/Exception/RuntimeException.php | 19 + .../console/Formatter/NullOutputFormatter.php | 51 + .../Formatter/NullOutputFormatterStyle.php | 54 + .../console/Formatter/OutputFormatter.php | 277 ++ .../Formatter/OutputFormatterInterface.php | 56 + .../Formatter/OutputFormatterStyle.php | 110 + .../OutputFormatterStyleInterface.php | 60 + .../Formatter/OutputFormatterStyleStack.php | 107 + .../WrappableOutputFormatterInterface.php | 27 + .../console/Helper/DebugFormatterHelper.php | 98 + .../console/Helper/DescriptorHelper.php | 93 + vendor/symfony/console/Helper/Dumper.php | 57 + .../console/Helper/FormatterHelper.php | 81 + vendor/symfony/console/Helper/Helper.php | 174 + .../console/Helper/HelperInterface.php | 39 + vendor/symfony/console/Helper/HelperSet.php | 77 + .../console/Helper/InputAwareHelper.php | 33 + .../symfony/console/Helper/OutputWrapper.php | 76 + .../symfony/console/Helper/ProcessHelper.php | 137 + vendor/symfony/console/Helper/ProgressBar.php | 618 +++ .../console/Helper/ProgressIndicator.php | 235 + .../symfony/console/Helper/QuestionHelper.php | 612 +++ .../console/Helper/SymfonyQuestionHelper.php | 109 + vendor/symfony/console/Helper/Table.php | 926 ++++ vendor/symfony/console/Helper/TableCell.php | 72 + .../symfony/console/Helper/TableCellStyle.php | 84 + 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 | 370 ++ vendor/symfony/console/Input/ArrayInput.php | 196 + vendor/symfony/console/Input/Input.php | 193 + .../symfony/console/Input/InputArgument.php | 154 + .../console/Input/InputAwareInterface.php | 28 + .../symfony/console/Input/InputDefinition.php | 416 ++ .../symfony/console/Input/InputInterface.php | 150 + vendor/symfony/console/Input/InputOption.php | 255 + .../Input/StreamableInputInterface.php | 39 + vendor/symfony/console/Input/StringInput.php | 87 + vendor/symfony/console/LICENSE | 19 + .../symfony/console/Logger/ConsoleLogger.php | 119 + .../console/Messenger/RunCommandContext.php | 25 + .../console/Messenger/RunCommandMessage.php | 36 + .../Messenger/RunCommandMessageHandler.php | 48 + .../symfony/console/Output/AnsiColorMode.php | 106 + .../symfony/console/Output/BufferedOutput.php | 43 + .../symfony/console/Output/ConsoleOutput.php | 165 + .../console/Output/ConsoleOutputInterface.php | 33 + .../console/Output/ConsoleSectionOutput.php | 244 + vendor/symfony/console/Output/NullOutput.php | 104 + vendor/symfony/console/Output/Output.php | 155 + .../console/Output/OutputInterface.php | 111 + .../symfony/console/Output/StreamOutput.php | 114 + .../console/Output/TrimmedBufferOutput.php | 61 + .../console/Question/ChoiceQuestion.php | 177 + .../console/Question/ConfirmationQuestion.php | 57 + vendor/symfony/console/Question/Question.php | 291 ++ vendor/symfony/console/README.md | 27 + .../console/Resources/bin/hiddeninput.exe | Bin 0 -> 9216 bytes .../symfony/console/Resources/completion.bash | 94 + .../symfony/console/Resources/completion.fish | 29 + .../symfony/console/Resources/completion.zsh | 82 + .../console/SignalRegistry/SignalMap.php | 36 + .../console/SignalRegistry/SignalRegistry.php | 57 + .../console/SingleCommandApplication.php | 72 + vendor/symfony/console/Style/OutputStyle.php | 132 + .../symfony/console/Style/StyleInterface.php | 138 + vendor/symfony/console/Style/SymfonyStyle.php | 514 +++ vendor/symfony/console/Terminal.php | 236 + .../console/Tester/ApplicationTester.php | 85 + .../Tester/CommandCompletionTester.php | 56 + .../symfony/console/Tester/CommandTester.php | 76 + .../Tester/Constraint/CommandIsSuccessful.php | 43 + vendor/symfony/console/Tester/TesterTrait.php | 178 + vendor/symfony/console/composer.json | 55 + .../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/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 | 41 + .../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 | 38 + .../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 | 39 + 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 | 41 + .../service-contracts/Attribute/Required.php | 25 + .../Attribute/SubscribedService.php | 47 + vendor/symfony/service-contracts/CHANGELOG.md | 5 + vendor/symfony/service-contracts/LICENSE | 19 + vendor/symfony/service-contracts/README.md | 9 + .../service-contracts/ResetInterface.php | 33 + .../service-contracts/ServiceLocatorTrait.php | 115 + .../ServiceProviderInterface.php | 45 + .../ServiceSubscriberInterface.php | 62 + .../ServiceSubscriberTrait.php | 78 + .../Test/ServiceLocatorTest.php | 23 + .../Test/ServiceLocatorTestCase.php | 92 + .../symfony/service-contracts/composer.json | 41 + vendor/symfony/string/AbstractString.php | 702 +++ .../symfony/string/AbstractUnicodeString.php | 590 +++ vendor/symfony/string/ByteString.php | 485 ++ vendor/symfony/string/CHANGELOG.md | 40 + vendor/symfony/string/CodePointString.php | 260 ++ .../string/Exception/ExceptionInterface.php | 16 + .../Exception/InvalidArgumentException.php | 16 + .../string/Exception/RuntimeException.php | 16 + .../string/Inflector/EnglishInflector.php | 541 +++ .../string/Inflector/FrenchInflector.php | 151 + .../string/Inflector/InflectorInterface.php | 33 + vendor/symfony/string/LICENSE | 19 + vendor/symfony/string/LazyString.php | 145 + vendor/symfony/string/README.md | 14 + .../Resources/data/wcswidth_table_wide.php | 1155 +++++ .../Resources/data/wcswidth_table_zero.php | 1415 ++++++ vendor/symfony/string/Resources/functions.php | 38 + .../symfony/string/Slugger/AsciiSlugger.php | 207 + .../string/Slugger/SluggerInterface.php | 27 + vendor/symfony/string/UnicodeString.php | 382 ++ vendor/symfony/string/composer.json | 43 + 257 files changed, 49780 insertions(+), 13 deletions(-) create mode 100644 vendor/marcocesarato/php-conventional-changelog/.changelog create mode 100644 vendor/marcocesarato/php-conventional-changelog/.devcontainer/Dockerfile create mode 100644 vendor/marcocesarato/php-conventional-changelog/.devcontainer/devcontainer.json create mode 100644 vendor/marcocesarato/php-conventional-changelog/.gitattributes create mode 100644 vendor/marcocesarato/php-conventional-changelog/.github/workflows/php.yml create mode 100644 vendor/marcocesarato/php-conventional-changelog/.github/workflows/pr.yml create mode 100644 vendor/marcocesarato/php-conventional-changelog/.gitignore create mode 100644 vendor/marcocesarato/php-conventional-changelog/.php-cs-fixer.php create mode 100644 vendor/marcocesarato/php-conventional-changelog/CHANGELOG.md create mode 100644 vendor/marcocesarato/php-conventional-changelog/LICENSE create mode 100644 vendor/marcocesarato/php-conventional-changelog/README.md create mode 100644 vendor/marcocesarato/php-conventional-changelog/TODO.md create mode 100644 vendor/marcocesarato/php-conventional-changelog/autoload.php create mode 100644 vendor/marcocesarato/php-conventional-changelog/composer.json create mode 100644 vendor/marcocesarato/php-conventional-changelog/composer.lock create mode 100644 vendor/marcocesarato/php-conventional-changelog/conventional-changelog create mode 100644 vendor/marcocesarato/php-conventional-changelog/docs/config.md create mode 100644 vendor/marcocesarato/php-conventional-changelog/docs/images/logo.png create mode 100644 vendor/marcocesarato/php-conventional-changelog/docs/images/usage.gif create mode 100644 vendor/marcocesarato/php-conventional-changelog/phpunit.xml create mode 100644 vendor/marcocesarato/php-conventional-changelog/renovate.json create mode 100644 vendor/marcocesarato/php-conventional-changelog/src/Changelog.php create mode 100644 vendor/marcocesarato/php-conventional-changelog/src/Configuration.php create mode 100644 vendor/marcocesarato/php-conventional-changelog/src/DefaultCommand.php create mode 100644 vendor/marcocesarato/php-conventional-changelog/src/Git/Commit.php create mode 100644 vendor/marcocesarato/php-conventional-changelog/src/Git/Commit/Body.php create mode 100644 vendor/marcocesarato/php-conventional-changelog/src/Git/Commit/Description.php create mode 100644 vendor/marcocesarato/php-conventional-changelog/src/Git/Commit/Footer.php create mode 100644 vendor/marcocesarato/php-conventional-changelog/src/Git/Commit/Mention.php create mode 100644 vendor/marcocesarato/php-conventional-changelog/src/Git/Commit/Reference.php create mode 100644 vendor/marcocesarato/php-conventional-changelog/src/Git/Commit/Scope.php create mode 100644 vendor/marcocesarato/php-conventional-changelog/src/Git/Commit/Subject.php create mode 100644 vendor/marcocesarato/php-conventional-changelog/src/Git/Commit/Type.php create mode 100644 vendor/marcocesarato/php-conventional-changelog/src/Git/ConventionalCommit.php create mode 100644 vendor/marcocesarato/php-conventional-changelog/src/Git/Repository.php create mode 100644 vendor/marcocesarato/php-conventional-changelog/src/Helper/Formatter.php create mode 100644 vendor/marcocesarato/php-conventional-changelog/src/Helper/SemanticVersion.php create mode 100644 vendor/marcocesarato/php-conventional-changelog/src/Helper/ShellCommand.php create mode 100644 vendor/marcocesarato/php-conventional-changelog/src/PackageBump/ComposerJson.php create mode 100644 vendor/marcocesarato/php-conventional-changelog/src/PackageBump/PackageJson.php create mode 100644 vendor/marcocesarato/php-conventional-changelog/src/Type/PackageBump.php create mode 100644 vendor/marcocesarato/php-conventional-changelog/src/Type/Stringable.php create mode 100644 vendor/marcocesarato/php-conventional-changelog/tests/ChangelogTest.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/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/Command/TraceableCommand.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/Output/FishCompletionOutput.php create mode 100644 vendor/symfony/console/Completion/Output/ZshCompletionOutput.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/DataCollector/CommandDataCollector.php create mode 100644 vendor/symfony/console/Debug/CliRequest.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/ReStructuredTextDescriptor.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/RunCommandFailedException.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/OutputWrapper.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/Messenger/RunCommandContext.php create mode 100644 vendor/symfony/console/Messenger/RunCommandMessage.php create mode 100644 vendor/symfony/console/Messenger/RunCommandMessageHandler.php create mode 100644 vendor/symfony/console/Output/AnsiColorMode.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/Resources/completion.fish create mode 100644 vendor/symfony/console/Resources/completion.zsh create mode 100644 vendor/symfony/console/SignalRegistry/SignalMap.php 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/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/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/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/Test/ServiceLocatorTestCase.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 diff --git a/composer.json b/composer.json index 5ba6e815..dbd16f11 100644 --- a/composer.json +++ b/composer.json @@ -11,11 +11,20 @@ "require-dev": { "dealerdirect/phpcodesniffer-composer-installer": "^1.0", "wp-coding-standards/wpcs": "^3.0", - "phpunit/phpunit": "10" + "phpunit/phpunit": "10", + "marcocesarato/php-conventional-changelog": "^1.17" }, "config": { "allow-plugins": { "dealerdirect/phpcodesniffer-composer-installer": true } - } -} + }, + "scripts": { + "changelog": "conventional-changelog", + "release": "conventional-changelog --commit", + "release:patch": "conventional-changelog --patch --commit", + "release:minor": "conventional-changelog --minor --commit", + "release:major": "conventional-changelog --major --commit" + }, + "version": "1.1.2" +} \ No newline at end of file diff --git a/composer.lock b/composer.lock index f8da2b3a..907b2a9b 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "415b91125309894f3be47a7378723764", + "content-hash": "fd3daacfaa5b239d111c85b34b8721f4", "packages": [ { "name": "composer/ca-bundle", @@ -1192,6 +1192,83 @@ }, "time": "2023-01-05T11:28:13+00:00" }, + { + "name": "marcocesarato/php-conventional-changelog", + "version": "1.17.0", + "source": { + "type": "git", + "url": "https://github.com/marcocesarato/php-conventional-changelog.git", + "reference": "9269b0a3198d2107322f9f9a0fca399719825f67" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/marcocesarato/php-conventional-changelog/zipball/9269b0a3198d2107322f9f9a0fca399719825f67", + "reference": "9269b0a3198d2107322f9f9a0fca399719825f67", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": ">=7.1.3", + "symfony/console": "^4 || ^5 || ^6" + }, + "require-dev": { + "brainmaestro/composer-git-hooks": "^2.8", + "friendsofphp/php-cs-fixer": "^3.8", + "php-mock/php-mock": "^2.3", + "php-mock/php-mock-phpunit": "^2.6", + "phpunit/phpunit": "^9.6" + }, + "bin": [ + "conventional-changelog" + ], + "type": "library", + "extra": { + "hooks": { + "pre-commit": "composer fix-cs", + "pre-push": "composer check-cs", + "post-merge": "composer install" + } + }, + "autoload": { + "psr-4": { + "ConventionalChangelog\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-3.0-or-later" + ], + "authors": [ + { + "name": "Marco Cesarato", + "email": "cesarato.developer@gmail.com" + } + ], + "description": "Generate changelogs and release notes from a project's commit messages and metadata and automate versioning with semver.org and conventionalcommits.org", + "keywords": [ + "changelog", + "commit", + "commits", + "convention", + "conventional", + "conventional-changelog", + "conventional-changelog-preset", + "conventional-commit", + "conventional-commits", + "conventionalcommits", + "generation", + "git", + "history", + "php", + "readme", + "tag" + ], + "support": { + "issues": "https://github.com/marcocesarato/php-conventional-changelog/issues", + "source": "https://github.com/marcocesarato/php-conventional-changelog/tree/v1.17.0" + }, + "time": "2023-03-26T16:59:30+00:00" + }, { "name": "myclabs/deep-copy", "version": "1.11.1", @@ -1975,6 +2052,59 @@ ], "time": "2023-02-03T07:16:15+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": "" + }, + "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": "sebastian/cli-parser", "version": "2.0.0", @@ -2947,6 +3077,665 @@ }, "time": "2023-02-22T23:07:41+00:00" }, + { + "name": "symfony/console", + "version": "v6.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "cd9864b47c367450e14ab32f78fdbf98c44c26b6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/cd9864b47c367450e14ab32f78fdbf98c44c26b6", + "reference": "cd9864b47c367450e14ab32f78fdbf98c44c26b6", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^5.4|^6.0|^7.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|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^5.4|^6.0|^7.0", + "symfony/messenger": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/stopwatch": "^5.4|^6.0|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0" + }, + "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.4.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": "2023-11-20T16:41:16+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf", + "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.4-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.4.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": "2023-05-23T14:45:45+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", + "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "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.28.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": "2023-01-26T09:26:14+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "875e90aeea2777b6f135677f618529449334a612" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/875e90aeea2777b6f135677f618529449334a612", + "reference": "875e90aeea2777b6f135677f618529449334a612", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "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.28.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": "2023-01-26T09:26:14+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92", + "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "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.28.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": "2023-01-26T09:26:14+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "42292d99c55abe617799667f454222c54c60e229" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229", + "reference": "42292d99c55abe617799667f454222c54c60e229", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "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.28.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": "2023-07-28T09:04:16+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v3.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "b3313c2dbffaf71c8de2934e2ea56ed2291a3838" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/b3313c2dbffaf71c8de2934e2ea56ed2291a3838", + "reference": "b3313c2dbffaf71c8de2934e2ea56ed2291a3838", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^2.0" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "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.4.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": "2023-07-30T20:28:31+00:00" + }, + { + "name": "symfony/string", + "version": "v7.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "92bd2bfbba476d4a1838e5e12168bef2fd1e6620" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/92bd2bfbba476d4a1838e5e12168bef2fd1e6620", + "reference": "92bd2bfbba476d4a1838e5e12168bef2fd1e6620", + "shasum": "" + }, + "require": { + "php": ">=8.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.5" + }, + "require-dev": { + "symfony/error-handler": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/intl": "^6.4|^7.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^6.4|^7.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/v7.0.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": "2023-11-29T08:40:23+00:00" + }, { "name": "theseer/tokenizer", "version": "1.2.1", diff --git a/package.json b/package.json index e5655ac6..b95812a2 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,9 @@ { - "dependencies": { - "@fontsource/atkinson-hyperlegible": "^4.5.9", - "@fontsource/raleway": "^4.5.10", - "fomantic-ui": "^2.9.0", - "html2canvas": "^1.4.1" - } -} + "dependencies": { + "@fontsource/atkinson-hyperlegible": "^4.5.9", + "@fontsource/raleway": "^4.5.10", + "fomantic-ui": "^2.9.0", + "html2canvas": "^1.4.1" + }, + "version": "1.1.2" +} \ No newline at end of file diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php index 304afd52..97c8db8a 100644 --- a/vendor/composer/autoload_classmap.php +++ b/vendor/composer/autoload_classmap.php @@ -8,6 +8,7 @@ $baseDir = dirname($vendorDir); return array( 'Attribute' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Attribute.php', 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php', + 'Normalizer' => $vendorDir . '/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php', 'PHPCSUtils\\AbstractSniffs\\AbstractArrayDeclarationSniff' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/AbstractSniffs/AbstractArrayDeclarationSniff.php', 'PHPCSUtils\\BackCompat\\BCFile' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/BackCompat/BCFile.php', 'PHPCSUtils\\BackCompat\\BCTokens' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/BackCompat/BCTokens.php', diff --git a/vendor/composer/autoload_files.php b/vendor/composer/autoload_files.php index 41f01322..8ff69796 100644 --- a/vendor/composer/autoload_files.php +++ b/vendor/composer/autoload_files.php @@ -6,6 +6,12 @@ $vendorDir = dirname(__DIR__); $baseDir = dirname($vendorDir); return array( + '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php', + '320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php', + '8825ede83f2f289127722d4e842cf7e8' => $vendorDir . '/symfony/polyfill-intl-grapheme/bootstrap.php', + 'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php', + '6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php', + 'b6b991a57620e2fb6b2f66f03fe9ddc2' => $vendorDir . '/symfony/string/Resources/functions.php', '6124b4c8570aa390c21fafd04a26c69f' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php', '7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php', 'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php', diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php index 9bb38fe2..53d5fd46 100644 --- a/vendor/composer/autoload_psr4.php +++ b/vendor/composer/autoload_psr4.php @@ -7,10 +7,18 @@ $baseDir = dirname($vendorDir); return array( '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\\Service\\' => array($vendorDir . '/symfony/service-contracts'), + 'Symfony\\Component\\String\\' => array($vendorDir . '/symfony/string'), + 'Symfony\\Component\\Console\\' => array($vendorDir . '/symfony/console'), 'Slim\\Psr7\\' => array($vendorDir . '/slim/psr7/src'), 'Qferrer\\Mjml\\' => array($vendorDir . '/qferr/mjml-php/src'), 'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-factory/src', $vendorDir . '/psr/http-message/src'), 'Psr\\Http\\Client\\' => array($vendorDir . '/psr/http-client/src'), + 'Psr\\Container\\' => array($vendorDir . '/psr/container/src'), 'PhpParser\\' => array($vendorDir . '/nikic/php-parser/lib/PhpParser'), 'PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\' => array($vendorDir . '/dealerdirect/phpcodesniffer-composer-installer/src'), 'ML\\JsonLD\\' => array($vendorDir . '/ml/json-ld'), @@ -22,5 +30,6 @@ return array( 'Fig\\Http\\Message\\' => array($vendorDir . '/fig/http-message-util/src'), 'Embed\\' => array($vendorDir . '/embed/embed/src'), 'DeepCopy\\' => array($vendorDir . '/myclabs/deep-copy/src/DeepCopy'), + 'ConventionalChangelog\\' => array($vendorDir . '/marcocesarato/php-conventional-changelog/src'), 'Composer\\CaBundle\\' => array($vendorDir . '/composer/ca-bundle/src'), ); diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index 67d06149..4cfe16a3 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -7,6 +7,12 @@ namespace Composer\Autoload; class ComposerStaticInit5f3db9fc1d0cf1dd6a77a1d84501b4b1 { public static $files = array ( + '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php', + '320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php', + '8825ede83f2f289127722d4e842cf7e8' => __DIR__ . '/..' . '/symfony/polyfill-intl-grapheme/bootstrap.php', + 'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php', + '6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php', + 'b6b991a57620e2fb6b2f66f03fe9ddc2' => __DIR__ . '/..' . '/symfony/string/Resources/functions.php', '6124b4c8570aa390c21fafd04a26c69f' => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php', '7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php', 'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php', @@ -18,6 +24,13 @@ class ComposerStaticInit5f3db9fc1d0cf1dd6a77a1d84501b4b1 'S' => array ( 'Symfony\\Polyfill\\Php80\\' => 23, + 'Symfony\\Polyfill\\Mbstring\\' => 26, + 'Symfony\\Polyfill\\Intl\\Normalizer\\' => 33, + 'Symfony\\Polyfill\\Intl\\Grapheme\\' => 31, + 'Symfony\\Polyfill\\Ctype\\' => 23, + 'Symfony\\Contracts\\Service\\' => 26, + 'Symfony\\Component\\String\\' => 25, + 'Symfony\\Component\\Console\\' => 26, 'Slim\\Psr7\\' => 10, ), 'Q' => @@ -28,6 +41,7 @@ class ComposerStaticInit5f3db9fc1d0cf1dd6a77a1d84501b4b1 array ( 'Psr\\Http\\Message\\' => 17, 'Psr\\Http\\Client\\' => 16, + 'Psr\\Container\\' => 14, 'PhpParser\\' => 10, 'PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\' => 57, ), @@ -63,6 +77,7 @@ class ComposerStaticInit5f3db9fc1d0cf1dd6a77a1d84501b4b1 ), 'C' => array ( + 'ConventionalChangelog\\' => 22, 'Composer\\CaBundle\\' => 18, ), ); @@ -72,6 +87,34 @@ class ComposerStaticInit5f3db9fc1d0cf1dd6a77a1d84501b4b1 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\\Service\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/service-contracts', + ), + 'Symfony\\Component\\String\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/string', + ), + 'Symfony\\Component\\Console\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/console', + ), 'Slim\\Psr7\\' => array ( 0 => __DIR__ . '/..' . '/slim/psr7/src', @@ -89,6 +132,10 @@ class ComposerStaticInit5f3db9fc1d0cf1dd6a77a1d84501b4b1 array ( 0 => __DIR__ . '/..' . '/psr/http-client/src', ), + 'Psr\\Container\\' => + array ( + 0 => __DIR__ . '/..' . '/psr/container/src', + ), 'PhpParser\\' => array ( 0 => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser', @@ -134,6 +181,10 @@ class ComposerStaticInit5f3db9fc1d0cf1dd6a77a1d84501b4b1 array ( 0 => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy', ), + 'ConventionalChangelog\\' => + array ( + 0 => __DIR__ . '/..' . '/marcocesarato/php-conventional-changelog/src', + ), 'Composer\\CaBundle\\' => array ( 0 => __DIR__ . '/..' . '/composer/ca-bundle/src', @@ -153,6 +204,7 @@ class ComposerStaticInit5f3db9fc1d0cf1dd6a77a1d84501b4b1 public static $classMap = array ( 'Attribute' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Attribute.php', 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', + 'Normalizer' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php', 'PHPCSUtils\\AbstractSniffs\\AbstractArrayDeclarationSniff' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/AbstractSniffs/AbstractArrayDeclarationSniff.php', 'PHPCSUtils\\BackCompat\\BCFile' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/BackCompat/BCFile.php', 'PHPCSUtils\\BackCompat\\BCTokens' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/BackCompat/BCTokens.php', diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index 429c1e88..bce6c476 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -640,6 +640,86 @@ }, "install-path": "../jaybizzle/crawler-detect" }, + { + "name": "marcocesarato/php-conventional-changelog", + "version": "1.17.0", + "version_normalized": "1.17.0.0", + "source": { + "type": "git", + "url": "https://github.com/marcocesarato/php-conventional-changelog.git", + "reference": "9269b0a3198d2107322f9f9a0fca399719825f67" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/marcocesarato/php-conventional-changelog/zipball/9269b0a3198d2107322f9f9a0fca399719825f67", + "reference": "9269b0a3198d2107322f9f9a0fca399719825f67", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": ">=7.1.3", + "symfony/console": "^4 || ^5 || ^6" + }, + "require-dev": { + "brainmaestro/composer-git-hooks": "^2.8", + "friendsofphp/php-cs-fixer": "^3.8", + "php-mock/php-mock": "^2.3", + "php-mock/php-mock-phpunit": "^2.6", + "phpunit/phpunit": "^9.6" + }, + "time": "2023-03-26T16:59:30+00:00", + "bin": [ + "conventional-changelog" + ], + "type": "library", + "extra": { + "hooks": { + "pre-commit": "composer fix-cs", + "pre-push": "composer check-cs", + "post-merge": "composer install" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "ConventionalChangelog\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-3.0-or-later" + ], + "authors": [ + { + "name": "Marco Cesarato", + "email": "cesarato.developer@gmail.com" + } + ], + "description": "Generate changelogs and release notes from a project's commit messages and metadata and automate versioning with semver.org and conventionalcommits.org", + "keywords": [ + "changelog", + "commit", + "commits", + "convention", + "conventional", + "conventional-changelog", + "conventional-changelog-preset", + "conventional-commit", + "conventional-commits", + "conventionalcommits", + "generation", + "git", + "history", + "php", + "readme", + "tag" + ], + "support": { + "issues": "https://github.com/marcocesarato/php-conventional-changelog/issues", + "source": "https://github.com/marcocesarato/php-conventional-changelog/tree/v1.17.0" + }, + "install-path": "../marcocesarato/php-conventional-changelog" + }, { "name": "ml/iri", "version": "1.1.4", @@ -1625,6 +1705,62 @@ ], "install-path": "../phpunit/phpunit" }, + { + "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": "" + }, + "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.2", @@ -2994,6 +3130,515 @@ }, "install-path": "../squizlabs/php_codesniffer" }, + { + "name": "symfony/console", + "version": "v6.4.0", + "version_normalized": "6.4.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "cd9864b47c367450e14ab32f78fdbf98c44c26b6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/cd9864b47c367450e14ab32f78fdbf98c44c26b6", + "reference": "cd9864b47c367450e14ab32f78fdbf98c44c26b6", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^5.4|^6.0|^7.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|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^5.4|^6.0|^7.0", + "symfony/messenger": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/stopwatch": "^5.4|^6.0|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0" + }, + "time": "2023-11-20T16:41:16+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.4.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/console" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.4.0", + "version_normalized": "3.4.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf", + "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "time": "2023-05-23T14:45:45+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.4-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.4.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/deprecation-contracts" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.28.0", + "version_normalized": "1.28.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", + "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "time": "2023-01-26T09:26:14+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "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.28.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.28.0", + "version_normalized": "1.28.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "875e90aeea2777b6f135677f618529449334a612" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/875e90aeea2777b6f135677f618529449334a612", + "reference": "875e90aeea2777b6f135677f618529449334a612", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "time": "2023-01-26T09:26:14+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "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.28.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.28.0", + "version_normalized": "1.28.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92", + "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "time": "2023-01-26T09:26:14+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "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.28.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.28.0", + "version_normalized": "1.28.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "42292d99c55abe617799667f454222c54c60e229" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229", + "reference": "42292d99c55abe617799667f454222c54c60e229", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "time": "2023-07-28T09:04:16+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "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.28.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.28.0", @@ -3080,6 +3725,180 @@ ], "install-path": "../symfony/polyfill-php80" }, + { + "name": "symfony/service-contracts", + "version": "v3.4.0", + "version_normalized": "3.4.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "b3313c2dbffaf71c8de2934e2ea56ed2291a3838" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/b3313c2dbffaf71c8de2934e2ea56ed2291a3838", + "reference": "b3313c2dbffaf71c8de2934e2ea56ed2291a3838", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^2.0" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "time": "2023-07-30T20:28:31+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "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.4.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/service-contracts" + }, + { + "name": "symfony/string", + "version": "v7.0.0", + "version_normalized": "7.0.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "92bd2bfbba476d4a1838e5e12168bef2fd1e6620" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/92bd2bfbba476d4a1838e5e12168bef2fd1e6620", + "reference": "92bd2bfbba476d4a1838e5e12168bef2fd1e6620", + "shasum": "" + }, + "require": { + "php": ">=8.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.5" + }, + "require-dev": { + "symfony/error-handler": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/intl": "^6.4|^7.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^6.4|^7.0" + }, + "time": "2023-11-29T08:40:23+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/v7.0.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/string" + }, { "name": "theseer/tokenizer", "version": "1.2.1", @@ -3206,6 +4025,7 @@ "dev": true, "dev-package-names": [ "dealerdirect/phpcodesniffer-composer-installer", + "marcocesarato/php-conventional-changelog", "myclabs/deep-copy", "nikic/php-parser", "phar-io/manifest", @@ -3218,6 +4038,7 @@ "phpunit/php-text-template", "phpunit/php-timer", "phpunit/phpunit", + "psr/container", "sebastian/cli-parser", "sebastian/code-unit", "sebastian/code-unit-reverse-lookup", @@ -3234,6 +4055,14 @@ "sebastian/type", "sebastian/version", "squizlabs/php_codesniffer", + "symfony/console", + "symfony/deprecation-contracts", + "symfony/polyfill-ctype", + "symfony/polyfill-intl-grapheme", + "symfony/polyfill-intl-normalizer", + "symfony/polyfill-mbstring", + "symfony/service-contracts", + "symfony/string", "theseer/tokenizer", "wp-coding-standards/wpcs" ] diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php index 97dca2d7..4611cc81 100644 --- a/vendor/composer/installed.php +++ b/vendor/composer/installed.php @@ -3,7 +3,7 @@ 'name' => '__root__', 'pretty_version' => 'dev-develop', 'version' => 'dev-develop', - 'reference' => 'e01b7e170146c4ec435ab28dada918ff171996e4', + 'reference' => '1ae6f74b44e2f24e28b1e895bbcc73c0773fdaad', 'type' => 'library', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), @@ -13,7 +13,7 @@ '__root__' => array( 'pretty_version' => 'dev-develop', 'version' => 'dev-develop', - 'reference' => 'e01b7e170146c4ec435ab28dada918ff171996e4', + 'reference' => '1ae6f74b44e2f24e28b1e895bbcc73c0773fdaad', 'type' => 'library', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), @@ -100,6 +100,15 @@ 'aliases' => array(), 'dev_requirement' => false, ), + 'marcocesarato/php-conventional-changelog' => array( + 'pretty_version' => '1.17.0', + 'version' => '1.17.0.0', + 'reference' => '9269b0a3198d2107322f9f9a0fca399719825f67', + 'type' => 'library', + 'install_path' => __DIR__ . '/../marcocesarato/php-conventional-changelog', + 'aliases' => array(), + 'dev_requirement' => true, + ), 'ml/iri' => array( 'pretty_version' => '1.1.4', 'version' => '1.1.4.0', @@ -235,6 +244,15 @@ 'aliases' => array(), 'dev_requirement' => true, ), + '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' => true, + ), 'psr/http-client' => array( 'pretty_version' => '1.0.2', 'version' => '1.0.2.0', @@ -274,6 +292,12 @@ 0 => '1.0', ), ), + 'psr/log-implementation' => array( + 'dev_requirement' => true, + 'provided' => array( + 0 => '1.0|2.0|3.0', + ), + ), 'qferr/mjml-php' => array( 'pretty_version' => '2.0.0', 'version' => '2.0.0.0', @@ -445,6 +469,60 @@ 'aliases' => array(), 'dev_requirement' => true, ), + 'symfony/console' => array( + 'pretty_version' => 'v6.4.0', + 'version' => '6.4.0.0', + 'reference' => 'cd9864b47c367450e14ab32f78fdbf98c44c26b6', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/console', + 'aliases' => array(), + 'dev_requirement' => true, + ), + 'symfony/deprecation-contracts' => array( + 'pretty_version' => 'v3.4.0', + 'version' => '3.4.0.0', + 'reference' => '7c3aff79d10325257a001fcf92d991f24fc967cf', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/deprecation-contracts', + 'aliases' => array(), + 'dev_requirement' => true, + ), + 'symfony/polyfill-ctype' => array( + 'pretty_version' => 'v1.28.0', + 'version' => '1.28.0.0', + 'reference' => 'ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/polyfill-ctype', + 'aliases' => array(), + 'dev_requirement' => true, + ), + 'symfony/polyfill-intl-grapheme' => array( + 'pretty_version' => 'v1.28.0', + 'version' => '1.28.0.0', + 'reference' => '875e90aeea2777b6f135677f618529449334a612', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/polyfill-intl-grapheme', + 'aliases' => array(), + 'dev_requirement' => true, + ), + 'symfony/polyfill-intl-normalizer' => array( + 'pretty_version' => 'v1.28.0', + 'version' => '1.28.0.0', + 'reference' => '8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/polyfill-intl-normalizer', + 'aliases' => array(), + 'dev_requirement' => true, + ), + 'symfony/polyfill-mbstring' => array( + 'pretty_version' => 'v1.28.0', + 'version' => '1.28.0.0', + 'reference' => '42292d99c55abe617799667f454222c54c60e229', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/polyfill-mbstring', + 'aliases' => array(), + 'dev_requirement' => true, + ), 'symfony/polyfill-php80' => array( 'pretty_version' => 'v1.28.0', 'version' => '1.28.0.0', @@ -454,6 +532,24 @@ 'aliases' => array(), 'dev_requirement' => false, ), + 'symfony/service-contracts' => array( + 'pretty_version' => 'v3.4.0', + 'version' => '3.4.0.0', + 'reference' => 'b3313c2dbffaf71c8de2934e2ea56ed2291a3838', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/service-contracts', + 'aliases' => array(), + 'dev_requirement' => true, + ), + 'symfony/string' => array( + 'pretty_version' => 'v7.0.0', + 'version' => '7.0.0.0', + 'reference' => '92bd2bfbba476d4a1838e5e12168bef2fd1e6620', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/string', + 'aliases' => array(), + 'dev_requirement' => true, + ), 'theseer/tokenizer' => array( 'pretty_version' => '1.2.1', 'version' => '1.2.1.0', diff --git a/vendor/marcocesarato/php-conventional-changelog/.changelog b/vendor/marcocesarato/php-conventional-changelog/.changelog new file mode 100644 index 00000000..6c20a80b --- /dev/null +++ b/vendor/marcocesarato/php-conventional-changelog/.changelog @@ -0,0 +1,52 @@ + ['feat', 'fix', 'perf', 'docs', 'chore'], + // Ignore changelogs + 'ignorePatterns' => [ + '/' . preg_quote($releaseMessagePrefix, '/') . '.*/i', + '/chore\(changelog\)[:].*/i', + ], + 'releaseCommitMessageFormat' => "{$releaseMessagePrefix}{{currentTag}}", + 'postRun' => function () use ($releaseMessagePrefix) { + $lastTag = Repository::getLastTagRefname(); + $lastTagCommit = Repository::getLastTagRefnameCommit(); + $lastCommit = Repository::getLastCommit(); + + if ($lastTagCommit !== $lastCommit) { + return; + } + + $binFile = __DIR__ . '/conventional-changelog'; + $readmeFile = __DIR__ . '/README.md'; + $semverRegex = SemanticVersion::PATTERN; + + // Get version + $version = new SemanticVersion($lastTag); + + // Update version on readme + $readme = file_get_contents($readmeFile); + $readme = preg_replace("/(https:\/\/img\.shields\.io\/badge\/version)-({$semverRegex})-/m", '$1-' . $version->getVersion() . '-', $readme); + file_put_contents($readmeFile, $readme); + + // Update version on bin + $bin = file_get_contents($binFile); + $bin = preg_replace("/(\('conventional-changelog', )'({$semverRegex})'/m", '$1\'' . $version->getVersion() . '\'', $bin); + file_put_contents($binFile, $bin); + + // Delete tag + Repository::deleteTag($lastTag); + + // Commit and tag + $message = $releaseMessagePrefix . $version->getVersion(); + Repository::commit($message, [$binFile, $readmeFile], true, true, true); + Repository::tag($lastTag); + }, +]; diff --git a/vendor/marcocesarato/php-conventional-changelog/.devcontainer/Dockerfile b/vendor/marcocesarato/php-conventional-changelog/.devcontainer/Dockerfile new file mode 100644 index 00000000..acbcd64b --- /dev/null +++ b/vendor/marcocesarato/php-conventional-changelog/.devcontainer/Dockerfile @@ -0,0 +1,15 @@ +# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.195.0/containers/php/.devcontainer/base.Dockerfile +# [Choice] PHP version (use -bullseye variants on local arm64/Apple Silicon): 8, 8.0, 7, 7.4, 7.3, 8-bullseye, 8.0-bullseye, 7-bullseye, 7.4-bullseye, 7.3-bullseye, 8-buster, 8.0-buster, 7-buster, 7.4-buster, 7.3-buster +ARG VARIANT=8-bullseye +FROM mcr.microsoft.com/vscode/devcontainers/php:0-${VARIANT} + +# [Choice] Node.js version: none, lts/*, 16, 14, 12, 10 +ARG NODE_VERSION="none" +RUN if [ "${NODE_VERSION}" != "none" ]; then su vscode -c "umask 0002 && . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi + +# [Optional] Uncomment this section to install additional OS packages. +# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ +# && apt-get -y install --no-install-recommends + +# [Optional] Uncomment this line to install global node packages. +# RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g " 2>&1 \ No newline at end of file diff --git a/vendor/marcocesarato/php-conventional-changelog/.devcontainer/devcontainer.json b/vendor/marcocesarato/php-conventional-changelog/.devcontainer/devcontainer.json new file mode 100644 index 00000000..78083cde --- /dev/null +++ b/vendor/marcocesarato/php-conventional-changelog/.devcontainer/devcontainer.json @@ -0,0 +1,55 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: +// https://github.com/microsoft/vscode-dev-containers/tree/v0.195.0/containers/php +{ + "name": "PHP", + "build": { + "dockerfile": "Dockerfile", + "args": { + // Update VARIANT to pick a PHP version: 8, 8.0, 7, 7.4, 7.3 + // Append -bullseye or -buster to pin to an OS version. + // Use -bullseye variants on local on arm64/Apple Silicon. + "VARIANT": "8-bullseye", + "NODE_VERSION": "lts/*" + } + }, + + // Configure tool-specific properties. + "customizations": { + // Configure properties specific to VS Code. + "vscode": { + // Set *default* container specific settings.json values on container create. + "settings": { + "php.validate.executablePath": "/usr/local/bin/php" + }, + + // Add the IDs of extensions you want installed when the container is created. + "extensions": [ + "felixfbecker.php-debug", + "bmewburn.vscode-intelephense-client", + "mrmlnc.vscode-apache" + ] + } + }, + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [8080], + + // Use 'portsAttributes' to set default properties for specific forwarded ports. More info: https://code.visualstudio.com/docs/remote/devcontainerjson-reference. + "portsAttributes": { + "8000": { + "label": "Hello Remote World", + "onAutoForward": "notify" + } + }, + + // Use 'otherPortsAttributes' to configure any ports that aren't configured using 'portsAttributes'. + // "otherPortsAttributes": { + // "onAutoForward": "silent" + // }, + + // Use 'postCreateCommand' to run commands after the container is created. + // "postCreateCommand": "sudo chmod a+x \"$(pwd)\" && sudo rm -rf /var/www/html && sudo ln -s \"$(pwd)\" /var/www/html" + + // Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. + "remoteUser": "vscode" +} \ No newline at end of file diff --git a/vendor/marcocesarato/php-conventional-changelog/.gitattributes b/vendor/marcocesarato/php-conventional-changelog/.gitattributes new file mode 100644 index 00000000..85564bd9 --- /dev/null +++ b/vendor/marcocesarato/php-conventional-changelog/.gitattributes @@ -0,0 +1 @@ +eol=lf \ No newline at end of file diff --git a/vendor/marcocesarato/php-conventional-changelog/.github/workflows/php.yml b/vendor/marcocesarato/php-conventional-changelog/.github/workflows/php.yml new file mode 100644 index 00000000..e0f7986b --- /dev/null +++ b/vendor/marcocesarato/php-conventional-changelog/.github/workflows/php.yml @@ -0,0 +1,42 @@ +name: Code Checker + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Update composer + run: composer update + + - name: Validate composer.json and composer.lock + run: composer validate + + - name: Cache Composer packages + id: composer-cache + uses: actions/cache@v3 + with: + path: vendor + key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-php- + + - name: Install dependencies + if: steps.composer-cache.outputs.cache-hit != 'true' + run: composer install --prefer-dist --no-progress --no-suggest + + - name: PHP Lint + uses: davidlienhard/php-simple-lint@1 + with: + folder: './' + ignore: './vendor/\*' + + - uses: symfonycorp/security-checker-action@v5 \ No newline at end of file diff --git a/vendor/marcocesarato/php-conventional-changelog/.github/workflows/pr.yml b/vendor/marcocesarato/php-conventional-changelog/.github/workflows/pr.yml new file mode 100644 index 00000000..bee1ad22 --- /dev/null +++ b/vendor/marcocesarato/php-conventional-changelog/.github/workflows/pr.yml @@ -0,0 +1,12 @@ +name: Pull Request +on: pull_request +jobs: + conventional: + name: Conventional PR + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v2-beta + - uses: beemojs/conventional-pr-action@v2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/vendor/marcocesarato/php-conventional-changelog/.gitignore b/vendor/marcocesarato/php-conventional-changelog/.gitignore new file mode 100644 index 00000000..e0ef515e --- /dev/null +++ b/vendor/marcocesarato/php-conventional-changelog/.gitignore @@ -0,0 +1,5 @@ +vendor/** +.idea +.php-cs-fixer.cache +cghooks.lock +/.phpunit.result.cache diff --git a/vendor/marcocesarato/php-conventional-changelog/.php-cs-fixer.php b/vendor/marcocesarato/php-conventional-changelog/.php-cs-fixer.php new file mode 100644 index 00000000..c616cae5 --- /dev/null +++ b/vendor/marcocesarato/php-conventional-changelog/.php-cs-fixer.php @@ -0,0 +1,47 @@ +ignoreDotFiles(false) + ->ignoreVCS(true) + ->exclude('vendor') + ->name('.changelog') + ->name('.php_cs') + ->name('conventional-changelog') + ->in(__DIR__); + +$config = new PhpCsFixer\Config(); + +return $config + ->setUsingCache(true) + ->setRiskyAllowed(true) + ->setCacheFile(__DIR__ . '/.php-cs-fixer.cache') + ->setRules([ + '@PSR1' => true, + '@PSR2' => true, + '@Symfony' => true, + 'psr_autoloading' => true, + // Custom rules + 'align_multiline_comment' => ['comment_type' => 'phpdocs_only'], // PSR-5 + 'phpdoc_to_comment' => false, + 'array_indentation' => true, + 'array_syntax' => ['syntax' => 'short'], + 'cast_spaces' => ['space' => 'none'], + 'concat_space' => ['spacing' => 'one'], + 'compact_nullable_typehint' => true, + 'declare_equal_normalize' => ['space' => 'single'], + 'increment_style' => ['style' => 'post'], + 'list_syntax' => ['syntax' => 'long'], + 'echo_tag_syntax' => ['format' => 'long'], + 'phpdoc_align' => false, + 'phpdoc_no_empty_return' => false, + 'phpdoc_order' => true, // PSR-5 + 'phpdoc_no_useless_inheritdoc' => false, + 'protected_to_private' => false, + 'yoda_style' => false, + 'method_argument_space' => ['on_multiline' => 'ensure_fully_multiline'], + 'ordered_imports' => [ + 'sort_algorithm' => 'alpha', + 'imports_order' => ['class', 'const', 'function'], + ], + ]) + ->setFinder($finder); diff --git a/vendor/marcocesarato/php-conventional-changelog/CHANGELOG.md b/vendor/marcocesarato/php-conventional-changelog/CHANGELOG.md new file mode 100644 index 00000000..38e0fd09 --- /dev/null +++ b/vendor/marcocesarato/php-conventional-changelog/CHANGELOG.md @@ -0,0 +1,840 @@ + +# Changelog + +All notable changes to this project will be documented in this file. + + +## [1.17.0](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.16.0...v1.17.0) (2023-03-26) + +### Bug Fixes + +* Cast `Scope` as string to use it as array key ([aff96f](https://github.com/marcocesarato/php-conventional-changelog/commit/aff96f70c05dec4191190eaf32951930f2b2570c)) +* Check string offset exists before using it ([1fe715](https://github.com/marcocesarato/php-conventional-changelog/commit/1fe7152382ea5719a6a39067e4d8ce6e154f31f2)) + + +--- + +## [1.16.0](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.15.6...v1.16.0) (2022-11-26) + +### Features + +* New option: Do not update CHANGELOG if no commits found. ([00a81e](https://github.com/marcocesarato/php-conventional-changelog/commit/00a81ee59f3f3e3f3b386f454d480a77538892ab)) + +### Bug Fixes + +* Can parse header with carriage return and newline. ([ac4710](https://github.com/marcocesarato/php-conventional-changelog/commit/ac47108d2d1aed3e1e9fd8e7c5ea644a1216f8df)) +* Retrieve last tag with extra [#48](https://github.com/marcocesarato/php-conventional-changelog/issues/48) ([454513](https://github.com/marcocesarato/php-conventional-changelog/commit/45451305801803aedc40cf01397440eea4524c0d)) + + +--- + +## [1.15.6](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.15.5...v1.15.6) (2022-11-17) + +### Bug Fixes + +* Resolves problems with bumping alpha, beta and rc releases ([ad9eff](https://github.com/marcocesarato/php-conventional-changelog/commit/ad9efff67b1b83027865a2c1fa583d80370fb433)) +* Resolves with extra releases ([e31c16](https://github.com/marcocesarato/php-conventional-changelog/commit/e31c16bfa180952fa8f10e271e49f4ffdfec8290)) + + +--- + +## [1.15.5](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.15.4...v1.15.5) (2022-11-04) + +### Bug Fixes + +* Bump first time with extra version ([22b302](https://github.com/marcocesarato/php-conventional-changelog/commit/22b302aae1490eb78c018db9ed5148669d63c9d9)) +* Extra tag bumping ([b6223f](https://github.com/marcocesarato/php-conventional-changelog/commit/b6223f47171af784e5c2a578db13d8afe49f1904)) +* Semantic version regex compare ([a71a4b](https://github.com/marcocesarato/php-conventional-changelog/commit/a71a4bbaf46a72ffa27b5153a9da60554845ec31)) + + +--- + +## [1.15.4](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.15.3...v1.15.4) (2022-10-27) + +### Bug Fixes + +* Restore last tag with refname method ([ef97fb](https://github.com/marcocesarato/php-conventional-changelog/commit/ef97fb098fb25b6f9c6c285fe2ffb32d8fc06211)) + + +--- + +## [1.15.3](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.15.2...v1.15.3) (2022-10-27) + +### Bug Fixes + +* Prefix stripping and merged tags only [#47](https://github.com/marcocesarato/php-conventional-changelog/issues/47) ([60274c](https://github.com/marcocesarato/php-conventional-changelog/commit/60274c9bc220d0285ad535b14dfc630548a701cf)) +* Semantic version prefix remove ([a821c1](https://github.com/marcocesarato/php-conventional-changelog/commit/a821c1366e9a2323d625af958b4196656ef01788)) + + +--- + +## [1.15.2](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.15.1...v1.15.2) (2022-10-24) + +### Bug Fixes + +* Enable merged feature with new extra logics ([6a7d70](https://github.com/marcocesarato/php-conventional-changelog/commit/6a7d7090cc754ff73040db408c75bd02e2da2192)) +* Extra release generation ([854599](https://github.com/marcocesarato/php-conventional-changelog/commit/854599bccf45b461e66bfd37a306f9c9a4db09c8)) +* Force autobump on an extra release ([ce5d80](https://github.com/marcocesarato/php-conventional-changelog/commit/ce5d806de1afa01f87dfe73d35b45dead7c530a2)) + + +--- + +## [1.15.1](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.15.0...v1.15.1) (2022-06-03) + +### Bug Fixes + +* Add blank line after changeLogVersionHeading [#39](https://github.com/marcocesarato/php-conventional-changelog/issues/39) ([9e4108](https://github.com/marcocesarato/php-conventional-changelog/commit/9e41083adb9d898c97c3e5d3d8561e9b84cae410)) + + +--- + +## [1.15.0](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.14.1...v1.15.0) (2022-06-02) +### Features + +* Add a getCurrentBranch method to Repository ([8b6266](https://github.com/marcocesarato/php-conventional-changelog/commit/8b62668eea440b64683f0af20a13065a7890be3f)) + + +--- + +## [1.14.1](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.14.0...v1.14.1) (2022-05-24) +### Bug Fixes + +* Breaking changes check [#37](https://github.com/marcocesarato/php-conventional-changelog/issues/37) ([476ff7](https://github.com/marcocesarato/php-conventional-changelog/commit/476ff7f8b2e6d3efffbfa4af152f4d0269491934)) + + +--- + +## [1.14.0](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.13.0...v1.14.0) (2022-05-06) +### ⚠ BREAKING CHANGES + +* Sort git tags by version instead of creation date, match prefix when listing tags ([042af9](https://github.com/marcocesarato/php-conventional-changelog/commit/042af9ec98d85519a9e91130e2fcbdd15b981565)) + +### Features + +* Annotated tags ([e738b2](https://github.com/marcocesarato/php-conventional-changelog/commit/e738b29b22cbdaa1cd1490408cff3b1b4299a2f2)) + +### Bug Fixes + +* Get last version with prefix ([2460c8](https://github.com/marcocesarato/php-conventional-changelog/commit/2460c81fab175e23260e449e4a4554a2e8d479c6)) +* Issues with tag version fetch ([d64f35](https://github.com/marcocesarato/php-conventional-changelog/commit/d64f359450bc0b678c90eeb7aa926e2b39bff4b2)) + + +--- + +## [1.13.0](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.12.3...v1.13.0) (2021-12-08) +### Features + +* Added support for limiting commits to current branch. ([6aad51](https://github.com/marcocesarato/php-conventional-changelog/commit/6aad514354334b9a3041bfdbe0c81b98e8f118b1)) + +### Bug Fixes + + +##### Semver + +* Bump minor version when breaking change before 1.0.0 ([5b13aa](https://github.com/marcocesarato/php-conventional-changelog/commit/5b13aa1408459be415f475a23e55983aaa297111)) + + +--- + +## [1.12.3](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.12.2...v1.12.3) (2021-11-25) +### Features + +* Allows own package bumpers ([e4aef9](https://github.com/marcocesarato/php-conventional-changelog/commit/e4aef903f14c981b8978ab1299eedc2f7e0888cb)) + + +--- + +## [1.12.2](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.12.1...v1.12.2) (2021-10-22) +### Bug Fixes + +* Repository url format detector [#22](https://github.com/marcocesarato/php-conventional-changelog/issues/22) ([63ff44](https://github.com/marcocesarato/php-conventional-changelog/commit/63ff443cbc53d2453ee217950d4664a15e9b3dd4)) + + +--- + +## [1.12.1](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.12.0...v1.12.1) (2021-10-22) +### Features + +* Allows issue id to be string ([30c20e](https://github.com/marcocesarato/php-conventional-changelog/commit/30c20e52369c723b53b054fa2199c8c52256d9a9)) + +### Bug Fixes + +* Change repo parse remote url regex ([2dd6c8](https://github.com/marcocesarato/php-conventional-changelog/commit/2dd6c87faf67b809a8b37a763a2e33d5357141a4)) +* Version separator markdown ([08092d](https://github.com/marcocesarato/php-conventional-changelog/commit/08092d21e8fa223de2bf4954b5d8f738109c36c1)) + + +--- + +## [1.12.0](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.11.0...v1.12.0) (2021-08-24) +### Features + +* Allows ability to hide version separator. ([e74c4d](https://github.com/marcocesarato/php-conventional-changelog/commit/e74c4d47130baa742397c7701fb27f5f6681d95d)) + +--- + +## [1.11.0](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.10.7...v1.11.0) (2021-07-28) +### Features + +* Allows generated changelog to have configurable version headers ([f57e95](https://github.com/marcocesarato/php-conventional-changelog/commit/f57e953b7a8c3b96519238e0122bbb3cb949b9ba)) + +--- + +## [1.10.7](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.10.6...v1.10.7) (2021-05-15) + + +### Features + +* Add option to render text instead of links in generated changelog ([5cb2e5](https://github.com/marcocesarato/php-conventional-changelog/commit/5cb2e5d2d8b9b445e38eede49dbc3a8036ba36a6)) + +--- + +## [1.10.6](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.10.5...v1.10.6) (2021-05-15) + + +### Bug Fixes + +* Check if valid remote url [#14](https://github.com/marcocesarato/php-conventional-changelog/issues/14) ([7bc76c](https://github.com/marcocesarato/php-conventional-changelog/commit/7bc76cdaa11109efd43699d62ff3e71d1b445a15)) +* Explicit compare ([78a084](https://github.com/marcocesarato/php-conventional-changelog/commit/78a084dca3f22e82614d2287ec350fe9cc05f539)) + +--- + +## [1.10.5](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.10.4...v1.10.5) (2021-05-15) + + +### Bug Fixes + +* Output error on check requirements [#14](https://github.com/marcocesarato/php-conventional-changelog/issues/14) ([f020d9](https://github.com/marcocesarato/php-conventional-changelog/commit/f020d9e5a79fb355cd92785882eed8f325152d6a)) +* Remove remote url repository requirement [#14](https://github.com/marcocesarato/php-conventional-changelog/issues/14) ([dacd7c](https://github.com/marcocesarato/php-conventional-changelog/commit/dacd7c3a82c2924047fcf4128e2d604f58c94978)) + +--- + +## [1.10.4](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.10.3...v1.10.4) (2021-05-14) + + +### Bug Fixes + +* Add git remote repository url format check ([56aff6](https://github.com/marcocesarato/php-conventional-changelog/commit/56aff6304296c239aad8646e96d5452927857e84)) +* Add requirements checks at startup [#14](https://github.com/marcocesarato/php-conventional-changelog/issues/14) ([b0b124](https://github.com/marcocesarato/php-conventional-changelog/commit/b0b1245cfca1bae60950d313fdb6d131c7cd5852)) +* History version code ([eebfbe](https://github.com/marcocesarato/php-conventional-changelog/commit/eebfbea5e02a2e93d36eee3ba8ef357ecb269c79)) +* Version code format and initialization [#14](https://github.com/marcocesarato/php-conventional-changelog/issues/14) ([b65bb6](https://github.com/marcocesarato/php-conventional-changelog/commit/b65bb669eaa33964b25b8fff97f0b1901d42b152)) + +##### Git + +* Detect is inside work tree check remote url ([cb57a6](https://github.com/marcocesarato/php-conventional-changelog/commit/cb57a6e9c2d62e3c4009af6dd060acd6854b2bc2)) + +##### Semver + +* Add get version code method ([54599f](https://github.com/marcocesarato/php-conventional-changelog/commit/54599fb8e91d6bc06bfc7881978f3a0fcde2602f)) + +##### Shell + +* Add is enabled method ([edee7d](https://github.com/marcocesarato/php-conventional-changelog/commit/edee7de39313697b3c327a8729cc06c8d0774fb1)) + +--- + +## [1.10.3](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.10.2...v1.10.3) (2021-05-11) + + +### Bug Fixes + + +##### Composer Json + +* Disable composer update on bump [#13](https://github.com/marcocesarato/php-conventional-changelog/issues/13) ([669262](https://github.com/marcocesarato/php-conventional-changelog/commit/669262ba6ceba78f82d6bb557c47564a90710716)) + +--- + +## [1.10.2](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.10.1...v1.10.2) (2021-04-16) + + +### Bug Fixes + +* Skip tags and --not-tag are not working ([7f4d45](https://github.com/marcocesarato/php-conventional-changelog/commit/7f4d45886ad2723da11e4cb0073a7d29ead14e2d)) + +--- + +## [1.10.1](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.10.0...v1.10.1) (2021-04-15) + + +### Bug Fixes + + +##### Composer Json + +* Composer update change path command ([18bb19](https://github.com/marcocesarato/php-conventional-changelog/commit/18bb19395d9c33905786ff1b1d834db369142c20)) + +--- + +## [1.10.0](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.9.10...v1.10.0) (2021-04-15) + + +### Features + +* Add packages bumper [#11](https://github.com/marcocesarato/php-conventional-changelog/issues/11) ([0849aa](https://github.com/marcocesarato/php-conventional-changelog/commit/0849aad6d04e6640777a819391736edde56f00ba)) + +##### Composer Json + +* Add composer update on save [#11](https://github.com/marcocesarato/php-conventional-changelog/issues/11) ([ea8fb7](https://github.com/marcocesarato/php-conventional-changelog/commit/ea8fb7eded13e6924388f044044a9898ec810163)) + +##### Config + +* Add bump package setting [#11](https://github.com/marcocesarato/php-conventional-changelog/issues/11) ([343dd6](https://github.com/marcocesarato/php-conventional-changelog/commit/343dd6e47259c0190aa41cad5a42597df64a0d0d)) +* Add package lock commit setting [#11](https://github.com/marcocesarato/php-conventional-changelog/issues/11) ([2fc824](https://github.com/marcocesarato/php-conventional-changelog/commit/2fc8247fa77792ac1a6f6805196f3f42605321ea)) + +### Bug Fixes + +* Add git command exists check ([9ecd9e](https://github.com/marcocesarato/php-conventional-changelog/commit/9ecd9ebb4979352f93e071a4b74686fe10c3b060)) + +##### Bump + +* Add lock files to commit [#11](https://github.com/marcocesarato/php-conventional-changelog/issues/11) ([4da206](https://github.com/marcocesarato/php-conventional-changelog/commit/4da2061af0fb9e8da24215ed174896d5d8f4eb04)) +* Unescape slashes on json encode [#11](https://github.com/marcocesarato/php-conventional-changelog/issues/11) ([29a83e](https://github.com/marcocesarato/php-conventional-changelog/commit/29a83e243c8fd29df984dfe33253f2836c8f9435)) + +--- + +## [1.9.10](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.9.9...v1.9.10) (2021-04-13) + + +### Bug Fixes + +* Replace command constants response for symfony 4 compatibility [#10](https://github.com/marcocesarato/php-conventional-changelog/issues/10) ([639aec](https://github.com/marcocesarato/php-conventional-changelog/commit/639aec65cdcb86a0d5cfe9715d8a6516594700e6)) + +--- + +## [1.9.9](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.9.8...v1.9.9) (2021-04-02) + + +### Bug Fixes + +* Commit properties from protected to public for sorting ([d3b658](https://github.com/marcocesarato/php-conventional-changelog/commit/d3b65854ee00139e43577b03d3d2eb04dd489fd3)) + +--- + +## [1.9.8](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.9.7...v1.9.8) (2021-04-02) + + +### Bug Fixes + +* Commit sorting on changelog [#9](https://github.com/marcocesarato/php-conventional-changelog/issues/9) ([0d402c](https://github.com/marcocesarato/php-conventional-changelog/commit/0d402cb2904e1aaff19d95616332462fdf519dec)) + +--- + +## [1.9.7](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.9.6...v1.9.7) (2021-03-27) + + +### Bug Fixes + +* Array key exists on mixed value check ([4007ab](https://github.com/marcocesarato/php-conventional-changelog/commit/4007abe2b325ae7235c69cd4d4f66fb1f2d8d461)) + +--- + +## [1.9.6](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.9.5...v1.9.6) (2021-03-12) + + +### Bug Fixes + +* Incorrect parsing of URLs [#7](https://github.com/marcocesarato/php-conventional-changelog/issues/7) ([24ca1c](https://github.com/marcocesarato/php-conventional-changelog/commit/24ca1c20b579625266ee8de656cb4616bb38fb85)) + +--- + +## [1.9.5](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.9.4...v1.9.5) (2021-03-02) + + +### Bug Fixes + + +##### Git + +* Incorrect user type in Mention ([0c9fc5](https://github.com/marcocesarato/php-conventional-changelog/commit/0c9fc5b7f1a4ad610a1429d38550e7a80d8c82a6)) + +--- + +## [1.9.4](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.9.3...v1.9.4) (2021-02-28) + + +### Bug Fixes + +* Remove tag prefix and suffix on history release header ([070113](https://github.com/marcocesarato/php-conventional-changelog/commit/070113ac76f2f6606bb2acd9a9c6f03d4fc8da07)) + +--- + +## [1.9.3](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.9.2...v1.9.3) (2021-02-22) + + +### Bug Fixes + +* Add tag prefix and suffix on compare url tag [#5](https://github.com/marcocesarato/php-conventional-changelog/issues/5) ([db81e0](https://github.com/marcocesarato/php-conventional-changelog/commit/db81e0ec63a665d03170165b1623f72b4b70b08e)) + +--- + +## [1.9.2](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.9.1...v1.9.2) (2021-02-14) + + +### Features + + +##### Git + +* Add closed attribute to references ([acb104](https://github.com/marcocesarato/php-conventional-changelog/commit/acb10474b5cb4f4002755ca7e55ab785c2750149)) +* Add mention and reference class ([b22fa5](https://github.com/marcocesarato/php-conventional-changelog/commit/b22fa5c46b54ed7de6072445c90ad2a8a1b7408a)) +* Commit classes auto compose when raw is empty ([0f7f9d](https://github.com/marcocesarato/php-conventional-changelog/commit/0f7f9d7d139c8ee0ccf53effa580735a0a40d508)) + +### Bug Fixes + +* Using new reference class on changelog generation ([03e3a2](https://github.com/marcocesarato/php-conventional-changelog/commit/03e3a2bbaebd26db6fef3a7e699020c30d89511c)) + +##### Config + +* Add isset check on setting ignore types [#4](https://github.com/marcocesarato/php-conventional-changelog/pull/4) ([22ab18](https://github.com/marcocesarato/php-conventional-changelog/commit/22ab180da24dec738f2dc1875590704ad473add9)) +* Remove empty check of setting ignore types [#4](https://github.com/marcocesarato/php-conventional-changelog/pull/4) ([4b764d](https://github.com/marcocesarato/php-conventional-changelog/commit/4b764def749372df2270ccee7cbb4f9c8dc7ce01)) + +##### Git + +* Add check empty commit on parse ([a26d6a](https://github.com/marcocesarato/php-conventional-changelog/commit/a26d6aeeecdd23e76773b5549b06800c4a240e09)) +* Footer references detection ([6a40c1](https://github.com/marcocesarato/php-conventional-changelog/commit/6a40c1a0a3fa67f088450e1aef45e295371797eb)) + +--- + +## [1.9.1](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.9.0...v1.9.1) (2021-02-13) + + +### Features + +* Add date format config ([05196a](https://github.com/marcocesarato/php-conventional-changelog/commit/05196ad943bffc5317df575d7a782ca04fb53421)) + +--- + +## [1.9.0](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.8.0...v1.9.0) (2021-02-13) + + +### Features + +* Add pre and post run hooks and changes sorting ([a38ef1](https://github.com/marcocesarato/php-conventional-changelog/commit/a38ef1d6f74128322d031ef9404888a6e1706d38), [668d2e](https://github.com/marcocesarato/php-conventional-changelog/commit/668d2ea679f92c9f1022b95f93b83b614fd6d461)) + +##### Git + +* Add delete tag method ([7712c2](https://github.com/marcocesarato/php-conventional-changelog/commit/7712c24494972365270aff896421f127003c2108)) +* Add get last commit hash method ([c650b5](https://github.com/marcocesarato/php-conventional-changelog/commit/c650b5e48b3c79959af2d8b941818c2740cc4fae)) +* Add no edit param to commit method ([bf4d1a](https://github.com/marcocesarato/php-conventional-changelog/commit/bf4d1a85e8303307dcd42c0318d6a33089bf49d7)) + +##### Semver + +* Pattern version validation ([e19170](https://github.com/marcocesarato/php-conventional-changelog/commit/e191708265b8764823f397dbd28a8597edd2e0cd)) + +--- + +## [1.8.0](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.7.0...v1.8.0) (2021-02-12) + + +### Features + +* Add pretty scope config ([f47356](https://github.com/marcocesarato/php-conventional-changelog/commit/f47356fac49edd01dab3f9cceb29481bab66758c)) +* Add hidden configurations ([42c9af](https://github.com/marcocesarato/php-conventional-changelog/commit/42c9afe7aee84ab75ccc4ce9f7add83cf1922f51)) +* Add user mentions ([214c75](https://github.com/marcocesarato/php-conventional-changelog/commit/214c75f84874ef4a92fd869230762c257d97c638)) + +### Bug Fixes + +* Breaking changes indicated by a ! and ignore duplicated or empty message [#1](https://github.com/marcocesarato/php-conventional-changelog/issues/1) ([f3ebee](https://github.com/marcocesarato/php-conventional-changelog/commit/f3ebeee6bdac4e0fc1010ce3f7d3afd58bfda381)) + +--- + +## [1.7.0](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.6.2...v1.7.0) (2021-02-12) + + +### Features + +* Urls and release commit formats ([7cb62a](https://github.com/marcocesarato/php-conventional-changelog/commit/7cb62acf85196bb576791bba6afd7ba55e2a3690)) + +##### Git + +* Add parse remote url method ([81812c](https://github.com/marcocesarato/php-conventional-changelog/commit/81812c0838964198d577240ae12e79d059e18cc4)) + +--- + +## [1.6.2](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.6.1...v1.6.2) (2021-02-10) + + +### Bug Fixes + +* Conventional commit wakeup class parse [#3](https://github.com/marcocesarato/php-conventional-changelog/issues/3) ([9eb3fb](https://github.com/marcocesarato/php-conventional-changelog/commit/9eb3fbee01f7e8717be67aaa5199bd51c7a17030)) + +--- + +## [1.6.1](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.6.0...v1.6.1) (2021-02-09) + + +### Bug Fixes + +* Option commit all ([d84122](https://github.com/marcocesarato/php-conventional-changelog/commit/d84122283b244d213c2e5ee7329bce696bf85bc1)) + +##### Config + +* Empty configurations from array [#1](https://github.com/marcocesarato/php-conventional-changelog/issues/1) ([481f05](https://github.com/marcocesarato/php-conventional-changelog/commit/481f051069f5c9a5ae2ec0f1375fa3ac8e81b9b4)) + +--- + +## [1.6.0](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.5.4...v1.6.0) (2021-01-28) + + +### Features + +* Add tag prefix and suffix ([469f16](https://github.com/marcocesarato/php-conventional-changelog/commit/469f166f117478198d9fbdb55a4ea2b7f1d02ff3)) +* Add commit all option ([323a5c](https://github.com/marcocesarato/php-conventional-changelog/commit/323a5c0c4bdb030938cea7b41056240cfbc4a9a6)) +* Add skip verify, bump and tag ([1ae0e9](https://github.com/marcocesarato/php-conventional-changelog/commit/1ae0e964abf46ba2668c6562f59133654bea700d)) + +### Bug Fixes + +* Option commit all ([d84122](https://github.com/marcocesarato/php-conventional-changelog/commit/d84122283b244d213c2e5ee7329bce696bf85bc1)) + +--- + +## [1.5.4](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.5.3...v1.5.4) (2021-01-24) + + +### Features + +* Add commit parent class with raw metadata ([962e0f](https://github.com/marcocesarato/php-conventional-changelog/commit/962e0f7474ba34d7cfe01660025f272df9742eb8)) + +##### Config + +* Add root setting ([2ad49c](https://github.com/marcocesarato/php-conventional-changelog/commit/2ad49c5614e5bcba17c95f142ea11e698d07a644)) + +### Bug Fixes + + +##### Config + +* Use default settings if is not a valid config return value ([55fd29](https://github.com/marcocesarato/php-conventional-changelog/commit/55fd292f3a5d9068502b9ce54997d5015e03ad94)) +* Check valid array ([849f43](https://github.com/marcocesarato/php-conventional-changelog/commit/849f43dcadef5361a64fa0ed5705a67850cef329)) + +--- + +## [1.5.3](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.5.2...v1.5.3) (2021-01-23) + + +### Bug Fixes + +* Autobump on specified version ([9e35af](https://github.com/marcocesarato/php-conventional-changelog/commit/9e35af66941124a209bbc0c3ea54963c55a2f92c)) + +--- + +## [1.5.2](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.5.1...v1.5.2) (2021-01-22) + + +### Features + +* Add check if inside a git work tree ([E3ad12](https://github.com/marcocesarato/php-conventional-changelog/commit/E3ad1287e1bfccfddf18bf52152dd1edba4eff2a)) + +##### Git + +* Add inside work tree method ([081196](https://github.com/marcocesarato/php-conventional-changelog/commit/0811968babb8de519a3f249b4dcdb7da33a5ad99)) + +### Bug Fixes + +* Auto bump new version code commit and tag ([Bf26ee](https://github.com/marcocesarato/php-conventional-changelog/commit/Bf26eee06484d2045c7f701f2e4b1f02fd430911)) + +##### Commit Parser + +* Change return to string nullable on getters hash and raw ([5aa565](https://github.com/marcocesarato/php-conventional-changelog/commit/5aa565eb18090287e4033a6178acbe0959537d0b)) + +##### Git + +* Get last tag name ([B265d0](https://github.com/marcocesarato/php-conventional-changelog/commit/B265d06b1f857d1f95db27a912fd74bd574c981a)) +* Quotes on values exec return ([4e78c4](https://github.com/marcocesarato/php-conventional-changelog/commit/4e78c4ea8bd4e0f290d20284a7820d5392c04064)) + +--- + +## [1.5.1](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.5.0...v1.5.1) (2021-01-22) + + +### Bug Fixes + +* Commits retrieve on ranges and remove additional whitespaces ([C3bffa](https://github.com/marcocesarato/php-conventional-changelog/commit/C3bffa74021727937c1f5b4a42efe534c820b943)) + +##### Config + +* Possibile issues with empty data ([179c1f](https://github.com/marcocesarato/php-conventional-changelog/commit/179c1fa95fac3853b24a57d0c933d09e5500c328)) + +##### Git + +* Get commits command compatibility and sorting get tags ([28d6ad](https://github.com/marcocesarato/php-conventional-changelog/commit/28d6ad76bb0ebfb1fe21ba70ec4e1b512ef6a3be)) + +--- + +## [1.5.0](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.4.3...v1.5.0) (2021-01-21) + + +### Features + +* Split types and presets configuration ([4239ec](https://github.com/marcocesarato/php-conventional-changelog/commit/4239ec0866ca0d2e21b8bf3394f2d888bf44c08a)) +* Add auto versioning semver bump ([82c883](https://github.com/marcocesarato/php-conventional-changelog/commit/82c8839300880d16c428abebca8f999f866baaeb)) +* Add summary ([C37116](https://github.com/marcocesarato/php-conventional-changelog/commit/C371169f3dde9bcdb16a9de6e339ae26d70a477a)) +* Add to tag and from tag options ([0b0c4b](https://github.com/marcocesarato/php-conventional-changelog/commit/0b0c4b06fcdb5e472914fa33f3175dbad3bac60e)) + +##### Command + +* Add custom configuration file option ([D65fc7](https://github.com/marcocesarato/php-conventional-changelog/commit/D65fc7472885116e891f383a6600d3df43b72e58)) + +##### Commit Parser + +* Add getters and has scope method ([83d99a](https://github.com/marcocesarato/php-conventional-changelog/commit/83d99aa44cba23bc0fe6b829c337cb9814a10bf9)) +* Set default values on body and scope ([B362bd](https://github.com/marcocesarato/php-conventional-changelog/commit/B362bd5167b2951333c12397c544233266676eda)) + +### Bug Fixes + +* Breaking changes preserving the initial commit and customizable label ([607f6e](https://github.com/marcocesarato/php-conventional-changelog/commit/607f6e09026eaee8b1b7e83fa4a7475d7a4f5694)) +* Add hash check and rename parser to conventional ([3e8996](https://github.com/marcocesarato/php-conventional-changelog/commit/3e899655c82737ca363995530ec300d4215c2a64)) +* Customize changelog file path ([1795ff](https://github.com/marcocesarato/php-conventional-changelog/commit/1795ffba53ea35108c05138054772e3bcde73b09)) + +##### Semver + +* Bump major and minor ([Fd7485](https://github.com/marcocesarato/php-conventional-changelog/commit/Fd7485f2b7ce77e32542b4431e37ea271d031878)) + +### Documentation + + +##### Readme + +* Add usage gif image and improve usage section ([Fbfdfe](https://github.com/marcocesarato/php-conventional-changelog/commit/Fbfdfe0f888aa5d36451c3e8bd589ddc7674b163)) +* Add config option and adjust usage list example ([Fbe9fd](https://github.com/marcocesarato/php-conventional-changelog/commit/Fbe9fdba9ee21ee915dcd18a04a3ff9240c90b1b)) +* Merged notes on configuration section ([9efedc](https://github.com/marcocesarato/php-conventional-changelog/commit/9efedcd91d1d84c022586e3d52ad88c02544547e)) + +### Chores + +* Implement types configuration ([4d90f1](https://github.com/marcocesarato/php-conventional-changelog/commit/4d90f1d2c1c7b87e6adfdb7b31de3cbc1df659c5)) + +##### Preset + +* Change fixes description ([Cbaa8f](https://github.com/marcocesarato/php-conventional-changelog/commit/Cbaa8fd627650303874f72e3206f8a1a6664064c)) + +--- + +## [1.4.4](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.4.3...v1.4.4) (2021-01-21) + + +### Features + +* Split types and presets configuration ([4239ec](https://github.com/marcocesarato/php-conventional-changelog/commit/4239ec0866ca0d2e21b8bf3394f2d888bf44c08a)) +* Add summary ([C37116](https://github.com/marcocesarato/php-conventional-changelog/commit/C371169f3dde9bcdb16a9de6e339ae26d70a477a)) + +##### Commit Parser + +* Add getters and has scope method ([83d99a](https://github.com/marcocesarato/php-conventional-changelog/commit/83d99aa44cba23bc0fe6b829c337cb9814a10bf9)) +* Set default values on body and scope ([B362bd](https://github.com/marcocesarato/php-conventional-changelog/commit/B362bd5167b2951333c12397c544233266676eda)) + +### Bug Fixes + +* Breaking changes preserving the initial commit and customizable label ([607f6e](https://github.com/marcocesarato/php-conventional-changelog/commit/607f6e09026eaee8b1b7e83fa4a7475d7a4f5694)) +* Add hash check and rename parser to conventional ([3e8996](https://github.com/marcocesarato/php-conventional-changelog/commit/3e899655c82737ca363995530ec300d4215c2a64)) +* Customize changelog file path ([1795ff](https://github.com/marcocesarato/php-conventional-changelog/commit/1795ffba53ea35108c05138054772e3bcde73b09)) + +##### Git + +* Format option quotes ([693860](https://github.com/marcocesarato/php-conventional-changelog/commit/693860b5ba9c5ddfd13919a48186aeaf22931f12)) + +### Chores + +##### Preset + +* Change fixes description ([Cbaa8f](https://github.com/marcocesarato/php-conventional-changelog/commit/Cbaa8fd627650303874f72e3206f8a1a6664064c)) + +--- + +## [1.4.3](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.4.2...v1.4.3) (2021-01-19) + + +### Features + +* Implement new commit parser and refactoring of helper classes ([F2f2ec](https://github.com/marcocesarato/php-conventional-changelog/commit/F2f2ec80adfa787d453817faa64906e9dd988e44)) +* Add new conventional commit parser ([603f72](https://github.com/marcocesarato/php-conventional-changelog/commit/603f724dc759f4762c9b09489cb7620a98a10d67)) +* Add breaking changes and issues references ([Bda545](https://github.com/marcocesarato/php-conventional-changelog/commit/Bda5458e6c190a861417fb0239f86057c1d0c22f)) + +### Bug Fixes + +* Semantic version extra part split ([183116](https://github.com/marcocesarato/php-conventional-changelog/commit/18311683929859060af3c449a76fad41cb2baa52)) +* Commit changes list generation ([68b1ff](https://github.com/marcocesarato/php-conventional-changelog/commit/68b1ff9f424b47d0965702724e3cce5866b88892)) +* Uppercase first char of scope on stringify ([4eaebe](https://github.com/marcocesarato/php-conventional-changelog/commit/4eaebe96c84fa74aefa39d60fa1aeb3777316ce9)) +* Move scope to string method to pretty string ([D64421](https://github.com/marcocesarato/php-conventional-changelog/commit/D644210b973b1b16e3feeb140c3da881667888fa)) + +### Chores + +* Change php requirements ([5997ec](https://github.com/marcocesarato/php-conventional-changelog/commit/5997ecf5f02bbeedd1e31fe98cc5f6d8d743cb14)) + +--- + +## [1.4.2](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.4.1...v1.4.2) (2021-01-19) + + +### Bug Fixes + +* Add chores to not notable types ([2c4d1c](https://github.com/marcocesarato/php-conventional-changelog/commit/2c4d1cdc23455f899922d1796bcd6fa343c6d589)) + +--- + +## [1.4.1](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.4.0...v1.4.1) (2021-01-18) + + +### Features + +* Add semantic version parser ([7b5a9f](https://github.com/marcocesarato/php-conventional-changelog/commit/7b5a9fd99a4557bc7c167b99baa7ac3107d6f6c6)) +* Add rc, alpha and beta release method ([f955e1](https://github.com/marcocesarato/php-conventional-changelog/commit/f955e18309fa8c304bb3fce0faaf23eb3e35eebe)) + +### Documentation + + +##### Readme + +* Add new options on command list ([1ce4e3](https://github.com/marcocesarato/php-conventional-changelog/commit/1ce4e3b60c065e4ab7b4a0eaf272e427ecd58425)) + +--- + +## [1.4.0](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.3.0...v1.4.0) (2021-01-18) + + +### Features + +* Add amend and no verify options ([625ed1](https://github.com/marcocesarato/php-conventional-changelog/commit/625ed1bfa6fc70716736be303b1f7a048b894756)) +* Add not tag option and add errors on commit and tagging ([285ad0](https://github.com/marcocesarato/php-conventional-changelog/commit/285ad0a1bfe13b2460653a02ad40243184e7d80a)) + +### Bug Fixes + + +##### Config + +* Get configs from project root or working dir ([ee9d65](https://github.com/marcocesarato/php-conventional-changelog/commit/ee9d654f3e340575cb69e3fa940083891c150716)) +* Improve configuration and adjusted some settings ([cd6bdf](https://github.com/marcocesarato/php-conventional-changelog/commit/cd6bdf1de89c603afce1735487c4264c683473ab)) + +### Documentation + + +##### Readme + +* Update description ([02c2c3](https://github.com/marcocesarato/php-conventional-changelog/commit/02c2c36c9e20aabbb708f6a12531300ea80e02b0)) +* Fix configuration comment ([da733a](https://github.com/marcocesarato/php-conventional-changelog/commit/da733a985030a9d5ab99fd3683551fa44267f0ff)) +* Add configuration notes and update example ([10c1f5](https://github.com/marcocesarato/php-conventional-changelog/commit/10c1f5a709961cb52a97009b63189dfaea8d1bf9)) + +--- + +## [1.3.0](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.2.0...v1.3.0) (2021-01-17) + + +### Features + +* Add config file inclusion from working dir ([2cbb9f](https://github.com/marcocesarato/php-conventional-changelog/commit/2cbb9ff0bae2d25d4801845fec46d01d529fafe9)) +* Implement configuration system ([28001b](https://github.com/marcocesarato/php-conventional-changelog/commit/28001b4fa9de6da256641a5cf377a72f281bbc2a)) + + +### Documentation + + +##### Readme + +* Add configuration section with an example ([bc6aa3](https://github.com/marcocesarato/php-conventional-changelog/commit/bc6aa3ea23d0cef82c150620d33fb6d50f4125c0)) + +--- + +## [1.2.0](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.1.0...v1.2.0) (2021-01-17) + + +### Features + +* Add history option ([d53555](https://github.com/marcocesarato/php-conventional-changelog/commit/d53555442dbff0375db46649a306234309ba60ae)) +* Add commit type exclusions options and add changelog file on commit ([055497](https://github.com/marcocesarato/php-conventional-changelog/commit/0554976a445d82db914cd256cc931f70b5923db6)) + +### Bug Fixes + +* Add current release on history only with commit option flagged ([bac00d](https://github.com/marcocesarato/php-conventional-changelog/commit/bac00d478a9a93831af797080841c6991c6d660b)) +* Git commit add files to the repository ([301184](https://github.com/marcocesarato/php-conventional-changelog/commit/301184dc9d17649b25db06f4e4d45ac316533c74)) +* Auto commit success message ([5dec89](https://github.com/marcocesarato/php-conventional-changelog/commit/5dec89ce412e37476868076a0e550b41d122049d)) +* Git commit release message ([b6f3e4](https://github.com/marcocesarato/php-conventional-changelog/commit/b6f3e461d348e21fbfde278385a8a7119defdf98)) + + +### Documentation + + +##### Readme + +* Add history option with examples ([33d51b](https://github.com/marcocesarato/php-conventional-changelog/commit/33d51b1ab1c7c9821554c407e69f6b95925755bc)) +* Add no chores and no refactor options on command list ([106114](https://github.com/marcocesarato/php-conventional-changelog/commit/106114359633a230270139b867b0be18af8086e2)) + +### Chores + +* Update history option description ([b7d180](https://github.com/marcocesarato/php-conventional-changelog/commit/b7d180fb0a506d79828b6d4ef88df7b33f4aab2d)) + +##### Composer + +* Add scripts for changelog and release ([a5bd92](https://github.com/marcocesarato/php-conventional-changelog/commit/a5bd92f4baff375a1cafb4bc87190ff346930cb9)) + +--- + +## [v1.1.0](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.0.2...vv1.1.0) (2021-01-17) + + +### Features + +* Add autoloader ([8e5667](https://github.com/marcocesarato/php-conventional-changelog/commit/8e5667550c2188e78a0d3ec8e5d388f413f0b813)) +* Add information output about commit tag and file path ([7c7f4f](https://github.com/marcocesarato/php-conventional-changelog/commit/7c7f4f43995dd88a5b836b7c5f45b1144b7856fe)) +* Implementing symfony console ([78e11b](https://github.com/marcocesarato/php-conventional-changelog/commit/78e11be242147cee1f7ab8ce526a9b969ef79b09)) +* Add first release option ([143d6b](https://github.com/marcocesarato/php-conventional-changelog/commit/143d6b7828c8989e33ef23d22476ed14a7d4d935)) + +### Documentation + +* Update description and add new instructions on readme ([3e1086](https://github.com/marcocesarato/php-conventional-changelog/commit/3e1086f844bd4af6609911333cbf97b0ed99cdee)) + +##### Readme + +* Fix json composer scripts ([118cfd](https://github.com/marcocesarato/php-conventional-changelog/commit/118cfd275099a8534cd38176d5b6e1820fc88a4e)) +* Remove duplicate line ([d88a00](https://github.com/marcocesarato/php-conventional-changelog/commit/d88a00b704f7d621f664facb5656e384b273f531)) +* Clarify bump version processs ([ba9227](https://github.com/marcocesarato/php-conventional-changelog/commit/ba92277bdd60c2ff97a1ee26918f8bfca5527652)) +* Add first release option, update commands list and last version code ([59e3bb](https://github.com/marcocesarato/php-conventional-changelog/commit/59e3bb92016dfcc1d553cd83450a0427007069a4)) + +### Chores + +* Indicate default value of patch flag on helper list ([91df9c](https://github.com/marcocesarato/php-conventional-changelog/commit/91df9cc1600b9a2fb91be2ad2ba3eed9dafd3802)) + +##### Code Standard + +* Change script for code standard fixing ([dba6bb](https://github.com/marcocesarato/php-conventional-changelog/commit/dba6bb1807ef72808cb879d913dad86eeee53c90)) + +--- + +## [v1.0.2](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.0.1...vv1.0.2) (2021-01-17) + + +### Documentation + +* Specified date format to use on commands ([cdb712](https://github.com/marcocesarato/php-conventional-changelog/commit/cdb71211ce49c04e8789338b700839aaeb5303e1)) + +### Chores + +##### Bin + +* Move conventional changelog file to root ([f7a20c](https://github.com/marcocesarato/php-conventional-changelog/commit/f7a20c5ae16ea372b26f6402695d94a47b432866)) + +--- + +## [v1.0.1](https://github.com/marcocesarato/php-conventional-changelog/compare/v1.0.0...vv1.0.1) (2021-01-17) + + +### Documentation + + +##### Readme + +* Add package description ([5a63a4](https://github.com/marcocesarato/php-conventional-changelog/commit/5a63a4e9e59347aa99753444e7156ca23b2b6058)) +* Add info shields ([b96291](https://github.com/marcocesarato/php-conventional-changelog/commit/b9629156f0af4c3207e77739a52ccb5ddcf4f4da)) + +### Chores + +* Report only fatal error ([0de3e0](https://github.com/marcocesarato/php-conventional-changelog/commit/0de3e0fc6d5419bd823bd73dd0bae27ecb7c3d33)) + +--- + +## [v1.0.0](https://github.com/marcocesarato/php-conventional-changelog/compare/021a49f43ef65ac7a594450374f1772eef1fd8b0...vv1.0.0) (2021-01-17) + +### Description + +- Generate changelogs and release notes from a project's commit messages and metadata using php composer and automate versioning with semver.org and conventionalcommits.org + +--- + diff --git a/vendor/marcocesarato/php-conventional-changelog/LICENSE b/vendor/marcocesarato/php-conventional-changelog/LICENSE new file mode 100644 index 00000000..f288702d --- /dev/null +++ b/vendor/marcocesarato/php-conventional-changelog/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/vendor/marcocesarato/php-conventional-changelog/README.md b/vendor/marcocesarato/php-conventional-changelog/README.md new file mode 100644 index 00000000..f13e9277 --- /dev/null +++ b/vendor/marcocesarato/php-conventional-changelog/README.md @@ -0,0 +1,212 @@ +
+ +

PHP Conventional Changelog

+ +![Version](https://img.shields.io/badge/version-1.17.0-brightgreen?style=for-the-badge) +![Requirements](https://img.shields.io/badge/php-%3E%3D%207.1.3-4F5D95?style=for-the-badge) +[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow?style=for-the-badge)](https://conventionalcommits.org) +![License](https://img.shields.io/github/license/marcocesarato/php-conventional-changelog?style=for-the-badge) +[![GitHub](https://img.shields.io/badge/GitHub-Repo-6f42c1?style=for-the-badge)](https://github.com/marcocesarato/php-conventional-changelog) + +#### If this project helped you out, please support us with a star :star: + +
+ +![](docs/images/logo.png) + +
+ +## Description + +When a new release of a software project is announced, it is convenient to generate documents that let the project +users know what were the changes and other relevant notes about the new project release. + +This package can help to automatically generate changelog and release note files, so the developers of the project +reduce the work that is necessary to finalize and check the new release of the project. + +This package can generate a changelog from a project's committing history messages and metadata using composer and automate versioning +with [**semver**](https://semver.org) and [**conventional-commits**](https://conventionalcommits.org). + +It provides a command that can be run from the terminal, or using composer scripts, +to generate a changelog file in **markdown** for the current project. + +The command may take parameters that define the releases of the project that will be considered to extract the changes +from the git history to generate the file. The package uses a configuration system with that permit to customize the +settings you may want to have your desired changelog generated. + +Look at our [CHANGELOG](CHANGELOG.md) file if you are looking for an example of a possible final result. + +### How to contribute + +Have an idea? Found a bug? Please raise to [ISSUES](https://github.com/marcocesarato/php-conventional-changelog/issues) +or [PULL REQUEST](https://github.com/marcocesarato/php-conventional-changelog/pulls). Contributions are welcome and are +greatly appreciated! Every little bit helps. + +## 📘 Requirements +- [php](https://www.php.net) >= 7.1.3 +- [git](https://git-scm.com) >= 2.1.4 + +## 📖 Installation + +You can install it easily with composer + +`composer require --dev marcocesarato/php-conventional-changelog` + +#### Scripts *(Optional)* + +For easy use the changelog generator or release faster your new version you can add to your `composer.json` the scripts: + +> **Notes:** you can customize it according to your needs + +``` +{ + ... + "scripts": { + "changelog": "conventional-changelog", + "release": "conventional-changelog --commit", + "release:patch": "conventional-changelog --patch --commit", + "release:minor": "conventional-changelog --minor --commit", + "release:major": "conventional-changelog --major --commit" + }, + ... +} +``` + +Now you can just run `composer changelog` or `composer release` (the last one will autobump the version code and commit changes) to generate your changelog. + +## 📘 Configuration + +> **Notes:** this procedure is *optional* and permit to overwriting/merging the default settings + +For customize settings you just needs to create a file named `.changelog` on the root of your project/on the working +dir or use the `--config` option to specify the location of your configuration file. + +> **Notes:**
+> - When a setting on the configuration file is not necessary just omit it +> - The default ignored types are: `build`, `chore`, `ci`, `docs`, `perf`, `refactor`, `revert`, `style`, `test` +> - To allow all types just keep empty `types` and set empty `ignoreTypes` + +You can have more info about reading the [config documentation](./docs/config.md). + +## 💻 Usage + +The changelog generator will generate a log of changes from the date of the last tag to the current date, and it will +put all commit logs in the latest version just created. + +![](docs/images/usage.gif) + +> **Notes:**
+> - Some of these options listed on examples could be used together at the same time (ex. `--first-release --commit`) +> - Auto bump of version code using the [Semantic Versioning](https://semver.org) (`MAJOR.MINOR.PATCH`) is enabled by default if not specified the release method. +> - `MAJOR`: At least one breaking change. +> - `MINOR`: At least one new feature. +> - `PATCH`: Default +> - Use these options to specify the release method: `--major`, `--minor`, `--patch`, `--rc`, `--beta`, `--alpha`. + +### Examples + +#### First version + +> **Notes:** use this option only if you don't need all history changes or is the first version, else run with `--history` option + +To generate your changelog for the first version run: + +```shell +php vendor/bin/conventional-changelog --first-release +``` + +#### New version + +To generate your changelog *(without committing files)* + +```shell +php vendor/bin/conventional-changelog +``` + +#### New release (with commit and tag) + +To generate your changelog with auto commit and auto versioning tagging run: + +```shell +php vendor/bin/conventional-changelog --commit +``` + +or to amend at an existing commit you can run: + +```shell +php vendor/bin/conventional-changelog --amend +``` + +#### History + +To generate your changelog with the entire history of changes of all releases + +> **Warn:** this operation will overwrite the `CHANGELOG.md` file if it already exists + +```shell +php vendor/bin/conventional-changelog --history +``` + +#### Date range + +To generate your changelog from a specified date to another specified date + +```shell +php vendor/bin/conventional-changelog --from-date="2020-12-01" --to-date="2021-01-01" +``` + +#### Tag range + +To generate your changelog from a specified tag to another specified tag + +```shell +php vendor/bin/conventional-changelog --from-tag="v1.0.2" --to-tag="1.0.4" +``` + +#### Specific version + +To generate your changelog with a specific version code + +```shell +php vendor/bin/conventional-changelog --ver="2.0.1" +``` + +### Commands List + +> **Info:** You can have more info about running `php vendor/bin/conventional-changelog --help` + +``` +Description: + Generate changelogs and release notes from a project's commit messagesand metadata and automate versioning with semver.org and conventionalcommits.org + +Usage: + changelog [options] [--] [] + +Arguments: + path Specify the path directory where generate changelog + +Options: + --config=CONFIG Specify the configuration file path + -c, --commit Commit the new release once changelog is generated + -a, --amend Amend commit the new release once changelog is generated + --commit-all Commit all changes the new release once changelog is generated + --first-release Run at first release (if --ver isn't specified version code it will be 1.0.0) + --from-date=FROM-DATE Get commits from specified date [YYYY-MM-DD] + --to-date=TO-DATE Get commits last tag date (or specified on --from-date) to specified date [YYYY-MM-DD] + --from-tag=FROM-TAG Get commits from specified tag + --to-tag=TO-TAG Get commits last tag (or specified on --from-tag) to specified tag + --major Major release (important changes) + --minor Minor release (add functionality) + --patch Patch release (bug fixes) [default] + --rc Release candidate + --beta Beta release + --alpha Alpha release + --ver=VER Specify the next release version code (semver) + --history Generate the entire history of changes of all releases + --no-verify Bypasses the pre-commit and commit-msg hooks + --no-tag Disable release auto tagging + --no-change-without-commits Do not apply change if no commits + --annotate-tag[=ANNOTATE-TAG] Make an unsigned, annotated tag object once changelog is generated [default: false] + --merged Only include commits whose tips are reachable from HEAD + -h, --help Display help for the given command. When no command is given display help for the changelog command +``` \ No newline at end of file diff --git a/vendor/marcocesarato/php-conventional-changelog/TODO.md b/vendor/marcocesarato/php-conventional-changelog/TODO.md new file mode 100644 index 00000000..a9ef1810 --- /dev/null +++ b/vendor/marcocesarato/php-conventional-changelog/TODO.md @@ -0,0 +1,3 @@ +# TODO + +- Check version history on semver code generator \ No newline at end of file diff --git a/vendor/marcocesarato/php-conventional-changelog/autoload.php b/vendor/marcocesarato/php-conventional-changelog/autoload.php new file mode 100644 index 00000000..920f07fe --- /dev/null +++ b/vendor/marcocesarato/php-conventional-changelog/autoload.php @@ -0,0 +1,14 @@ +=7.1.3", + "ext-json": "*", + "symfony/console": "^4 || ^5 || ^6" + }, + "extra": { + "hooks": { + "pre-commit": "composer fix-cs", + "pre-push": "composer check-cs", + "post-merge": "composer install" + } + } +} \ No newline at end of file diff --git a/vendor/marcocesarato/php-conventional-changelog/composer.lock b/vendor/marcocesarato/php-conventional-changelog/composer.lock new file mode 100644 index 00000000..4b3cbf19 --- /dev/null +++ b/vendor/marcocesarato/php-conventional-changelog/composer.lock @@ -0,0 +1,4086 @@ +{ + "_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": "c53ad5d8994ba867396f937338b0d9fa", + "packages": [ + { + "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": "" + }, + "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": "symfony/console", + "version": "v5.4.15", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "ea59bb0edfaf9f28d18d8791410ee0355f317669" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/ea59bb0edfaf9f28d18d8791410ee0355f317669", + "reference": "ea59bb0edfaf9f28d18d8791410ee0355f317669", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php73": "^1.9", + "symfony/polyfill-php80": "^1.16", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/string": "^5.1|^6.0" + }, + "conflict": { + "psr/log": ">=3", + "symfony/dependency-injection": "<4.4", + "symfony/dotenv": "<5.1", + "symfony/event-dispatcher": "<4.4", + "symfony/lock": "<4.4", + "symfony/process": "<4.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0" + }, + "require-dev": { + "psr/log": "^1|^2", + "symfony/config": "^4.4|^5.0|^6.0", + "symfony/dependency-injection": "^4.4|^5.0|^6.0", + "symfony/event-dispatcher": "^4.4|^5.0|^6.0", + "symfony/lock": "^4.4|^5.0|^6.0", + "symfony/process": "^4.4|^5.0|^6.0", + "symfony/var-dumper": "^4.4|^5.0|^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/v5.4.15" + }, + "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-10-26T21:41:52+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.2.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e", + "reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.3-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.2.1" + }, + "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-03-01T10:25:55+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "5bbc823adecdae860bb64756d639ecfec17b050a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a", + "reference": "5bbc823adecdae860bb64756d639ecfec17b050a", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "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.27.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": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "511a08c03c1960e08a883f4cffcacd219b758354" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/511a08c03c1960e08a883f4cffcacd219b758354", + "reference": "511a08c03c1960e08a883f4cffcacd219b758354", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "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.27.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": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/19bd1e4fcd5b91116f14d8533c57831ed00571b6", + "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "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.27.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": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "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.27.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": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/polyfill-php73", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php73.git", + "reference": "9e8ecb5f92152187c4799efd3c96b78ccab18ff9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/9e8ecb5f92152187c4799efd3c96b78ccab18ff9", + "reference": "9e8ecb5f92152187c4799efd3c96b78ccab18ff9", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php73\\": "" + }, + "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 7.3+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php73/tree/v1.27.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": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", + "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "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.27.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": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v3.2.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "a8c9cedf55f314f3a186041d19537303766df09a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/a8c9cedf55f314f3a186041d19537303766df09a", + "reference": "a8c9cedf55f314f3a186041d19537303766df09a", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^2.0" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "suggest": { + "symfony/service-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.3-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "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.2.1" + }, + "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-03-01T10:32:47+00:00" + }, + { + "name": "symfony/string", + "version": "v6.1.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "823f143370880efcbdfa2dbca946b3358c4707e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/823f143370880efcbdfa2dbca946b3358c4707e5", + "reference": "823f143370880efcbdfa2dbca946b3358c4707e5", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "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.1.7" + }, + "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-10-10T09:34:31+00:00" + } + ], + "packages-dev": [ + { + "name": "brainmaestro/composer-git-hooks", + "version": "v2.8.5", + "source": { + "type": "git", + "url": "https://github.com/BrainMaestro/composer-git-hooks.git", + "reference": "ffed8803690ac12214082120eee3441b00aa390e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/BrainMaestro/composer-git-hooks/zipball/ffed8803690ac12214082120eee3441b00aa390e", + "reference": "ffed8803690ac12214082120eee3441b00aa390e", + "shasum": "" + }, + "require": { + "php": "^5.6 || >=7.0", + "symfony/console": "^3.2 || ^4.0 || ^5.0" + }, + "require-dev": { + "ext-json": "*", + "friendsofphp/php-cs-fixer": "^2.9", + "phpunit/phpunit": "^5.7 || ^7.0" + }, + "bin": [ + "cghooks" + ], + "type": "library", + "extra": { + "hooks": { + "pre-commit": "composer check-style", + "pre-push": [ + "composer test", + "appver=$(grep -o -E '\\d.\\d.\\d' cghooks)", + "tag=$(git describe --tags --abbrev=0)", + "if [ \"$tag\" != \"v$appver\" ]; then", + "echo \"The most recent tag $tag does not match the application version $appver\\n\"", + "tag=${tag#v}", + "sed -i -E \"s/$appver/$tag/\" cghooks", + "exit 1", + "fi" + ] + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "BrainMaestro\\GitHooks\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ezinwa Okpoechi", + "email": "brainmaestro@outlook.com" + } + ], + "description": "Easily manage git hooks in your composer config", + "keywords": [ + "HOOK", + "composer", + "git" + ], + "support": { + "issues": "https://github.com/BrainMaestro/composer-git-hooks/issues", + "source": "https://github.com/BrainMaestro/composer-git-hooks/tree/v2.8.5" + }, + "time": "2021-02-08T15:59:11+00:00" + }, + { + "name": "composer/pcre", + "version": "3.1.0", + "source": { + "type": "git", + "url": "https://github.com/composer/pcre.git", + "reference": "4bff79ddd77851fe3cdd11616ed3f92841ba5bd2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/pcre/zipball/4bff79ddd77851fe3cdd11616ed3f92841ba5bd2", + "reference": "4bff79ddd77851fe3cdd11616ed3f92841ba5bd2", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.3", + "phpstan/phpstan-strict-rules": "^1.1", + "symfony/phpunit-bridge": "^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Pcre\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "keywords": [ + "PCRE", + "preg", + "regex", + "regular expression" + ], + "support": { + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.1.0" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-11-17T09:50:14+00:00" + }, + { + "name": "composer/semver", + "version": "3.3.2", + "source": { + "type": "git", + "url": "https://github.com/composer/semver.git", + "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/semver/zipball/3953f23262f2bff1919fc82183ad9acb13ff62c9", + "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.4", + "symfony/phpunit-bridge": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Semver\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "keywords": [ + "semantic", + "semver", + "validation", + "versioning" + ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.3.2" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-04-01T19:23:25+00:00" + }, + { + "name": "composer/xdebug-handler", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "ced299686f41dce890debac69273b47ffe98a40c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/ced299686f41dce890debac69273b47ffe98a40c", + "reference": "ced299686f41dce890debac69273b47ffe98a40c", + "shasum": "" + }, + "require": { + "composer/pcre": "^1 || ^2 || ^3", + "php": "^7.2.5 || ^8.0", + "psr/log": "^1 || ^2 || ^3" + }, + "require-dev": { + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-strict-rules": "^1.1", + "symfony/phpunit-bridge": "^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Composer\\XdebugHandler\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" + } + ], + "description": "Restarts a process without Xdebug.", + "keywords": [ + "Xdebug", + "performance" + ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/3.0.3" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-02-25T21:32:43+00:00" + }, + { + "name": "doctrine/annotations", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/annotations.git", + "reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f", + "reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f", + "shasum": "" + }, + "require": { + "doctrine/lexer": "^2 || ^3", + "ext-tokenizer": "*", + "php": "^7.2 || ^8.0", + "psr/cache": "^1 || ^2 || ^3" + }, + "require-dev": { + "doctrine/cache": "^2.0", + "doctrine/coding-standard": "^10", + "phpstan/phpstan": "^1.8.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "symfony/cache": "^5.4 || ^6", + "vimeo/psalm": "^4.10" + }, + "suggest": { + "php": "PHP 8.0 or higher comes with attributes, a native replacement for annotations" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Docblock Annotations Parser", + "homepage": "https://www.doctrine-project.org/projects/annotations.html", + "keywords": [ + "annotations", + "docblock", + "parser" + ], + "support": { + "issues": "https://github.com/doctrine/annotations/issues", + "source": "https://github.com/doctrine/annotations/tree/2.0.1" + }, + "time": "2023-02-02T22:02:53+00:00" + }, + { + "name": "doctrine/instantiator", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "doctrine/coding-standard": "^11", + "ext-pdo": "*", + "ext-phar": "*", + "phpbench/phpbench": "^1.2", + "phpstan/phpstan": "^1.9.4", + "phpstan/phpstan-phpunit": "^1.3", + "phpunit/phpunit": "^9.5.27", + "vimeo/psalm": "^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "https://ocramius.github.io/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", + "keywords": [ + "constructor", + "instantiate" + ], + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/2.0.0" + }, + "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%2Finstantiator", + "type": "tidelift" + } + ], + "time": "2022-12-30T00:23:10+00:00" + }, + { + "name": "doctrine/lexer", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/lexer.git", + "reference": "84a527db05647743d50373e0ec53a152f2cde568" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/84a527db05647743d50373e0ec53a152f2cde568", + "reference": "84a527db05647743d50373e0ec53a152f2cde568", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "doctrine/coding-standard": "^10", + "phpstan/phpstan": "^1.9", + "phpunit/phpunit": "^9.5", + "psalm/plugin-phpunit": "^0.18.3", + "vimeo/psalm": "^5.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Lexer\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "https://www.doctrine-project.org/projects/lexer.html", + "keywords": [ + "annotations", + "docblock", + "lexer", + "parser", + "php" + ], + "support": { + "issues": "https://github.com/doctrine/lexer/issues", + "source": "https://github.com/doctrine/lexer/tree/3.0.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", + "type": "tidelift" + } + ], + "time": "2022-12-15T16:57:16+00:00" + }, + { + "name": "friendsofphp/php-cs-fixer", + "version": "v3.15.1", + "source": { + "type": "git", + "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", + "reference": "d48755372a113bddb99f749e34805d83f3acfe04" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/d48755372a113bddb99f749e34805d83f3acfe04", + "reference": "d48755372a113bddb99f749e34805d83f3acfe04", + "shasum": "" + }, + "require": { + "composer/semver": "^3.3", + "composer/xdebug-handler": "^3.0.3", + "doctrine/annotations": "^2", + "doctrine/lexer": "^2 || ^3", + "ext-json": "*", + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0", + "sebastian/diff": "^4.0 || ^5.0", + "symfony/console": "^5.4 || ^6.0", + "symfony/event-dispatcher": "^5.4 || ^6.0", + "symfony/filesystem": "^5.4 || ^6.0", + "symfony/finder": "^5.4 || ^6.0", + "symfony/options-resolver": "^5.4 || ^6.0", + "symfony/polyfill-mbstring": "^1.27", + "symfony/polyfill-php80": "^1.27", + "symfony/polyfill-php81": "^1.27", + "symfony/process": "^5.4 || ^6.0", + "symfony/stopwatch": "^5.4 || ^6.0" + }, + "require-dev": { + "justinrainbow/json-schema": "^5.2", + "keradus/cli-executor": "^2.0", + "mikey179/vfsstream": "^1.6.11", + "php-coveralls/php-coveralls": "^2.5.3", + "php-cs-fixer/accessible-object": "^1.1", + "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.2", + "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.2.1", + "phpspec/prophecy": "^1.16", + "phpspec/prophecy-phpunit": "^2.0", + "phpunit/phpunit": "^9.5", + "phpunitgoodpractices/polyfill": "^1.6", + "phpunitgoodpractices/traits": "^1.9.2", + "symfony/phpunit-bridge": "^6.2.3", + "symfony/yaml": "^5.4 || ^6.0" + }, + "suggest": { + "ext-dom": "For handling output formats in XML", + "ext-mbstring": "For handling non-UTF8 characters." + }, + "bin": [ + "php-cs-fixer" + ], + "type": "application", + "autoload": { + "psr-4": { + "PhpCsFixer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Dariusz Rumiński", + "email": "dariusz.ruminski@gmail.com" + } + ], + "description": "A tool to automatically fix PHP code style", + "keywords": [ + "Static code analysis", + "fixer", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.15.1" + }, + "funding": [ + { + "url": "https://github.com/keradus", + "type": "github" + } + ], + "time": "2023-03-13T23:26:30+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.11.1", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3,<3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2023-03-08T13:26:56+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v4.15.4", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/6bb5176bc4af8bcb7d926f88718db9b96a2d4290", + "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=7.0" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.4" + }, + "time": "2023-03-05T19:49:14+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.3" + }, + "time": "2021-07-20T11:28:43+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "php-mock/php-mock", + "version": "2.4.0", + "source": { + "type": "git", + "url": "https://github.com/php-mock/php-mock.git", + "reference": "6f71999665d27fbdf684c1639981e96eff540b5f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-mock/php-mock/zipball/6f71999665d27fbdf684c1639981e96eff540b5f", + "reference": "6f71999665d27fbdf684c1639981e96eff540b5f", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0 || ^8.0", + "phpunit/php-text-template": "^1 || ^2 || ^3" + }, + "replace": { + "malkusch/php-mock": "*" + }, + "require-dev": { + "phpunit/phpunit": "^5.7 || ^6.5 || ^7.5 || ^8.0 || ^9.0 || ^10.0", + "squizlabs/php_codesniffer": "^3.5" + }, + "suggest": { + "php-mock/php-mock-phpunit": "Allows integration into PHPUnit testcase with the trait PHPMock." + }, + "type": "library", + "autoload": { + "files": [ + "autoload.php" + ], + "psr-4": { + "phpmock\\": [ + "classes/", + "tests/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "WTFPL" + ], + "authors": [ + { + "name": "Markus Malkusch", + "email": "markus@malkusch.de", + "homepage": "http://markus.malkusch.de", + "role": "Developer" + } + ], + "description": "PHP-Mock can mock built-in PHP functions (e.g. time()). PHP-Mock relies on PHP's namespace fallback policy. No further extension is needed.", + "homepage": "https://github.com/php-mock/php-mock", + "keywords": [ + "BDD", + "TDD", + "function", + "mock", + "stub", + "test", + "test double" + ], + "support": { + "issues": "https://github.com/php-mock/php-mock/issues", + "source": "https://github.com/php-mock/php-mock/tree/2.4.0" + }, + "funding": [ + { + "url": "https://github.com/michalbundyra", + "type": "github" + } + ], + "time": "2023-02-13T08:51:05+00:00" + }, + { + "name": "php-mock/php-mock-integration", + "version": "2.2.1", + "source": { + "type": "git", + "url": "https://github.com/php-mock/php-mock-integration.git", + "reference": "04f4a8d5442ca457b102b5204673f77323e3edb5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-mock/php-mock-integration/zipball/04f4a8d5442ca457b102b5204673f77323e3edb5", + "reference": "04f4a8d5442ca457b102b5204673f77323e3edb5", + "shasum": "" + }, + "require": { + "php": ">=5.6", + "php-mock/php-mock": "^2.4", + "phpunit/php-text-template": "^1 || ^2 || ^3" + }, + "require-dev": { + "phpunit/phpunit": "^5.7.27 || ^6 || ^7 || ^8 || ^9 || ^10" + }, + "type": "library", + "autoload": { + "psr-4": { + "phpmock\\integration\\": "classes/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "WTFPL" + ], + "authors": [ + { + "name": "Markus Malkusch", + "email": "markus@malkusch.de", + "homepage": "http://markus.malkusch.de", + "role": "Developer" + } + ], + "description": "Integration package for PHP-Mock", + "homepage": "https://github.com/php-mock/php-mock-integration", + "keywords": [ + "BDD", + "TDD", + "function", + "mock", + "stub", + "test", + "test double" + ], + "support": { + "issues": "https://github.com/php-mock/php-mock-integration/issues", + "source": "https://github.com/php-mock/php-mock-integration/tree/2.2.1" + }, + "funding": [ + { + "url": "https://github.com/michalbundyra", + "type": "github" + } + ], + "time": "2023-02-13T09:51:29+00:00" + }, + { + "name": "php-mock/php-mock-phpunit", + "version": "2.7.1", + "source": { + "type": "git", + "url": "https://github.com/php-mock/php-mock-phpunit.git", + "reference": "dc4cf8896bf47647080dc5709a2c67ee9d437c21" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-mock/php-mock-phpunit/zipball/dc4cf8896bf47647080dc5709a2c67ee9d437c21", + "reference": "dc4cf8896bf47647080dc5709a2c67ee9d437c21", + "shasum": "" + }, + "require": { + "php": ">=7", + "php-mock/php-mock-integration": "^2.2.1", + "phpunit/phpunit": "^6 || ^7 || ^8 || ^9 || ^10.0.17" + }, + "require-dev": { + "mockery/mockery": "^1.3.6" + }, + "type": "library", + "autoload": { + "files": [ + "autoload.php" + ], + "psr-4": { + "phpmock\\phpunit\\": "classes/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "WTFPL" + ], + "authors": [ + { + "name": "Markus Malkusch", + "email": "markus@malkusch.de", + "homepage": "http://markus.malkusch.de", + "role": "Developer" + } + ], + "description": "Mock built-in PHP functions (e.g. time()) with PHPUnit. This package relies on PHP's namespace fallback policy. No further extension is needed.", + "homepage": "https://github.com/php-mock/php-mock-phpunit", + "keywords": [ + "BDD", + "TDD", + "function", + "mock", + "phpunit", + "stub", + "test", + "test double" + ], + "support": { + "issues": "https://github.com/php-mock/php-mock-phpunit/issues", + "source": "https://github.com/php-mock/php-mock-phpunit/tree/2.7.1" + }, + "funding": [ + { + "url": "https://github.com/michalbundyra", + "type": "github" + } + ], + "time": "2023-03-21T06:55:31+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "9.2.26", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/443bc6912c9bd5b409254a40f4b0f4ced7c80ea1", + "reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.15", + "php": ">=7.3", + "phpunit/php-file-iterator": "^3.0.3", + "phpunit/php-text-template": "^2.0.2", + "sebastian/code-unit-reverse-lookup": "^2.0.2", + "sebastian/complexity": "^2.0", + "sebastian/environment": "^5.1.2", + "sebastian/lines-of-code": "^1.0.3", + "sebastian/version": "^3.0.1", + "theseer/tokenizer": "^1.2.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.26" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-03-06T12:58:08+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "3.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-12-02T12:48:52+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "3.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:58:55+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T05:33:50+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "5.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:16:10+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "9.6.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "86e761949019ae83f49240b2f2123fb5ab3b2fc5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/86e761949019ae83f49240b2f2123fb5ab3b2fc5", + "reference": "86e761949019ae83f49240b2f2123fb5ab3b2fc5", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.3.1 || ^2", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.10.1", + "phar-io/manifest": "^2.0.3", + "phar-io/version": "^3.0.2", + "php": ">=7.3", + "phpunit/php-code-coverage": "^9.2.13", + "phpunit/php-file-iterator": "^3.0.5", + "phpunit/php-invoker": "^3.1.1", + "phpunit/php-text-template": "^2.0.3", + "phpunit/php-timer": "^5.0.2", + "sebastian/cli-parser": "^1.0.1", + "sebastian/code-unit": "^1.0.6", + "sebastian/comparator": "^4.0.8", + "sebastian/diff": "^4.0.3", + "sebastian/environment": "^5.1.3", + "sebastian/exporter": "^4.0.5", + "sebastian/global-state": "^5.0.1", + "sebastian/object-enumerator": "^4.0.3", + "sebastian/resource-operations": "^3.0.3", + "sebastian/type": "^3.2", + "sebastian/version": "^3.0.2" + }, + "suggest": { + "ext-soap": "To be able to generate mocks based on WSDL files", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.6-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.5" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" + } + ], + "time": "2023-03-09T06:34:10+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": "" + }, + "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/event-dispatcher", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Standard interfaces for event handling.", + "keywords": [ + "events", + "psr", + "psr-14" + ], + "support": { + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + }, + "time": "2019-01-08T18:20:26+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": "" + }, + "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": "sebastian/cli-parser", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:08:49+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "1.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:08:54+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:30:19+00:00" + }, + { + "name": "sebastian/comparator", + "version": "4.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/diff": "^4.0", + "sebastian/exporter": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-09-14T12:41:17+00:00" + }, + { + "name": "sebastian/complexity", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.7", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T15:52:27+00:00" + }, + { + "name": "sebastian/diff", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:10:38+00:00" + }, + { + "name": "sebastian/environment", + "version": "5.1.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:03:51+00:00" + }, + { + "name": "sebastian/exporter", + "version": "4.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-09-14T06:03:37+00:00" + }, + { + "name": "sebastian/global-state", + "version": "5.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/0ca8db5a5fc9c8646244e629625ac486fa286bf2", + "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-02-14T08:28:10+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.6", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-28T06:42:11+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:12:34+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:14:26+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "4.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:07:39+00:00" + }, + { + "name": "sebastian/resource-operations", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "support": { + "issues": "https://github.com/sebastianbergmann/resource-operations/issues", + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:45:17+00:00" + }, + { + "name": "sebastian/type", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:13:03+00:00" + }, + { + "name": "sebastian/version", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c6c1022351a901512170118436c764e473f6de8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", + "reference": "c6c1022351a901512170118436c764e473f6de8c", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:39:44+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "v6.2.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "404b307de426c1c488e5afad64403e5f145e82a5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/404b307de426c1c488e5afad64403e5f145e82a5", + "reference": "404b307de426c1c488e5afad64403e5f145e82a5", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/event-dispatcher-contracts": "^2|^3" + }, + "conflict": { + "symfony/dependency-injection": "<5.4" + }, + "provide": { + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "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/error-handler": "^5.4|^6.0", + "symfony/expression-language": "^5.4|^6.0", + "symfony/http-foundation": "^5.4|^6.0", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/stopwatch": "^5.4|^6.0" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "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 that allow your application components to communicate with each other by dispatching events and listening to them", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/event-dispatcher/tree/v6.2.7" + }, + "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-02-14T08:44:56+00:00" + }, + { + "name": "symfony/event-dispatcher-contracts", + "version": "v3.2.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "0ad3b6f1e4e2da5690fefe075cd53a238646d8dd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/0ad3b6f1e4e2da5690fefe075cd53a238646d8dd", + "reference": "0ad3b6f1e4e2da5690fefe075cd53a238646d8dd", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/event-dispatcher": "^1" + }, + "suggest": { + "symfony/event-dispatcher-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.3-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\EventDispatcher\\": "" + } + }, + "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 dispatching event", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.2.1" + }, + "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-03-01T10:32:47+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v6.2.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "82b6c62b959f642d000456f08c6d219d749215b3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/82b6c62b959f642d000456f08c6d219d749215b3", + "reference": "82b6c62b959f642d000456f08c6d219d749215b3", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "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.2.7" + }, + "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-02-14T08:44:56+00:00" + }, + { + "name": "symfony/finder", + "version": "v6.2.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "20808dc6631aecafbe67c186af5dcb370be3a0eb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/20808dc6631aecafbe67c186af5dcb370be3a0eb", + "reference": "20808dc6631aecafbe67c186af5dcb370be3a0eb", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "symfony/filesystem": "^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "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": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v6.2.7" + }, + "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-02-16T09:57:23+00:00" + }, + { + "name": "symfony/options-resolver", + "version": "v6.2.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/options-resolver.git", + "reference": "aa0e85b53bbb2b4951960efd61d295907eacd629" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/aa0e85b53bbb2b4951960efd61d295907eacd629", + "reference": "aa0e85b53bbb2b4951960efd61d295907eacd629", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.1|^3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\OptionsResolver\\": "" + }, + "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 an improved replacement for the array_replace PHP function", + "homepage": "https://symfony.com", + "keywords": [ + "config", + "configuration", + "options" + ], + "support": { + "source": "https://github.com/symfony/options-resolver/tree/v6.2.7" + }, + "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-02-14T08:44:56+00:00" + }, + { + "name": "symfony/polyfill-php81", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/707403074c8ea6e2edaf8794b0157a0bfa52157a", + "reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "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.27.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": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/process", + "version": "v6.2.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "680e8a2ea6b3f87aecc07a6a65a203ae573d1902" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/680e8a2ea6b3f87aecc07a6a65a203ae573d1902", + "reference": "680e8a2ea6b3f87aecc07a6a65a203ae573d1902", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "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": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v6.2.7" + }, + "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-02-24T10:42:00+00:00" + }, + { + "name": "symfony/stopwatch", + "version": "v6.2.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/stopwatch.git", + "reference": "f3adc98c1061875dd2edcd45e5b04e63d0e29f8f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/f3adc98c1061875dd2edcd45e5b04e63d0e29f8f", + "reference": "f3adc98c1061875dd2edcd45e5b04e63d0e29f8f", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/service-contracts": "^1|^2|^3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Stopwatch\\": "" + }, + "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 a way to profile code", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/stopwatch/tree/v6.2.7" + }, + "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-02-14T08:44:56+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.1" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2021-07-28T10:34:58+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=7.1.3", + "ext-json": "*" + }, + "platform-dev": [], + "plugin-api-version": "2.3.0" +} diff --git a/vendor/marcocesarato/php-conventional-changelog/conventional-changelog b/vendor/marcocesarato/php-conventional-changelog/conventional-changelog new file mode 100644 index 00000000..22e9d959 --- /dev/null +++ b/vendor/marcocesarato/php-conventional-changelog/conventional-changelog @@ -0,0 +1,33 @@ +#!/usr/bin/env php +getName(); + +// Run application single command +$application = new Application('conventional-changelog', '1.17.0'); +$application->add($command); +$application->setDefaultCommand($commandName, true); +$application->run(); diff --git a/vendor/marcocesarato/php-conventional-changelog/docs/config.md b/vendor/marcocesarato/php-conventional-changelog/docs/config.md new file mode 100644 index 00000000..43db7836 --- /dev/null +++ b/vendor/marcocesarato/php-conventional-changelog/docs/config.md @@ -0,0 +1,168 @@ +# Configuration + +For customize settings you just needs to create a file named `.changelog` on the root of your project/ on the working +dir or use the `--config` option to specify the location of your configuration file. + +> **Notes:**
+> - When a setting on the configuration file is not necessary just omit it +> - To allow all types just keep empty `types` and set empty `ignoreTypes` + +## Settings + +- **Root:** Working directory of your project +- **Path:** Path to your changelog file (relative to the working dir/root) +- **Header Title:** Header title of the changelog +- **Header Description:** Header subtitle of the changelog +- **Sort By**: Sort changes by commit metadata (date, subject, authorName, authorEmail, authorDate, committerName, + committerEmail, committerDate) +- **Preset:** Add new types preset or modify existing types preset labels and description +- **Types:** Types allowed and showed on changelog. This setting could overwrite ignored types. +- **Package Bump:** Bump the package version in `composer.json` or `package.json` if files exists on the root path +- **Package Lock Commit:** Commit the package lock file (ex. `composer.lock`, `package.lock`, `yarn.lock`...) +- **Ignore Types:** Types ignored and so hidden on changelog +- **Ignore Patterns:** Patterns ignored and so hidden on changelog with a specific description. *(Regex are enabled)* +- **Tag Prefix:** Add prefix to release tag +- **Tag Suffix:** Add suffix to release tag +- **Skip Bump:** Skip automatic version code bump +- **Package Bumps:** Array of files to replace version, defaults to `['ConventionalChangelog\PackageBump\ComposerJson', 'ConventionalChangelog\PackageBump\PackageJson']` +- **Skip Tag:** Skip automatic commit tagging +- **Skip Verify:** Skip the pre-commit and commit-msg hooks +- **Disable Links:** Render text instead of link in changelog +- **Hidden Hash:** Hide commit hash from changelog +- **Hidden Mentions:** Hide users mentions from changelog +- **Hidden References:** Hide issue references from changelog +- **Pretty Scope:** Prettify the scope commit part (section name) on changelog *(ex. UserManager => User Manager or + user_config => User config)* +- **Url Protocol:** The URL protocol of all repository urls on changelogs (http/https) +- **Date Format:** The [format](https://www.php.net/manual/en/datetime.format.php) of the outputted date string +- **Changelog Version Format:** Allows the version header in changelog to have a configurable format +- **Commit Url Format:** A URL representing a specific commit at a hash +- **Compare Url Format:** A URL representing the comparison between two git sha +- **Issue Url Format:** A URL representing the issue format (allowing a different URL format to be swapped in for + Gitlab, Bitbucket, etc) +- **User Url Format:** A URL representing the a user's profile URL on GitHub, Gitlab, etc. This URL is used for + substituting @abc with https://github.com/abc in commit messages +- **Hidden Version Separator:** Hide version separator +- **Release Commit Message Format:** A string to be used to format the auto-generated release commit message +- **Pre Run**: Run a callback or command before run the script +- **Post Run**: Run a callback or command after run the script +- **Merged**: Only include commits whose tips are reachable from HEAD + +### Default settings + +These are the default settings: + +```php + getcwd(), + 'path' => 'CHANGELOG.md', + 'headerTitle' => 'Changelog', + 'headerDescription' => 'All notable changes to this project will be documented in this file.', + 'sortBy' => 'subject', + 'preset' => [ + // Breaking changes section + 'breaking_changes' => ['label' => '⚠ BREAKING CHANGES', 'description' => 'Code changes that potentially causes other components to fail'], + // Types section + 'feat' => ['label' => 'Features', 'description' => 'New features'], + 'perf' => ['label' => 'Performance Improvements', 'description' => 'Code changes that improves performance'], + 'fix' => ['label' => 'Bug Fixes', 'description' => 'Bugs and issues resolution'], + 'refactor' => ['label' => 'Code Refactoring', 'description' => 'A code change that neither fixes a bug nor adds a feature'], + 'style' => ['label' => 'Styles', 'description' => 'Changes that do not affect the meaning of the code'], + 'test' => ['label' => 'Tests', 'description' => 'Adding missing tests or correcting existing tests'], + 'build' => ['label' => 'Builds', 'description' => 'Changes that affect the build system or external dependencies '], + 'ci' => ['label' => 'Continuous Integrations', 'description' => 'Changes to CI configuration files and scripts'], + 'docs' => ['label' => 'Documentation', 'description' => 'Documentation changes'], + 'chore' => ['label' => 'Chores', 'description' => "Other changes that don't modify the source code or test files"], + 'revert' => ['label' => 'Reverts', 'description' => 'Reverts a previous commit'], + ], + 'types' => [], + 'packageBump' => true, + 'packageBumps' => [], + 'packageLockCommit' => true, + 'ignoreTypes' => ['build', 'chore', 'ci', 'docs', 'perf', 'refactor', 'revert', 'style', 'test'], + 'ignorePatterns' => ['/^chore\(release\):/i'], + 'tagPrefix' => 'v', + 'tagSuffix' => '', + 'skipBump' => false, + 'skipTag' => false, + 'skipVerify' => false, + 'disableLinks' => false, + 'hiddenHash' => false, + 'hiddenMentions' => false, + 'hiddenReferences' => false, + 'prettyScope' => true, + 'urlProtocol' => 'https', + 'dateFormat' => 'Y-m-d', + 'changelogVersionFormat' => '## {{version}} ({{date}})', + 'commitUrlFormat' => '{{host}}/{{owner}}/{{repository}}/commit/{{hash}}', + 'compareUrlFormat' => '{{host}}/{{owner}}/{{repository}}/compare/{{previousTag}}...{{currentTag}}', + 'issueUrlFormat' => '{{host}}/{{owner}}/{{repository}}/issues/{{id}}', + 'userUrlFormat' => '{{host}}/{{user}}', + 'releaseCommitMessageFormat' => 'chore(release): {{currentTag}}', + 'hiddenVersionSeparator' => false, + 'preRun' => null, + 'postRun' => null, + 'merged' => false, +]; +``` + +## Examples + +Configure your preferences with the help of the following examples. + +#### Short Example + +```php + ['feat', 'fix', 'perf', 'docs', 'chore'], + // Ignore chores with changelogs scope + 'ignorePatterns' => [ + '/chore\(changelog\)[:].*/i' + ], +]; +``` + +#### Full Example + +```php + dirname(__DIR__), // (ex. configs/changelog.php using --config option) + // File changelog (relative to the working dir/root) + 'path' => 'docs/CHANGELOG.md', // You can specify a different folder + 'headerTitle' => 'My changelog', + 'headerDescription' => 'This is my changelog file.', + 'preset' => [ + // Add improvements type (deprecated type) + 'improvements' => [ + 'label' => 'Improvements', + 'description' => 'Improvements to existing features' + ], + 'chore' => [ + // Change chore default label + 'label' => 'Others' + ], + ], + // Types allowed on changelog + 'types' => ['feat', 'fix', 'pref'], // These could overwrite ignored types + // Exclude not notables types (following types are the default excluded types) + 'ignoreTypes' => ['build', 'chore', 'ci', 'docs', 'refactor', 'revert', 'style', 'test'], + 'ignorePatterns' => [ + // Exclude all commits with this specific description + 'chore(deps): update dependencies', + // You can also use regex to exclude all commit like 'chore(changelog): updated' + '/chore\(changelog\)[:].*/i' + ], + 'tagPrefix' => 'ver', + 'tagSuffix' => '', + 'skipBump' => false, + 'skipTag' => false, + 'skipVerify' => true, +]; +``` diff --git a/vendor/marcocesarato/php-conventional-changelog/docs/images/logo.png b/vendor/marcocesarato/php-conventional-changelog/docs/images/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..7732f0d3329f86785e43f2dcb6edf862f1cdbd81 GIT binary patch literal 77910 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

0jy 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 rN7Al5d3U?-9Xfz5l4PTB6 + + + + ./tests + + + + + ./src + + + \ No newline at end of file diff --git a/vendor/marcocesarato/php-conventional-changelog/renovate.json b/vendor/marcocesarato/php-conventional-changelog/renovate.json new file mode 100644 index 00000000..39a2b6e9 --- /dev/null +++ b/vendor/marcocesarato/php-conventional-changelog/renovate.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:base" + ] +} diff --git a/vendor/marcocesarato/php-conventional-changelog/src/Changelog.php b/vendor/marcocesarato/php-conventional-changelog/src/Changelog.php new file mode 100644 index 00000000..2bcd4ba1 --- /dev/null +++ b/vendor/marcocesarato/php-conventional-changelog/src/Changelog.php @@ -0,0 +1,694 @@ +config = $config; + $this->remote = Repository::parseRemoteUrl(); + } + + /** + * Generate changelog. + */ + public function generate(string $root, InputInterface $input, SymfonyStyle $output): int + { + $nextVersion = $input->getOption('ver'); + $autoCommit = $input->getOption('commit'); // Commit once changelog is generated + $autoCommitAll = $input->getOption('commit-all'); // Commit all changes once changelog is generated + $autoTag = !($input->getOption('no-tag') || $this->config->skipTag()); // Tag release once is committed + $noChangeWithoutCommits = $input->getOption('no-change-without-commits'); + $annotateTag = $input->getOption('annotate-tag'); + $amend = $input->getOption('amend'); // Amend commit + $hooks = !$input->getOption('no-verify'); // Verify git hooks + $hooks = $hooks && $this->config->skipVerify() ? false : true; + $fromDate = $input->getOption('from-date'); + $toDate = $input->getOption('to-date'); + $fromTag = $input->getOption('from-tag'); + $toTag = $input->getOption('to-tag'); + $history = $input->getOption('history'); + $dateFormat = $this->config->getDateFormat(); + $sortBy = $this->config->getSortBy(); + $sortOrientation = $this->config->getSortOrientation($sortBy); + $merged = $input->getOption('merged'); + + $lastVersion = null; + $firstRelease = $input->getOption('first-release'); + $alphaRelease = $input->getOption('alpha'); + $betaRelease = $input->getOption('beta'); + $preRelease = $input->getOption('rc'); + $patchRelease = $input->getOption('patch'); + $minorRelease = $input->getOption('minor'); + $majorRelease = $input->getOption('major'); + + $tagPrefix = $this->config->getTagPrefix(); + $tagSuffix = $this->config->getTagSuffix(); + + $autoCommit = $autoCommit || $autoCommitAll; + $autoBump = false; + /** + * @var PackageBump[] + */ + $packageBumps = [ + ComposerJson::class, + PackageJson::class, + ]; + + // Allow config to specify own packages. + if ($this->config->getPackageBumps()) { + $packageBumps = $this->config->getPackageBumps(); + } + + $this->hasValidRemoteUrl = Repository::hasRemoteUrl() && !empty(Repository::parseRemoteUrl()); + + // Hook pre run + $this->config->preRun(); + + // If have amend option enable commit + if ($amend) { + $autoCommit = true; + } + + // Initialize changelogs + $file = $this->config->getPath(); + $dirname = dirname($file); + if (!is_file($file) && !is_dir($dirname)) { + $file = $root . DIRECTORY_SEPARATOR . $file; + } + $changelogCurrent = ''; + $changelogNew = ''; + + $mainHeaderPrefix = "\n# "; + $mainHeaderSuffix = "\n\n\n"; + $mainHeaderContent = $this->config->getHeaderTitle() . "\n\n" . $this->config->getHeaderDescription(); + $mainHeader = $mainHeaderPrefix . $mainHeaderContent . $mainHeaderSuffix; + + // Get changelogs content + if (file_exists($file)) { + $changelogCurrent = $this->removeHeader( + file_get_contents($file) + ); + } + + // Current Dates + $today = new \DateTime(); + // First release + $newVersion = '1.0.0'; + // First commit + $firstCommit = Repository::getFirstCommit(); + + if (!$firstRelease) { + $lastVersion = Repository::getLastTag($tagPrefix, $merged); + $bumpRelease = SemanticVersion::PATCH; + + if ($majorRelease) { + $bumpRelease = SemanticVersion::MAJOR; + } elseif ($minorRelease) { + $bumpRelease = SemanticVersion::MINOR; + } elseif ($patchRelease) { + $bumpRelease = SemanticVersion::PATCH; + } elseif ($preRelease) { + $bumpRelease = SemanticVersion::RC; + $lastVersion = Repository::getLastTag($tagPrefix, $merged, SemanticVersion::RC); + $autoBump = !$this->config->skipBump(); + } elseif ($betaRelease) { + $bumpRelease = SemanticVersion::BETA; + $lastVersion = Repository::getLastTag($tagPrefix, $merged, SemanticVersion::BETA); + $autoBump = !$this->config->skipBump(); + } elseif ($alphaRelease) { + $bumpRelease = SemanticVersion::ALPHA; + $lastVersion = Repository::getLastTag($tagPrefix, $merged, SemanticVersion::ALPHA); + $autoBump = !$this->config->skipBump(); + } else { + $autoBump = !$this->config->skipBump(); + } + + // Generate new version code + $semver = new SemanticVersion($lastVersion, $tagPrefix); + $newVersion = $semver->bump($bumpRelease); + } + if (!empty($nextVersion)) { + $newVersion = $nextVersion; + $autoBump = false; + } + $newVersion = preg_replace('/^' . preg_quote($tagPrefix, '/') . '/', '', $newVersion); + + $options = []; // Git retrieve options per version + + if ($history) { + $changelogCurrent = ''; // Clean changelog file + $tags = Repository::getTags($tagPrefix); + + $previousTag = null; + foreach ($tags as $key => $toTag) { + $fromTag = $firstCommit; + if (!empty($previousTag) && $key !== 0) { + $fromTag = $previousTag; + } + $commitDate = Repository::getCommitDate($toTag); + $options[$toTag] = [ + 'from' => $fromTag, + 'to' => $toTag, + 'date' => $commitDate->format($dateFormat), + 'options' => "{$fromTag}...{$toTag}", + 'autoBump' => false, + ]; + $previousTag = $toTag; + } + if ($autoCommit) { + $options[$newVersion] = [ + 'from' => $lastVersion, + 'to' => $newVersion, + 'date' => $today->format($dateFormat), + 'options' => "{$lastVersion}...HEAD", + 'autoBump' => false, + ]; + } + $options = array_reverse($options); + } else { + if ($firstRelease) { + // Get all commits from the first one + $additionalParams = "{$firstCommit}...HEAD"; + $lastVersion = $firstCommit; + if (empty($fromTag)) { + $fromTag = $firstCommit; + } + } else { + // Get latest commits from last version date + $additionalParams = "{$lastVersion}...HEAD"; + if (empty($fromTag)) { + $fromTag = $lastVersion; + } + } + + // Clean ranges + if ((!empty($fromDate) || !empty($toDate)) && + empty($fromTag) && + empty($toTag)) { + $additionalParams = ''; + } + + // Tag range + if (!empty($fromTag) || + !empty($toTag)) { + if (empty($toTag)) { + $toTag = 'HEAD'; + } + $additionalParams = "{$fromTag}...{$toTag}"; + } + + // Date range + if (!empty($fromDate) || + !empty($toDate)) { + if (!empty($fromDate)) { + $additionalParams .= ' --since="' . date('Y-m-d', strtotime($fromDate)) . '"'; + } + if (!empty($toDate)) { + $time = strtotime($toDate); + $additionalParams .= ' --before="' . date('Y-m-d', $time) . '"'; + $today->setTimestamp($time); + } + } + + $options[$newVersion] = [ + 'from' => $lastVersion, + 'to' => $newVersion, + 'date' => $today->format($dateFormat), + 'options' => $additionalParams, + 'autoBump' => $autoBump, + ]; + } + + $summary = []; + foreach ($this->config->getTypes() as $type) { + $summary[$type] = 0; + } + + foreach ($options as $params) { + $commitsRaw = Repository::getCommits($params['options']); + usort($commitsRaw, function ($x, $y) use ($sortBy, $sortOrientation) { + if (property_exists($x, $sortBy)) { + if ($sortOrientation === 'ASC') { + return $x->{$sortBy} <=> $y->{$sortBy}; + } + + return $y->{$sortBy} <=> $x->{$sortBy}; + } + + return 0; + }); + + // Get all commits information + $commits = []; + foreach ($commitsRaw as $commitRaw) { + $commit = ConventionalCommit::fromCommit($commitRaw); + + // Not a conventional commit + if (!$commit->isValid()) { + continue; + } + + // Check ignored commit + $ignore = false; + foreach ($this->config->getIgnorePatterns() as $pattern) { + if (preg_match($pattern, $commit->getHeader())) { + $ignore = true; + break; + } + } + // Add commit + if (!$ignore) { + $commits[] = $commit; + } + } + + // Changes groups sorting + $changes = []; + foreach ($this->config->getTypes() as $type) { + $changes[$type] = []; + } + + // Group all changes to lists by type + $types = $this->config->getAllowedTypes(); + foreach ($commits as $commit) { + if (in_array($commit->getType(), $types) || $commit->isBreakingChange()) { + $itemKey = $this->getItemKey($commit->getDescription()); + $breakingChanges = $commit->getBreakingChanges(); + $type = (string)$commit->getType(); + $scope = $commit->getScope(); + if ($this->config->isPrettyScope()) { + $scope = $commit->getScope()->toPrettyString(); + } + $hash = $commit->getHash(); + foreach ($breakingChanges as $description) { + $breakingType = Configuration::BREAKING_CHANGES_TYPE; + $key = $this->getItemKey($description); + if (empty($description) || $itemKey === $key) { + $commit->setBreakingChange(true); + continue; + } + // Clone commit as breaking with different description message + $breakingCommit = new ConventionalCommit(); + $breakingCommit->setType($breakingType) + ->setDescription($description) + ->setScope($scope) + ->setHash($hash); + $changes[$breakingType][(string)$scope][$key][$hash] = $breakingCommit; + $summary[$breakingType]++; + } + $changes[$type][(string)$scope][$itemKey][$hash] = $commit; + $summary[$type]++; + } + } + + if ($params['autoBump']) { + $semver = new SemanticVersion($params['from'], $tagPrefix); + $bumpRelease = SemanticVersion::PATCH; + + if ($summary['breaking_changes'] > 0) { + $bumpRelease = SemanticVersion::MAJOR; + + if (version_compare($semver->getVersion(), '1.0.0', '<')) { + $bumpRelease = SemanticVersion::MINOR; + } + } elseif ($summary['feat'] > 0) { + $bumpRelease = SemanticVersion::MINOR; + } + + $newVersion = $semver->bump($bumpRelease); + + if ($preRelease) { + $extraRelease = Repository::getLastReleaseCandidateTag($tagPrefix, $merged); + $bumpExtra = SemanticVersion::RC; + } elseif ($alphaRelease) { + $extraRelease = Repository::getLastAlphaTag($tagPrefix, $merged); + $bumpExtra = SemanticVersion::ALPHA; + } elseif ($betaRelease) { + $extraRelease = Repository::getLastBetaTag($tagPrefix, $merged); + $bumpExtra = SemanticVersion::BETA; + } + + if (!empty($bumpExtra)) { + $semVer = new SemanticVersion($extraRelease, $tagPrefix); + $newVersion = $semVer->bump($bumpRelease, $bumpExtra); + } + + $params['to'] = $newVersion; + } + + // Initialize changelogs + $params['to'] = $this->getVersionCode($params['to'], $tagPrefix, $tagSuffix); + $compareUrl = $this->getCompareUrl($params['from'], "{$tagPrefix}{$params['to']}{$tagSuffix}"); + $markdownCompareLink = $this->getMarkdownLink($params['to'], $compareUrl); + $changeLogVersionHeading = $this->getChangelogVersionHeading($markdownCompareLink, $params['date']); + $changelogNew .= $changeLogVersionHeading . "\n"; + // Add all changes list to new changelog + $changelogNew .= $this->getMarkdownChanges($changes); + } + $filesToCommit = [$file]; + + if (isset($commits) && !count($commits) && $noChangeWithoutCommits) { + $output->warning('No commits since last version.'); + + return 0; // Command::SUCCESS; + } + + if ($this->config->isPackageBump()) { + foreach ($packageBumps as $packageBump) { + try { + /** + * @var PackageBump + */ + $bumper = new $packageBump($root); + if ($bumper->exists()) { + $bumper->setVersion($newVersion); + $bumper->save(); + $filesToCommit[] = $bumper->getFilePath(); + if ($this->config->isPackageLockCommit()) { + $filesToCommit = array_merge($filesToCommit, $bumper->getExistingLockFiles()); + } + } + } catch (\Exception $e) { + $output->error('An error occurred bumping package version: ' . $e->getMessage()); + } + } + $filesToCommit = array_unique($filesToCommit); + } + + // Print summary + if (!empty($summary)) { + $output->section('Summary'); + $elements = []; + foreach ($summary as $type => $count) { + if ($count > 0) { + $elements[] = $count . ' ' . $this->config->getTypeDescription($type); + } + } + $output->listing($elements); + } + + // Save new changelog prepending the current one + file_put_contents($file, $mainHeader . "{$changelogNew}{$changelogCurrent}"); + $output->success('Changelog generated!'); + $output->writeln(" > Changelog file: {$file}"); + + // Create commit + if ($autoCommit) { + if ($autoCommitAll) { + Repository::addAll(); + } + $message = $this->getReleaseCommitMessage($newVersion); + $result = Repository::commit($message, $filesToCommit, $amend, $hooks); + if ($result !== false) { + $output->success('Release committed!'); + // Create tag + if ($autoTag) { + $tag = $tagPrefix . $newVersion . $tagSuffix; + $result = Repository::tag($tag, $annotateTag); + if ($result !== false) { + $output->success("Release tagged with success! New version: {$tag}"); + } else { + $output->error('An error occurred tagging the release!'); + + return 1; // Command::FAILURE; + } + } + } else { + $output->error('An error occurred committing the release!'); + + return 1; // Command::FAILURE; + } + } + + // Hook post run + $this->config->postRun(); + + return 0; // Command::SUCCESS; + } + + protected function removeHeader(string $content): string + { + return ltrim(preg_replace( + '#.*(.*?)#iUs', + '\1', + $content + )); + } + + /** + * Get version code. + */ + protected function getVersionCode(string $tag, string $prefix, string $suffix): string + { + $version = preg_replace('#^v#i', '', $tag); + + // Remove tag prefix + $rePrefix = '/^' . preg_quote($prefix, '/') . '/'; + $version = preg_replace($rePrefix, '', $version); + + // Remove tag suffix + $reSuffix = '/' . preg_quote($suffix, '/') . '$/'; + $version = preg_replace($reSuffix, '', $version); + + return $version; + } + + /** + * Generate item key for merge commit with similar description. + */ + protected function getItemKey(string $string): string + { + return strtolower(preg_replace('/[^a-zA-Z0-9_-]+/', '', $string)); + } + + /** + * Generate markdown from changes. + * + * @param ConventionalCommit[][][][] $changes + */ + protected function getMarkdownChanges(array $changes): string + { + $changelog = ''; + // Add all changes list to new changelog + foreach ($changes as $type => $list) { + if (empty($list)) { + continue; + } + $label = $this->config->getTypeLabel($type); + $changelog .= "\n### {$label}\n\n"; + ksort($list); + foreach ($list as $scope => $items) { + if ($this->config->getSortBy() === 'subject') { + ksort($items, SORT_NATURAL); + } + if (is_string($scope) && !empty($scope)) { + // scope section + $changelog .= "\n##### {$scope}\n\n"; + } + foreach ($items as $itemsList) { + $description = ''; + $sha = ''; + $references = ''; + $mentions = ''; + $shaGroup = []; + $refsGroup = []; + $mentionsGroup = []; + foreach ($itemsList as $item) { + $description = ucfirst($item->getDescription()); + // Hashes + if (!$this->config->isHiddenHash() && !empty($item->getHash())) { + $commitUrl = $this->getCommitUrl($item->getHash()); + $shaGroup[] = $this->getMarkdownLink($item->getShortHash(), $commitUrl); + } + // References + if (!$this->config->isHiddenReferences()) { + $refs = $item->getReferences(); + foreach ($refs as $ref) { + $refId = $ref->getId(); + $refUrl = $this->getIssueUrl($refId); + $refsGroup[] = $this->getMarkdownLink($ref, $refUrl); + } + } + // Mentions + $commitMentions = $item->getMentions(); + foreach ($commitMentions as $mention) { + $user = $mention->getUser(); + $userUrl = $this->getUserUrl($user); + $text = $this->getMarkdownLink("*{$mention}*", $userUrl); + if (strpos($description, (string)$mention) !== false) { + $description = str_replace($mention, $text, $description); + } elseif (!$this->config->isHiddenMentions()) { + $mentionsGroup[] = $text; + } + } + } + + if (!$this->config->isHiddenHash() && !empty($shaGroup)) { + $sha = '(' . implode(', ', $shaGroup) . ')'; + } + if (!$this->config->isHiddenReferences() && !empty($refsGroup)) { + $references = implode(', ', $refsGroup); + } + if (!$this->config->isHiddenMentions() && !empty($mentionsGroup)) { + $mentions = '*[*' . implode(', ', $mentionsGroup) . '*]*'; + } + $changelog .= Formatter::clean("* {$description} {$references} {$sha} {$mentions}"); + $changelog .= PHP_EOL; + } + } + } + // Add version separator + if (!$this->config->isHiddenVersionSeparator()) { + $changelog .= "\n\n---\n"; + } + + $changelog .= "\n"; + + return $changelog; + } + + /** + * Get Markdown link. + */ + protected function getMarkdownLink(string $text, string $url): string + { + $url = preg_replace('/([^:])(\/{2,})/', '$1/', $url); // Remove double slashes + + return !$this->config->isDisableLinks() ? "[{$text}]({$url})" : $text; + } + + protected function getChangelogVersionHeading($markdownCompareLink, $versionDate) + { + $versionFormat = $this->config->getChangelogVersionFormat(); + + // Handle the replacements. + $versionData = $this->getCompiledString($versionFormat, [ + 'version' => $markdownCompareLink, + 'date' => $versionDate, + ]); + + return $versionData; + } + + /** + * Compile string with values. + */ + public function getCompiledString(string $format, array $values): string + { + $string = $format; + $values = array_merge($this->remote, $values); + foreach ($values as $key => $value) { + $string = str_replace('{{' . $key . '}}', $value, $string); + } + + return $string; + } + + /** + * Get commit url. + */ + public function getCommitUrl(string $hash): string + { + if (!$this->hasValidRemoteUrl) { + return '#'; + } + + $protocol = $this->config->getUrlProtocol(); + $format = $this->config->getCommitUrlFormat(); + $url = $this->getCompiledString($format, ['hash' => $hash]); + + return "{$protocol}://{$url}"; + } + + /** + * Get commit compare url. + */ + public function getCompareUrl(string $previousTag, string $currentTag): string + { + if (!$this->hasValidRemoteUrl) { + return '#'; + } + + $protocol = $this->config->getUrlProtocol(); + $format = $this->config->getCompareUrlFormat(); + $url = $this->getCompiledString($format, ['previousTag' => $previousTag, 'currentTag' => $currentTag]); + + return "{$protocol}://{$url}"; + } + + /** + * Get issue url. + */ + public function getIssueUrl(string $id): string + { + if (!$this->hasValidRemoteUrl) { + return '#'; + } + + $protocol = $this->config->getUrlProtocol(); + $format = $this->config->getIssueUrlFormat(); + $url = $this->getCompiledString($format, ['id' => $id]); + + return "{$protocol}://{$url}"; + } + + /** + * Get user url. + */ + public function getUserUrl(string $user): string + { + if (!$this->hasValidRemoteUrl) { + return '#'; + } + + $protocol = $this->config->getUrlProtocol(); + $format = $this->config->getUserUrlFormat(); + $url = $this->getCompiledString($format, ['user' => $user]); + + return "{$protocol}://{$url}"; + } + + /** + * Get release commit message. + */ + public function getReleaseCommitMessage(string $tag): string + { + $format = $this->config->getReleaseCommitMessageFormat(); + + return $this->getCompiledString($format, ['currentTag' => $tag]); + } +} diff --git a/vendor/marcocesarato/php-conventional-changelog/src/Configuration.php b/vendor/marcocesarato/php-conventional-changelog/src/Configuration.php new file mode 100644 index 00000000..668dc27f --- /dev/null +++ b/vendor/marcocesarato/php-conventional-changelog/src/Configuration.php @@ -0,0 +1,933 @@ + ['label' => 'Features', 'description' => 'New features'], + 'perf' => ['label' => 'Performance Improvements', 'description' => 'Code changes that improves performance'], + 'fix' => ['label' => 'Bug Fixes', 'description' => 'Bugs and issues resolution'], + 'refactor' => ['label' => 'Code Refactoring', 'description' => 'A code change that neither fixes a bug nor adds a feature'], + 'style' => ['label' => 'Styles', 'description' => 'Changes that do not affect the meaning of the code'], + 'test' => ['label' => 'Tests', 'description' => 'Adding missing tests or correcting existing tests'], + 'build' => ['label' => 'Builds', 'description' => 'Changes that affect the build system or external dependencies '], + 'ci' => ['label' => 'Continuous Integrations', 'description' => 'Changes to CI configuration files and scripts'], + 'docs' => ['label' => 'Documentation', 'description' => 'Documentation changes'], + 'chore' => ['label' => 'Chores', 'description' => "Other changes that don't modify the source code or test files"], + 'revert' => ['label' => 'Reverts', 'description' => 'Reverts a previous commit'], + ]; + + /** + * Key of breaking changes. + * + * @var string + */ + public const BREAKING_CHANGES_TYPE = 'breaking_changes'; + + /** + * Preset of breaking changes. + * + * @var string[][] + */ + protected $breakingChangesPreset = ['label' => '⚠ BREAKING CHANGES', 'description' => 'Code changes that potentially causes other components to fail']; + + /** + * Types allowed on changelog. + * + * @var string[][] + */ + protected $types = []; + + /** + * Bump package. + * + * @var bool + */ + protected $packageBump = true; + + /** + * Bump packages. + * + * @var array + */ + protected $packageBumps = []; + + /** + * Commit package lock file. + * + * @var bool + */ + protected $packageLockCommit = true; + + /** + * Ignore message commit patterns. + * + * @var string[] + */ + protected $ignorePatterns = [ + '/^chore\(release\):/i', + ]; + + /** + * Ignore types. + * + * @var string[] + */ + protected $ignoreTypes = ['build', 'chore', 'ci', 'docs', 'perf', 'refactor', 'revert', 'style', 'test']; + + /** + * Tag prefix. + * + * @var string + */ + protected $tagPrefix = 'v'; + + /** + * Skip tag. + * + * @var bool + */ + protected $skipTag = false; + + /** + * Skip bump. + * + * @var bool + */ + protected $skipBump = false; + + /** + * Skip verify. + * + * @var bool + */ + protected $skipVerify = false; + + /** + * Render text instead of links. + * + * @var bool + */ + protected $disableLinks = false; + + /** + * Hidden references. + * + * @var bool + */ + protected $hiddenReferences = false; + + /** + * Hidden mentions. + * + * @var bool + */ + protected $hiddenMentions = false; + + /** + * Hidden hash. + * + * @var bool + */ + protected $hiddenHash = false; + + /** + * Tag suffix. + * + * @var string + */ + protected $tagSuffix = ''; + + /** + * The URL protocol of all repository urls on changelogs. + * + * @var string + */ + protected $urlProtocol = 'https'; + + /** + * Date format. + * + * @var string + */ + protected $dateFormat = 'Y-m-d'; + + /** + * Allows configurable changelog version header format. + * + * @var string + */ + protected $changelogVersionFormat = '## {{version}} ({{date}})'; + + /** + * A URL representing a specific commit at a hash. + * + * @var string + */ + protected $commitUrlFormat = '{{host}}/{{owner}}/{{repository}}/commit/{{hash}}'; + + /** + * A URL representing the comparison between two git sha. + * + * @var string + */ + protected $compareUrlFormat = '{{host}}/{{owner}}/{{repository}}/compare/{{previousTag}}...{{currentTag}}'; + + /** + * A URL representing the issue format (allowing a different URL format to be swapped in for Gitlab, Bitbucket, etc). + * + * @var string + */ + protected $issueUrlFormat = '{{host}}/{{owner}}/{{repository}}/issues/{{id}}'; + + /** + * A URL representing the a user's profile URL on GitHub, Gitlab, etc. This URL is used for substituting @abc with https://github.com/abc in commit messages. + * + * @var string + */ + protected $userUrlFormat = '{{host}}/{{user}}'; + + /** + * A string to be used to format the auto-generated release commit message. + * + * @var string + */ + protected $releaseCommitMessageFormat = 'chore(release): {{currentTag}}'; + + /** + * Prettify scope. + * + * @var bool + */ + protected $prettyScope = true; + + /** + * Hide Version Separator. + * + * @var bool + */ + protected $hiddenVersionSeparator = false; + + /** + * Sort by options and orientation. + * + * @var string[] + */ + protected const SORT_BY = [ + 'date' => 'DESC', + 'subject' => 'ASC', + 'authorName' => 'ASC', + 'authorEmail' => 'ASC', + 'authorDate' => 'DESC', + 'committerName' => 'ASC', + 'committerEmail' => 'ASC', + 'committerDate' => 'DESC', + ]; + + /** + * Commit sorting. + * + * @var string + */ + protected $sortBy = 'subject'; + + /** + * Pre run. + * + * @var mixed + */ + protected $preRun; + + /** + * Post run. + * + * @var mixed + */ + protected $postRun; + + /** + * Constructor. + */ + public function __construct(array $settings = []) + { + $this->setRoot(); + $this->setTypes($this->preset); + $this->fromArray($settings); + } + + /** + * From array. + */ + public function fromArray(array $array) + { + $defaults = [ + 'root' => null, + 'headerTitle' => $this->getHeaderTitle(), + 'headerDescription' => $this->getHeaderDescription(), + 'sortBy' => $this->getSortBy(), + 'path' => $this->getPath(), + 'preset' => $this->getPreset(), + 'types' => [], + 'packageBump' => $this->isPackageBump(), + 'packageBumps' => [], + 'packageLockCommit' => $this->isPackageLockCommit(), + 'ignoreTypes' => $this->getIgnoreTypes(), + 'ignorePatterns' => $this->getIgnorePatterns(), + 'tagPrefix' => $this->getTagPrefix(), + 'tagSuffix' => $this->getTagSuffix(), + 'skipBump' => $this->skipBump(), + 'skipTag' => $this->skipTag(), + 'skipVerify' => $this->skipVerify(), + 'disableLinks' => $this->isDisableLinks(), + 'hiddenHash' => $this->isHiddenHash(), + 'hiddenMentions' => $this->isHiddenMentions(), + 'hiddenReferences' => $this->isHiddenReferences(), + 'prettyScope' => $this->isPrettyScope(), + 'urlProtocol' => $this->getUrlProtocol(), + 'dateFormat' => $this->getDateFormat(), + 'changelogVersionFormat' => $this->getChangelogVersionFormat(), + 'commitUrlFormat' => $this->getCommitUrlFormat(), + 'compareUrlFormat' => $this->getCompareUrlFormat(), + 'issueUrlFormat' => $this->getIssueUrlFormat(), + 'userUrlFormat' => $this->getUserUrlFormat(), + 'hiddenVersionSeparator' => $this->isHiddenVersionSeparator(), + 'releaseCommitMessageFormat' => $this->getReleaseCommitMessageFormat(), + 'preRun' => $this->getPreRun(), + 'postRun' => $this->getPostRun(), + ]; + + $params = array_replace_recursive($defaults, $array); + + // Ignore Types + if (isset($array['ignoreTypes'])) { + $params['ignoreTypes'] = $array['ignoreTypes']; // Overwrite ignored types + } + + // Set Types (overwrite ignored types) + if (!empty($array['types'])) { + foreach ($this->preset as $type => $value) { + if (!in_array($type, $array['types'])) { + if (isset($params['preset'][$type])) { + unset($params['preset'][$type]); + } + } else { + $params['preset'][$type] = $value; + } + } + } + + // Add breaking changes + $breakingPreset = $this->getBreakingChangesPreset(); + $params['preset'] = array_merge($breakingPreset, $params['preset']); + + $this + // Paths + ->setRoot($params['root']) + ->setPath($params['path']) + // Types + ->setIgnorePatterns($params['ignorePatterns']) + ->setIgnoreTypes($params['ignoreTypes']) + ->setTypes($params['preset']) + // Package + ->setPackageBump($params['packageBump']) + ->setPackageBumps($params['packageBumps']) + ->setPackageLockCommit($params['packageLockCommit']) + // Document + ->setHeaderTitle($params['headerTitle']) + ->setHeaderDescription($params['headerDescription']) + ->setSortBy($params['sortBy']) + // Tag + ->setTagPrefix($params['tagPrefix']) + ->setTagSuffix($params['tagSuffix']) + // Skips + ->setSkipBump($params['skipBump']) + ->setSkipTag($params['skipTag']) + ->setSkipVerify($params['skipVerify']) + // Links + ->setDisableLinks($params['disableLinks']) + // Hidden + ->setHiddenHash($params['hiddenHash']) + ->setHiddenMentions($params['hiddenMentions']) + ->setHiddenReferences($params['hiddenReferences']) + ->setHiddenVersionSeparator($params['hiddenVersionSeparator']) + // Formats + ->setPrettyScope($params['prettyScope']) + ->setUrlProtocol($params['urlProtocol']) + ->setDateFormat($params['dateFormat']) + ->setCommitUrlFormat($params['commitUrlFormat']) + ->setCompareUrlFormat($params['compareUrlFormat']) + ->setIssueUrlFormat($params['issueUrlFormat']) + ->setUserUrlFormat($params['userUrlFormat']) + ->setReleaseCommitMessageFormat($params['releaseCommitMessageFormat']) + ->setDisableLinks($params['disableLinks']) + ->setChangelogVersionFormat($params['changelogVersionFormat']) + // Hooks + ->setPreRun($params['preRun']) + ->setPostRun($params['postRun']); + } + + /** + * @return string[] + */ + public function getTypes(): array + { + return array_keys($this->types); + } + + /** + * @return string[] + */ + public function getAllowedTypes(): array + { + $types = $this->types; + unset($types['breaking_changes']); + + return array_keys($types); + } + + public function getTypeLabel(string $type): string + { + return $this->types[$type]['label']; + } + + public function getTypeDescription(string $type): string + { + return $this->types[$type]['description'] ?? ''; + } + + /** + * @param string[][] $types + */ + public function setTypes(array $types): self + { + $ignoreTypes = $this->getIgnoreTypes(); + foreach ($ignoreTypes as $type) { + unset($types[$type]); // Unset excluded types + } + + $this->types = $types; + + return $this; + } + + public function getPath(): string + { + return $this->path; + } + + public function setPath(string $path): self + { + $this->path = $path; + + return $this; + } + + public function getHeaderTitle(): string + { + return $this->headerTitle; + } + + public function setHeaderTitle(string $headerTitle): self + { + $this->headerTitle = $headerTitle; + + return $this; + } + + public function getHeaderDescription(): string + { + return $this->headerDescription; + } + + public function setHeaderDescription(string $headerDescription): self + { + $this->headerDescription = $headerDescription; + + return $this; + } + + /** + * @return string[] + */ + public function getIgnorePatterns(): array + { + return $this->ignorePatterns; + } + + /** + * @param string[] $ignorePatterns + */ + public function setIgnorePatterns(array $ignorePatterns): self + { + foreach ($ignorePatterns as $key => $pattern) { + if (!$this->isRegex($pattern)) { + $ignorePatterns[$key] = '#' . preg_quote($pattern, '#') . '#i'; + } + } + $this->ignorePatterns = $ignorePatterns; + + return $this; + } + + /** + * @return string[][] + */ + public function getPreset(): array + { + return array_merge($this->getBreakingChangesPreset(), $this->preset); + } + + /** + * @param string[] $ignoreTypes + */ + public function setIgnoreTypes(array $ignoreTypes): self + { + $types = $this->getTypes(); + foreach ($ignoreTypes as $type) { + unset($types[$type]); // Unset excluded types + } + + $this->ignoreTypes = $ignoreTypes; + $this->types = $types; + + return $this; + } + + /** + * @return string[] + */ + public function getIgnoreTypes(): array + { + return $this->ignoreTypes; + } + + public function getRoot(): string + { + return $this->root; + } + + /** + * @param string $root + */ + public function setRoot(?string $root = null): self + { + if (empty($root) || !is_dir($root)) { + $root = getcwd(); + } + + $this->root = $root; + + return $this; + } + + public function getTagPrefix(): string + { + return $this->tagPrefix; + } + + public function setTagPrefix(string $tagPrefix): self + { + $this->tagPrefix = $tagPrefix; + + return $this; + } + + public function getTagSuffix(): string + { + return $this->tagSuffix; + } + + public function setTagSuffix(string $tagSuffix): self + { + $this->tagSuffix = $tagSuffix; + + return $this; + } + + public function skipTag(): bool + { + return $this->skipTag; + } + + public function setSkipTag(bool $skipTag): self + { + $this->skipTag = $skipTag; + + return $this; + } + + public function skipBump(): bool + { + return $this->skipBump; + } + + public function setSkipBump(bool $skipBump): self + { + $this->skipBump = $skipBump; + + return $this; + } + + public function skipVerify(): bool + { + return $this->skipVerify; + } + + public function setSkipVerify(bool $skipVerify): self + { + $this->skipVerify = $skipVerify; + + return $this; + } + + /** + * @return \string[][] + */ + public function getBreakingChangesPreset(): array + { + return [self::BREAKING_CHANGES_TYPE => $this->breakingChangesPreset]; + } + + public function getCommitUrlFormat(): string + { + return $this->commitUrlFormat; + } + + public function setCommitUrlFormat(string $commitUrlFormat): self + { + $this->commitUrlFormat = $commitUrlFormat; + + return $this; + } + + public function getCompareUrlFormat(): string + { + return $this->compareUrlFormat; + } + + public function setCompareUrlFormat(string $compareUrlFormat): self + { + $this->compareUrlFormat = $compareUrlFormat; + + return $this; + } + + public function getIssueUrlFormat(): string + { + return $this->issueUrlFormat; + } + + public function setIssueUrlFormat(string $issueUrlFormat): self + { + $this->issueUrlFormat = $issueUrlFormat; + + return $this; + } + + public function getUserUrlFormat(): string + { + return $this->userUrlFormat; + } + + public function setUserUrlFormat(string $userUrlFormat): self + { + $this->userUrlFormat = $userUrlFormat; + + return $this; + } + + public function getReleaseCommitMessageFormat(): string + { + return $this->releaseCommitMessageFormat; + } + + public function setReleaseCommitMessageFormat(string $releaseCommitMessageFormat): self + { + $this->releaseCommitMessageFormat = $releaseCommitMessageFormat; + + return $this; + } + + public function getUrlProtocol(): string + { + return $this->urlProtocol; + } + + public function setUrlProtocol(string $urlProtocol): self + { + $this->urlProtocol = $urlProtocol; + + return $this; + } + + public function getDateFormat(): string + { + return $this->dateFormat; + } + + public function setDateFormat(string $dateFormat): self + { + $this->dateFormat = $dateFormat; + + return $this; + } + + public function isDisableLinks(): bool + { + return $this->disableLinks; + } + + public function setDisableLinks(bool $disableLinks): self + { + $this->disableLinks = $disableLinks; + + return $this; + } + + public function getChangelogVersionFormat(): string + { + return $this->changelogVersionFormat; + } + + public function setChangelogVersionFormat($changelogVersionFormat): self + { + $this->changelogVersionFormat = $changelogVersionFormat; + + return $this; + } + + public function isHiddenReferences(): bool + { + return $this->hiddenReferences; + } + + public function setHiddenReferences(bool $hiddenReferences): self + { + $this->hiddenReferences = $hiddenReferences; + + return $this; + } + + public function isHiddenMentions(): bool + { + return $this->hiddenMentions; + } + + public function setHiddenMentions(bool $hiddenMentions): self + { + $this->hiddenMentions = $hiddenMentions; + + return $this; + } + + public function isHiddenHash(): bool + { + return $this->hiddenHash; + } + + public function setHiddenHash(bool $hiddenHash): self + { + $this->hiddenHash = $hiddenHash; + + return $this; + } + + public function isPrettyScope(): bool + { + return $this->prettyScope; + } + + public function setPrettyScope(bool $prettyScope): self + { + $this->prettyScope = $prettyScope; + + return $this; + } + + public function getSortBy(): string + { + $sortBy = $this->sortBy; + if ($sortBy === 'date') { + $sortBy = 'committerDate'; + } + + return $sortBy; + } + + public function setSortBy(string $sortBy): self + { + $sortBy = trim($sortBy); + if (!array_key_exists($sortBy, self::SORT_BY)) { + $sortBy = 'date'; + } + $this->sortBy = $sortBy; + + return $this; + } + + public function getSortOrientation(string $sort): string + { + return self::SORT_BY[$sort]; + } + + /** + * @param mixed $hook + */ + public function runHook($hook) + { + if (is_string($hook)) { + system($hook); + } elseif (is_callable($hook)) { + call_user_func($hook); + } + } + + /** + * @return mixed + */ + public function getPreRun() + { + return $this->preRun; + } + + public function preRun() + { + $this->runHook($this->preRun); + } + + /** + * @param mixed $preRun + */ + public function setPreRun($preRun): self + { + $this->preRun = $preRun; + + return $this; + } + + /** + * @return mixed + */ + public function getPostRun() + { + return $this->postRun; + } + + public function postRun() + { + $this->runHook($this->postRun); + } + + /** + * @param mixed $postRun + */ + public function setPostRun($postRun): self + { + $this->postRun = $postRun; + + return $this; + } + + public function isPackageBump(): bool + { + return $this->packageBump; + } + + public function setPackageBump(bool $packageBump): Configuration + { + $this->packageBump = $packageBump; + + return $this; + } + + public function setPackageBumps(array $packageBumps): Configuration + { + $this->packageBumps = $packageBumps; + + return $this; + } + + public function getPackageBumps(): array + { + return $this->packageBumps; + } + + public function isPackageLockCommit(): bool + { + return $this->packageLockCommit; + } + + public function setPackageLockCommit(bool $packageLockCommit): Configuration + { + $this->packageLockCommit = $packageLockCommit; + + return $this; + } + + public function isHiddenVersionSeparator(): bool + { + return $this->hiddenVersionSeparator; + } + + public function setHiddenVersionSeparator($hiddenVersionSeparator): Configuration + { + $this->hiddenVersionSeparator = $hiddenVersionSeparator; + + return $this; + } + + /** + * Validate settings. + * + * @param mixed $settings + */ + public static function validate($settings): bool + { + return is_array($settings); + } + + /** + * Check if is regex. + * + * @return bool + */ + protected function isRegex(string $pattern) + { + return @preg_match($pattern, '') !== false; + } +} diff --git a/vendor/marcocesarato/php-conventional-changelog/src/DefaultCommand.php b/vendor/marcocesarato/php-conventional-changelog/src/DefaultCommand.php new file mode 100644 index 00000000..269f5003 --- /dev/null +++ b/vendor/marcocesarato/php-conventional-changelog/src/DefaultCommand.php @@ -0,0 +1,210 @@ +settings = $settings; + } + + /** + * Configure. + * + * @return void + */ + protected function configure() + { + $this + ->setDescription("Generate changelogs and release notes from a project's commit messages" . + 'and metadata and automate versioning with semver.org and conventionalcommits.org') + ->setDefinition([ + new InputArgument('path', InputArgument::OPTIONAL, 'Specify the path directory where generate changelog'), + new InputOption('config', null, InputOption::VALUE_REQUIRED, 'Specify the configuration file path'), + new InputOption('commit', 'c', InputOption::VALUE_NONE, 'Commit the new release once changelog is generated'), + new InputOption('amend', 'a', InputOption::VALUE_NONE, 'Amend commit the new release once changelog is generated'), + new InputOption('commit-all', null, InputOption::VALUE_NONE, 'Commit all changes the new release once changelog is generated'), + new InputOption('first-release', null, InputOption::VALUE_NONE, "Run at first release (if --ver isn't specified version code it will be 1.0.0)"), + new InputOption('from-date', null, InputOption::VALUE_REQUIRED, 'Get commits from specified date [YYYY-MM-DD]'), + new InputOption('to-date', null, InputOption::VALUE_REQUIRED, 'Get commits last tag date (or specified on --from-date) to specified date [YYYY-MM-DD]'), + new InputOption('from-tag', null, InputOption::VALUE_REQUIRED, 'Get commits from specified tag'), + new InputOption('to-tag', null, InputOption::VALUE_REQUIRED, 'Get commits last tag (or specified on --from-tag) to specified tag'), + new InputOption('major', null, InputOption::VALUE_NONE, 'Major release (important changes)'), + new InputOption('minor', null, InputOption::VALUE_NONE, 'Minor release (add functionality)'), + new InputOption('patch', null, InputOption::VALUE_NONE, 'Patch release (bug fixes) [default]'), + new InputOption('rc', null, InputOption::VALUE_NONE, 'Release candidate'), + new InputOption('beta', null, InputOption::VALUE_NONE, 'Beta release'), + new InputOption('alpha', null, InputOption::VALUE_NONE, 'Alpha release'), + new InputOption('ver', null, InputOption::VALUE_REQUIRED, 'Specify the next release version code (semver)'), + new InputOption('history', null, InputOption::VALUE_NONE, 'Generate the entire history of changes of all releases'), + new InputOption('no-verify', null, InputOption::VALUE_NONE, 'Bypasses the pre-commit and commit-msg hooks'), + new InputOption('no-tag', null, InputOption::VALUE_NONE, 'Disable release auto tagging'), + new InputOption('no-change-without-commits', null, InputOption::VALUE_NONE, 'Do not apply change if no commits'), + new InputOption('annotate-tag', null, InputOption::VALUE_OPTIONAL, 'Make an unsigned, annotated tag object once changelog is generated', false), + new InputOption('merged', null, InputOption::VALUE_NONE, 'Only include commits whose tips are reachable from HEAD'), + ]); + } + + /** + * Execute. + * + * @return int + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + /** + * Initialize. + */ + $this->output = new SymfonyStyle($input, $output); + if ($this->getApplication() !== null) { + $appName = $this->getApplication()->getName(); + $appVersion = $this->getApplication()->getVersion(); + $this->output->title($appName . ' ' . $appVersion); + } + + /** + * Retrieve configuration settings. + */ + $config = $input->getOption('config'); + if (!empty($config) && is_file($config)) { + $this->settings = require $config; + } + if (!Configuration::validate($this->settings)) { + $this->output->error('Not a valid configuration! Using default settings.'); + $this->settings = []; + } + $this->config = new Configuration($this->settings); + // Get root path + $root = $input->getArgument('path'); + if (empty($root) || !is_dir($root)) { + $root = $this->config->getRoot(); + } + // Set working directory + chdir($root); + + /** + * Check environment. + */ + $this->output->writeln('Checking environment requirements'); + // Check shell exec function + if (!ShellCommand::isEnabled()) { + $this->output->error( + 'It looks like shell_exec function is disabled on disable_functions config of your php.ini settings file. ' . + 'Please check you configuration and enabled shell_exec to proceed.' + ); + + return 1; // Command::FAILURE; + } + $this->validRequirement('Commands executions enabled'); + // Check git command + if (!ShellCommand::exists('git')) { + $this->output->error( + 'It looks like Git is not installed on your system. ' . + 'Please check how to install it from https://git-scm.com before run this command.' + ); + + return 1; // Command::FAILURE; + } + $this->validRequirement('Git detected correctly'); + // Check git version + $gitVersion = ShellCommand::exec('git --version'); + $gitSemver = new SemanticVersion($gitVersion); + $gitVersionCode = $gitSemver->getVersionCode(); + if (version_compare($gitVersionCode, '2.1.4', '<')) { + $this->output->error( + 'It looks like your Git version is ' . $gitVersionCode . ' that isn\'t compatible with this tool (min required is 2.1.4). ' . + 'Please check how to update it from https://git-scm.com before run this command.' + ); + + return 1; // Command::FAILURE; + } + $this->validRequirement('Git version detected: ' . $gitVersionCode); + // Check working directory + if (!Repository::isInsideWorkTree()) { + $this->output->error('The directory "' . $root . '" isn\'t a valid git repository or isn\'t been detected correctly.'); + + return 1; // Command::FAILURE; + } + $this->validRequirement('Valid git worktree directory: ' . $root); + // Check git repository + if (Repository::hasRemoteUrl()) { + $url = Repository::getRemoteUrl(); + if (empty(Repository::parseRemoteUrl())) { + $this->output->warning( + 'The remote url of your git repository ("' . $url . '") not been parsed correctly. ' . + 'Please open and issue including your repository url format to make it compatible with this tool here ' . + 'https://github.com/marcocesarato/php-conventional-changelog/issues' + ); + } else { + $this->validRequirement('Valid git remote repository url: ' . $url); + } + } else { + $this->output->warning( + 'Remote repository url not detected. ' . + 'It looks like your project is running only locally and not on a remote repository. ' . + 'Care, all urls on the changelog will be empty.' + ); + } + $this->output->newLine(); + + /** + * Generate changelog. + */ + $this->output->writeln('Generating changelog'); + $this->changelog = new Changelog($this->config); + + return $this->changelog->generate($root, $input, $this->output); + } + + /** + * Print with valid requirement format. + */ + protected function validRequirement(string $messages) + { + $this->output->writeln(' ✓ ' . $messages); + } +} diff --git a/vendor/marcocesarato/php-conventional-changelog/src/Git/Commit.php b/vendor/marcocesarato/php-conventional-changelog/src/Git/Commit.php new file mode 100644 index 00000000..220a18db --- /dev/null +++ b/vendor/marcocesarato/php-conventional-changelog/src/Git/Commit.php @@ -0,0 +1,399 @@ +^([a-z0-9_-]+|BREAKING[[:blank:]]CHANGES?))(?([:][[:blank:]]|[:]?[[:blank:]][#](?=\w)).*?)$/iums"; + /** + * @var string + */ + protected const PATTERN_MENTION = "/(?:^|\s+)(?@(?[a-z\d](?:[a-z\d]|-(?=[a-z\d])){0,38}))(?:$|\s+)/smi"; + + /** + * Raw content. + * + * @var string + */ + public $raw; + + /** + * Subject content. + * + * @var Subject + */ + public $subject; + + /** + * Body content. + * + * @var Body + */ + public $body; + + /** + * Footers. + * + * @var Footer[] + */ + public $footers = []; + + /** + * User Mentions. + * + * @var Mention[] + */ + public $mentions = []; + + /** + * Sha hash. + * + * @var string + */ + public $hash; + + /** + * @var \DateTime + */ + public $authorDate; + + /** + * @var string + */ + public $authorName; + + /** + * @var string + */ + public $authorEmail; + + /** + * @var \DateTime + */ + public $committerDate; + + /** + * @var string + */ + public $committerName; + + /** + * @var string + */ + public $committerEmail; + + public function __construct(string $commit = null) + { + // New commit or empty commit + if (empty($commit)) { + $this->setSubject('') + ->setBody(''); + + return; + } + + $raw = Formatter::clean($commit); + $this->setRaw($raw); + $this->parse(); + } + + public function __wakeup() + { + $this->parse(); + } + + public function __toString(): string + { + if (!empty($this->raw)) { + return $this->raw; + } + + $header = $this->getSubject(); + $message = $this->getMessage(); + $string = $header . "\n\n" . $message; + + return Formatter::clean($string); + } + + /** + * From array. + * + * @throws \Exception + */ + public function fromArray(array $array): self + { + if (isset($array['raw'])) { + $this->setRaw($array['raw']); + } + if (isset($array['hash'])) { + $this->setHash($array['hash']); + } + if (isset($array['authorName'])) { + $this->setAuthorName($array['authorName']); + } + if (isset($array['authorEmail'])) { + $this->setAuthorEmail($array['authorEmail']); + } + if (isset($array['authorDate'])) { + $date = new \DateTime($array['authorDate']); + $this->setAuthorDate($date); + } + if (isset($array['committerName'])) { + $this->setCommitterName($array['committerName']); + } + if (isset($array['committerEmail'])) { + $this->setCommitterEmail($array['committerEmail']); + } + if (isset($array['committerDate'])) { + $date = new \DateTime($array['committerDate']); + $this->setCommitterDate($date); + } + $this->parse(); + + return $this; + } + + public function setRaw(string $raw): self + { + $this->raw = $raw; + + return $this; + } + + public function setHash(string $hash): self + { + if ($this->isValidHash($hash)) { + $this->hash = $hash; + } + + return $this; + } + + public function getRaw(): ?string + { + return $this->raw; + } + + public function getHash(): ?string + { + return $this->hash; + } + + public function getShortHash(): ?string + { + return substr($this->hash, 0, 6); + } + + public function getAuthorDate(): \DateTime + { + return $this->authorDate; + } + + /** + * @param \DateTime|\DateTimeImmutable $authorDate + */ + public function setAuthorDate(\DateTimeInterface $authorDate): Commit + { + $this->authorDate = $authorDate; + + return $this; + } + + public function getAuthorName(): ?string + { + return $this->authorName; + } + + public function setAuthorName(string $authorName): Commit + { + $this->authorName = $authorName; + + return $this; + } + + public function getAuthorEmail(): ?string + { + return $this->authorEmail; + } + + public function setAuthorEmail(string $authorEmail): Commit + { + $this->authorEmail = $authorEmail; + + return $this; + } + + public function getCommitterDate(): \DateTime + { + return $this->committerDate; + } + + /** + * @param \DateTime|\DateTimeImmutable $committerDate + */ + public function setCommitterDate(\DateTimeInterface $committerDate): Commit + { + $this->committerDate = $committerDate; + + return $this; + } + + public function getCommitterName(): ?string + { + return $this->committerName; + } + + public function setCommitterName(string $committerName): Commit + { + $this->committerName = $committerName; + + return $this; + } + + public function getCommitterEmail(): ?string + { + return $this->committerEmail; + } + + public function setCommitterEmail(string $committerEmail): Commit + { + $this->committerEmail = $committerEmail; + + return $this; + } + + public function getBody(): Body + { + return $this->body; + } + + public function setBody(string $body): self + { + $body = Formatter::clean($body); + $footers = []; + if (preg_match_all(self::PATTERN_FOOTER, $body, $matches, PREG_SET_ORDER, 0)) { + foreach ($matches as $match) { + $footer = $match[0]; + $body = str_replace($footer, '', $body); + $value = ltrim((string)$match['value'], ':'); + $value = Formatter::clean($value); + $footers[] = new Footer((string)$match['token'], $value); + } + } + $this->setFooters($footers); + + $body = Formatter::clean($body); + $this->body = new Body($body); + + return $this; + } + + public function getSubject(): Subject + { + return $this->subject; + } + + public function setSubject(string $subject): self + { + $this->subject = new Subject($subject); + + return $this; + } + + /** + * @param Footer[] $footers + */ + public function setFooters(array $footers): self + { + $this->footers = $footers; + + return $this; + } + + /** + * @return Footer[] + */ + public function getFooters(): array + { + return $this->footers; + } + + /** + * Set mentions. + * + * @param Mention[] $mentions + */ + public function setMentions(array $mentions): self + { + $this->mentions = array_unique($mentions); + + return $this; + } + + /** + * Get mentions. + * + * @return Mention[] + */ + public function getMentions(): array + { + return $this->mentions; + } + + public function getMessage(): string + { + $footer = implode("\n", $this->footers); + + return $this->body . "\n\n" . $footer; + } + + /** + * Check if is valid SHA-1. + */ + protected function isValidHash(string $hash): bool + { + return (bool)preg_match('/^[0-9a-f]{40}$/i', $hash); + } + + /** + * Parse raw commit. + */ + protected function parse() + { + // Empty + if (empty($this->raw)) { + return; + } + + $rows = explode("\n", $this->raw); + + $subject = $rows[0]; + $body = ''; + foreach ($rows as $i => $row) { + if ($i !== 0) { + $body .= $row . "\n"; + } + } + + $mentions = []; + if (preg_match_all(self::PATTERN_MENTION, $this->raw, $matches)) { + foreach ($matches['user'] as $match) { + $mentions[] = new Mention($match); + } + } + + $this->setSubject($subject) + ->setBody($body) + ->setMentions($mentions); + } +} diff --git a/vendor/marcocesarato/php-conventional-changelog/src/Git/Commit/Body.php b/vendor/marcocesarato/php-conventional-changelog/src/Git/Commit/Body.php new file mode 100644 index 00000000..3662e0a4 --- /dev/null +++ b/vendor/marcocesarato/php-conventional-changelog/src/Git/Commit/Body.php @@ -0,0 +1,23 @@ +content = (string)$content; + } + + public function __toString(): string + { + return ucfirst($this->content); + } +} diff --git a/vendor/marcocesarato/php-conventional-changelog/src/Git/Commit/Description.php b/vendor/marcocesarato/php-conventional-changelog/src/Git/Commit/Description.php new file mode 100644 index 00000000..827f2613 --- /dev/null +++ b/vendor/marcocesarato/php-conventional-changelog/src/Git/Commit/Description.php @@ -0,0 +1,23 @@ +content = $content; + } + + public function __toString(): string + { + return $this->content; + } +} diff --git a/vendor/marcocesarato/php-conventional-changelog/src/Git/Commit/Footer.php b/vendor/marcocesarato/php-conventional-changelog/src/Git/Commit/Footer.php new file mode 100644 index 00000000..45d2a05c --- /dev/null +++ b/vendor/marcocesarato/php-conventional-changelog/src/Git/Commit/Footer.php @@ -0,0 +1,103 @@ +token = trim($token); + $this->value = trim($value); + + $refs = []; + $tokenLower = strtolower($this->token); + $values = preg_split('/[,\s]+/', $this->value, -1, PREG_SPLIT_NO_EMPTY); + foreach ($values as $val) { + if (isset($val[0]) && $val[0] === '#') { + $ref = ltrim($val, '#'); + $obj = new Reference($ref); + if (in_array($tokenLower, self::TOKEN_CLOSE_ISSUE)) { + $obj->setClosed(true); + } + $refs[] = $obj; + } + } + + $this->setReferences($refs); + } + + public function __toString(): string + { + return $this->token . ': ' . $this->value; + } + + public function getToken(): string + { + return strtolower($this->token); + } + + public function getValue(): string + { + return ucfirst($this->value); + } + + /** + * Set issues references. + * + * @return Reference[] + */ + public function setReferences(array $references): self + { + $this->references = array_unique($references); + + return $this; + } + + /** + * Get issues references. + * + * @return Reference[] + */ + public function getReferences(): array + { + return $this->references; + } +} diff --git a/vendor/marcocesarato/php-conventional-changelog/src/Git/Commit/Mention.php b/vendor/marcocesarato/php-conventional-changelog/src/Git/Commit/Mention.php new file mode 100644 index 00000000..a15a5823 --- /dev/null +++ b/vendor/marcocesarato/php-conventional-changelog/src/Git/Commit/Mention.php @@ -0,0 +1,28 @@ +user = $user; + } + + public function __toString(): string + { + return '@' . $this->user; + } + + public function getUser(): string + { + return $this->user; + } +} diff --git a/vendor/marcocesarato/php-conventional-changelog/src/Git/Commit/Reference.php b/vendor/marcocesarato/php-conventional-changelog/src/Git/Commit/Reference.php new file mode 100644 index 00000000..88c6b998 --- /dev/null +++ b/vendor/marcocesarato/php-conventional-changelog/src/Git/Commit/Reference.php @@ -0,0 +1,45 @@ +id = $id; + } + + public function __toString(): string + { + return '#' . $this->id; + } + + public function getId(): string + { + return $this->id; + } + + public function isClosed(): bool + { + return $this->closed; + } + + public function setClosed(bool $closed): self + { + $this->closed = $closed; + + return $this; + } +} diff --git a/vendor/marcocesarato/php-conventional-changelog/src/Git/Commit/Scope.php b/vendor/marcocesarato/php-conventional-changelog/src/Git/Commit/Scope.php new file mode 100644 index 00000000..605a9de1 --- /dev/null +++ b/vendor/marcocesarato/php-conventional-changelog/src/Git/Commit/Scope.php @@ -0,0 +1,37 @@ +content = (string)$content; + } + + public function __toString(): string + { + return $this->content; + } + + /** + * Prettify. + */ + public function toPrettyString(): string + { + $string = ucfirst($this->content); + $string = preg_replace('/[_]+/m', ' ', $string); + $string = preg_replace('/((?<=\p{Ll})\p{Lu})|((?!\A)\p{Lu}(?>\p{Ll}))/u', ' $0', $string); + $string = preg_replace('/\.(php|md|json|txt|csv|js)($|\s)/', '', $string); + + return Formatter::clean($string); + } +} diff --git a/vendor/marcocesarato/php-conventional-changelog/src/Git/Commit/Subject.php b/vendor/marcocesarato/php-conventional-changelog/src/Git/Commit/Subject.php new file mode 100644 index 00000000..a6b4e804 --- /dev/null +++ b/vendor/marcocesarato/php-conventional-changelog/src/Git/Commit/Subject.php @@ -0,0 +1,23 @@ +content = (string)$content; + } + + public function __toString(): string + { + return $this->content; + } +} diff --git a/vendor/marcocesarato/php-conventional-changelog/src/Git/Commit/Type.php b/vendor/marcocesarato/php-conventional-changelog/src/Git/Commit/Type.php new file mode 100644 index 00000000..018fa0e4 --- /dev/null +++ b/vendor/marcocesarato/php-conventional-changelog/src/Git/Commit/Type.php @@ -0,0 +1,23 @@ +content = strtolower($content); + } + + public function __toString(): string + { + return $this->content; + } +} diff --git a/vendor/marcocesarato/php-conventional-changelog/src/Git/ConventionalCommit.php b/vendor/marcocesarato/php-conventional-changelog/src/Git/ConventionalCommit.php new file mode 100644 index 00000000..93f424a0 --- /dev/null +++ b/vendor/marcocesarato/php-conventional-changelog/src/Git/ConventionalCommit.php @@ -0,0 +1,222 @@ +[a-z]+)(?[!]?)(\((?.+)\))?(?[!]?)[:][[:blank:]](?.+)/iums"; + + /** + * Type. + * + * @var Type + */ + public $type; + + /** + * Scope. + * + * @var Scope + */ + public $scope; + + /** + * Is breaking change. + * + * @var bool + */ + public $isBreakingChange = false; + + /** + * Description. + * + * @var Description + */ + public $description; + + public function __construct(?string $commit = null) + { + parent::__construct($commit); + $this->parse(); + } + + public function __toString(): string + { + if (!empty($this->raw)) { + return $this->raw; + } + + $header = $this->getHeader(); + $message = $this->getMessage(); + $string = $header . "\n\n" . $message; + + return Formatter::clean($string); + } + + /** + * Check if is valid conventional commit. + */ + public function isValid(): bool + { + return (bool)preg_match(self::PATTERN_HEADER, $this->raw); + } + + public function getType(): Type + { + $type = $this->type; + if ($this->isBreakingChange()) { + $type = new Type(Configuration::BREAKING_CHANGES_TYPE); + } + + return $type; + } + + public function getScope(): Scope + { + return $this->scope; + } + + public function hasScope(): bool + { + return !empty((string)$this->scope); + } + + public function isBreakingChange(): bool + { + return $this->isBreakingChange; + } + + public function getDescription(): Description + { + return $this->description; + } + + public function getBreakingChanges(): array + { + $messages = []; + foreach ($this->footers as $footer) { + if (preg_match('/^breaking[[:blank:]]changes?$/', $footer->getToken())) { + $messages[] = $footer->getValue(); + } + } + + return $messages; + } + + /** + * Get issues references. + * + * @return Reference[] + */ + public function getReferences(): array + { + $refs = []; + foreach ($this->footers as $footer) { + $refs = array_merge($refs, $footer->getReferences()); + } + + return array_unique($refs); + } + + public function getHeader(): string + { + $header = $this->type; + if ($this->hasScope()) { + $header .= '(' . $this->scope . ')'; + } + if ($this->isBreakingChange) { + $header .= '!'; + } + + return $header . (': ' . $this->description); + } + + public function setType(string $type): self + { + $this->type = new Type($type); + + return $this; + } + + public function setScope(?string $scope): self + { + $this->scope = new Scope($scope); + + return $this; + } + + public function setBreakingChange(bool $isBreakingChange): self + { + $this->isBreakingChange = $isBreakingChange; + + return $this; + } + + public function setDescription(string $description): self + { + $this->description = new Description($description); + + return $this; + } + + /** + * From commit. + */ + public static function fromCommit(Commit $commit): self + { + return unserialize( + preg_replace('/^O:\d+:"[^"]++"/', 'O:' . strlen(self::class) . ':"' . self::class . '"', serialize($commit)), + [self::class] + ); + } + + /** + * Parse raw commit. + */ + protected function parse() + { + parent::parse(); + + // Empty + if (empty($this->raw)) { + return; + } + + // Not parsable + if (!$this->isValid()) { + return; + } + $header = $this->getSubject(); + + $this->parseHeader($header); + } + + /** + * Parse header. + */ + protected function parseHeader(string $header) + { + preg_match(self::PATTERN_HEADER, $header, $matches); + + $type = $matches['type'] ?? ''; + $scope = $matches['scope'] ?? ''; + $breakingBefore = $matches['breaking_before'] ?? ''; + $breakingAfter = $matches['breaking_after'] ?? ''; + $description = $matches['description'] ?? ''; + + $this->setType($type) + ->setScope($scope) + ->setBreakingChange(!empty($breakingBefore || !empty($breakingAfter))) + ->setDescription($description); + } +} diff --git a/vendor/marcocesarato/php-conventional-changelog/src/Git/Repository.php b/vendor/marcocesarato/php-conventional-changelog/src/Git/Repository.php new file mode 100644 index 00000000..847aece7 --- /dev/null +++ b/vendor/marcocesarato/php-conventional-changelog/src/Git/Repository.php @@ -0,0 +1,363 @@ + 0) { + foreach ($tagsFound as $found) { + if (SemanticVersion::validate($found, $prefix)) { + $lastBaseTag = $found; + break; + } + } + } + + if (!empty($extra)) { + $extraQuote = preg_quote("-{$extra}"); + $tagsFound = preg_grep('/^' . $prefixQuote . '[^-]*' . $extraQuote . '/', $tagsArray); + } + + $lastTag = '0.0.0'; + if (count($tagsFound) > 0) { + foreach ($tagsFound as $found) { + if (SemanticVersion::validate($found, $prefix)) { + $lastTag = $found; + break; + } + } + } + + if (SemanticVersion::compareBase($lastBaseTag, $lastTag) > 0) { + return $lastBaseTag; + } + + return $lastTag; + } + + /** + * Get last alpha tag based on a tag pattern. + */ + public static function getLastAlphaTag(string $prefix = '', bool $merged = false): ?string + { + return self::getLastTag($prefix, $merged, 'alpha'); + } + + /** + * Get last release candidate tag based on a tag pattern. + */ + public static function getLastReleaseCandidateTag(string $prefix = '', bool $merged = false): ?string + { + return self::getLastTag($prefix, $merged, 'rc'); + } + + /** + * Get last beta tag based on a tag pattern. + */ + public static function getLastBetaTag(string $prefix = '', bool $merged = false): ?string + { + return self::getLastTag($prefix, $merged, 'beta'); + } + + private static function getTag(string $tag, string $match): ?string + { + $find = self::run(sprintf('git describe --tags --match "*%s*-%s*" --abbrev=0', $tag, $match)); + + return !empty($find) ? $find : null; + } + + /** + * Get last tag commit hash. + */ + public static function getLastTagCommit($prefix = ''): string + { + $lastTag = self::getLastTag($prefix); + + return self::run("git rev-parse --verify {$lastTag}"); + } + + /** + * Get last tag commit hash. + */ + public static function getLastTagRefnameCommit($prefix = ''): string + { + $lastTag = self::getLastTagRefname($prefix); + + return self::run("git rev-parse --verify {$lastTag}"); + } + + /** + * Get current branch name. + */ + public static function getCurrentBranch(): string + { + return self::run('git branch --show-current'); + } + + /** + * Get commit date. + */ + public static function getCommitDate($hash): \DateTime + { + $date = self::run("git log -1 --format=%aI {$hash}"); + + return new \DateTime($date); + } + + /** + * Get remote url. + */ + public static function getRemoteUrl(): string + { + $url = self::run('git config --get remote.origin.url'); + $url = preg_replace("/\.git$/", '', $url); + + return preg_replace('/^(https?:\/\/)([0-9a-z.\-_:%]+@)/i', '$1', $url); + } + + /** + * Has remote url. + */ + public static function hasRemoteUrl(): bool + { + $url = self::getRemoteUrl(); + + return !empty($url); + } + + /** + * Get commits. + */ + public static function getCommits(string $options = ''): array + { + $commits = []; + $shortcodes = [ + 'raw' => '%B', + 'hash' => '%H', + 'authorName' => '%aN', + 'authorEmail' => '%aE', + 'authorDate' => '%aI', + 'committerName' => '%cN', + 'committerEmail' => '%cE', + 'committerDate' => '%cI', + ]; + + $format = ''; + foreach ($shortcodes as $key => $value) { + $format .= "[{$key}]{$value}[/{$key}]"; + } + $format .= self::$delimiter; + $commitsLogs = self::run("git log --pretty=format:'" . $format . "' {$options}") . "\n"; + + $commitsArray = explode(self::$delimiter . "\n", $commitsLogs); + array_pop($commitsArray); + + $shortcodesKeys = array_keys($shortcodes); + foreach ($commitsArray as $commitRaw) { + $parse = self::parseShortcodes($commitRaw, $shortcodesKeys); + $commit = new Commit(); + $commit->fromArray($parse); + $commits[] = $commit; + } + + return $commits; + } + + /** + * Get tags. + */ + public static function getTags($prefix = ''): array + { + $tags = self::run("git tag '" . $prefix . "*' --sort=-v:refname --list --format='%(refname:strip=2)" . self::$delimiter . "'") . "\n"; + $tagsArray = explode(self::$delimiter . "\n", $tags); + array_pop($tagsArray); + + return array_reverse($tagsArray); + } + + /** + * Add all. + * + * @return string + */ + public static function addAll() + { + system('git add --all'); + } + + /** + * Add files. + * + * @return string + */ + public static function add($files) + { + if (!is_array($files)) { + $files = [$files]; + } + foreach ($files as $file) { + system("git add \"{$file}\""); + } + } + + /** + * Commit. + * + * @return string + */ + public static function commit(string $message, array $files = [], bool $amend = false, bool $verify = true, $noEdit = false) + { + self::add($files); + $message = str_replace('"', "'", $message); // Escape + $command = "git commit -m \"{$message}\""; + if ($amend) { + $command .= ' --amend'; + } + if (!$verify) { + $command .= ' --no-verify'; + } + if ($noEdit) { + $command .= ' --no-edit'; + } + + return exec($command); + } + + /** + * Tag. + * + * @return string + */ + public static function tag(string $name, $annotation = false) + { + $message = $annotation ?: $name; + $flags = $annotation !== false ? "-a -m {$message}" : ''; + + return exec("git tag {$flags} {$name}"); + } + + /** + * Delete Tag. + * + * @return string + */ + public static function deleteTag(string $name) + { + return exec("git tag -d {$name}"); + } + + /** + * Parse shortcode. + * + * @return array + */ + protected static function parseShortcodes($content, $shortcodes) + { + $result = []; + foreach ($shortcodes as $key) { + $result[$key] = null; + $key = preg_quote($key, '/'); + $pattern = "/\[[\s]*" . $key . "[\s]*\](.+?)\[[\s]*\/[\s]*" . $key . "[\s]*\]/si"; + preg_match_all($pattern, $content, $match); + if (count($match) > 0 && !empty($match[0]) && isset($match[1][0])) { + $result[$key] = $match[1][0]; + } + } + + return $result; + } + + /** + * Parse remote url. + * + * @return array + */ + public static function parseRemoteUrl() + { + $url = self::getRemoteUrl(); + $patterns = [ + '#^(?Phttps?|git|ssh|rsync)\://(?:(?P.+)@)*(?P[a-z0-9_.-]*)[:/]*(?P[\d]+){0,1}(?P\/((?P.+)\/)?((?P.+?)(\.git|\/)?)?)$#smi', + '#(git\+)?((?P\w+)://)((?P\w+)@)?((?P[\w\.\-]+))(:(?P\d+))?(?P(\/(?P.+)/)?(\/?(?P.+)(\.git|\/)?)?)$#smi', + '#^(?:(?P.+)@)*(?P[a-z0-9_.-]*)[:]*(?P[\d]+){0,1}(?P\/?(?P.+)/(?P.+).git)$#smi', + '#((?P\w+)@)?((?P[\w\.\-]+))[\:\/]{1,2}(?P((?P.+)/)?((?P.+)(\.git|\/)?)?)$#smi', + ]; + foreach ($patterns as $pattern) { + if (preg_match($pattern, $url, $match)) { + return array_filter($match, 'is_string', ARRAY_FILTER_USE_KEY); + } + } + + return []; + } +} diff --git a/vendor/marcocesarato/php-conventional-changelog/src/Helper/Formatter.php b/vendor/marcocesarato/php-conventional-changelog/src/Helper/Formatter.php new file mode 100644 index 00000000..97b08ad2 --- /dev/null +++ b/vendor/marcocesarato/php-conventional-changelog/src/Helper/Formatter.php @@ -0,0 +1,16 @@ +setVersion($version); + } + + /** + * Bump version. + */ + public function bump(string $release, string $extraRelease = ''): string + { + $version = $this->getVersion(); + + $newVersion = [0, 0, 0]; + + if (!self::validate($version)) { + return $version; + } + + // Generate new version code + $split = explode('-', $version, 2); + $extra = empty($split[1]) ? '' : $split[1]; + + $skipBumpRelease = false; + $extraReleases = [self::RC, self::BETA, self::ALPHA]; + + if (in_array($extraRelease, $extraReleases)) { + $partsExtra = explode('.', $extra, 2); + $extraVersion = 0; + if (count($partsExtra) > 1) { + $extraVersion = $partsExtra[1]; + } + if (empty($extraVersion) || !is_numeric($extraVersion)) { + $extraVersion = 0; + } else { + $skipBumpRelease = true; + } + $extraVersion++; + $extra = "{$extraRelease}.{$extraVersion}"; + } + + $parts = explode('.', $split[0]); + foreach ($parts as $key => $value) { + $newVersion[$key] = (int)$value; + } + + if (!$skipBumpRelease) { + if ($release === self::MAJOR) { + $newVersion[0]++; + $newVersion[1] = 0; + $newVersion[2] = 0; + } elseif ($release === self::MINOR) { + $newVersion[1]++; + $newVersion[2] = 0; + } elseif ($release === self::PATCH) { + $newVersion[2]++; + } + } + + // Recompose semver + $version = implode('.', $newVersion) . (empty($extra) ? '' : '-' . $extra); + $this->setVersion($version); + + return $version; + } + + public function getVersion(): string + { + return $this->version; + } + + public function getVersionCode(): string + { + if (preg_match('/' . self::PATTERN_NO_EXTRA . '/', $this->version, $match)) { + return $match[0]; + } + + return '0.0.0'; + } + + public function setVersion(string $version): SemanticVersion + { + $this->version = $version; + + return $this; + } + + public static function validate($version, $prefix = 'v'): bool + { + $version = preg_replace('/^' . preg_quote($prefix, '/') . '/', '', $version); + + return preg_match('/^' . self::PATTERN . '$/', $version); + } + + public static function compareBase($v1, $v2) + { + $v1 = preg_replace('/^.*?(' . self::PATTERN_NO_EXTRA . ').*?$/', '$1', $v1); + $v2 = preg_replace('/^.*?(' . self::PATTERN_NO_EXTRA . ').*?$/', '$1', $v2); + + return version_compare($v1, $v2); + } +} diff --git a/vendor/marcocesarato/php-conventional-changelog/src/Helper/ShellCommand.php b/vendor/marcocesarato/php-conventional-changelog/src/Helper/ShellCommand.php new file mode 100644 index 00000000..75064fea --- /dev/null +++ b/vendor/marcocesarato/php-conventional-changelog/src/Helper/ShellCommand.php @@ -0,0 +1,40 @@ +content->version; + } + + /** + * {@inheritdoc} + */ + public function setVersion(string $version): self + { + $this->content->version = $version; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function save(): self + { + parent::save(); + /* Feature: https://github.com/marcocesarato/php-conventional-changelog/issues/11#issuecomment-819829828 + /* Bug: https://github.com/marcocesarato/php-conventional-changelog/issues/13 + if (ShellCommand::exists('composer')) { + exec('cd ' . escapeshellarg($this->getPath()) . ' && composer update'); + }*/ + + return $this; + } +} diff --git a/vendor/marcocesarato/php-conventional-changelog/src/PackageBump/PackageJson.php b/vendor/marcocesarato/php-conventional-changelog/src/PackageBump/PackageJson.php new file mode 100644 index 00000000..f6e46b31 --- /dev/null +++ b/vendor/marcocesarato/php-conventional-changelog/src/PackageBump/PackageJson.php @@ -0,0 +1,39 @@ +content->version; + } + + /** + * {@inheritdoc} + */ + public function setVersion(string $version): self + { + $this->content->version = $version; + + return $this; + } +} diff --git a/vendor/marcocesarato/php-conventional-changelog/src/Type/PackageBump.php b/vendor/marcocesarato/php-conventional-changelog/src/Type/PackageBump.php new file mode 100644 index 00000000..3b658dc6 --- /dev/null +++ b/vendor/marcocesarato/php-conventional-changelog/src/Type/PackageBump.php @@ -0,0 +1,195 @@ +path = $path; + if ($this->exists()) { + $raw = file_get_contents($this->getFilePath()); + switch ($this->fileType) { + case 'json': + $this->originContent = json_decode($raw); + if ($this->originContent === null && json_last_error() !== JSON_ERROR_NONE) { + throw new \Exception(json_last_error_msg(), json_last_error()); + } + $this->content = clone $this->originContent; + break; + default: + $this->originContent = $raw; + $this->content = $raw; + } + } + } + + /** + * Get version. + */ + public function getVersion(): ?string + { + return null; + } + + /** + * Set version. + * + * @return $this + */ + public function setVersion(string $version) + { + return $this; + } + + /** + * Get root path. + */ + public function getPath(): string + { + if (empty($this->path)) { + $this->path = getcwd(); + } + + return $this->path; + } + + /** + * Get file path. + */ + public function getFilePath(): string + { + $path = $this->getPath() . DIRECTORY_SEPARATOR . $this->fileName; + + return preg_replace('/' . preg_quote(DIRECTORY_SEPARATOR, '/') . '+/', DIRECTORY_SEPARATOR, $path); + } + + /** + * Get filename. + */ + public function getFileName(): string + { + return $this->fileName; + } + + /** + * Get all existing lock files. + */ + public function getExistingLockFiles(): array + { + $paths = []; + foreach ($this->lockFiles as $lockFile) { + $path = $this->getPath() . DIRECTORY_SEPARATOR . $lockFile; + $path = preg_replace('/' . preg_quote(DIRECTORY_SEPARATOR, '/') . '+/', DIRECTORY_SEPARATOR, $path); + if (is_file($path)) { + $paths[] = $path; + } + } + + return $paths; + } + + /** + * Get lock file name. + */ + public function getLockFiles(): array + { + return $this->lockFiles; + } + + /** + * Package file exists. + */ + public function exists(): bool + { + return is_file($this->getFilePath()); + } + + /** + * Save content. + * + * @return $this + */ + public function save() + { + if ($this->exists()) { + switch ($this->fileType) { + case 'json': + $content = json_encode($this->content, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); + if ($content === null && json_last_error() !== JSON_ERROR_NONE) { + throw new \Exception(json_last_error_msg(), json_last_error()); + } + break; + default: + $content = $this->content; + } + file_put_contents($this->getFilePath(), $content); + } + + return $this; + } + + /** + * Set content. + * + * @param mixed $content + * + * @return $this + */ + protected function setContent($content) + { + $this->content = $content; + + return $this; + } + + /** + * Get content and back backup. + * + * @return mixed + */ + protected function getContent() + { + return $this->content; + } +} diff --git a/vendor/marcocesarato/php-conventional-changelog/src/Type/Stringable.php b/vendor/marcocesarato/php-conventional-changelog/src/Type/Stringable.php new file mode 100644 index 00000000..44b78d0b --- /dev/null +++ b/vendor/marcocesarato/php-conventional-changelog/src/Type/Stringable.php @@ -0,0 +1,13 @@ +configuration = $this->createMock(Configuration::class); + $this->changelog = new Changelog($this->configuration); + } + + protected function setUpMocks($callback = null): void + { + $exec = $this->getFunctionMock(__NAMESPACE__, 'shell_exec'); + $exec->expects($this->any())->willReturnCallback( + function ($command) use ($callback) { + $output = null; + if (!empty($callback)) { + $output = $callback($command); + } + if ($output !== null) { + return $output; + } + + // getCommitDate + $commitDate = 'git log -1 --format=%aI'; + if (substr($commitDate, 0, strlen($command)) === $command) { + return '2000-01-01T00:00:00+00:00'; + } + + switch ($command) { + case 'git config --get remote.origin.url': + // getRemoteUrl + return 'https://fake.remote.com/user/repo.git'; + case 'git rev-list --max-parents=0 HEAD': + // getFirstCommit + return '021a49f43ef65ac7a894450374f1772eef1fd8b0'; + case 'git log -1 --pretty=format:%H': + // getLastCommit + return '84d151f93f36474055c9ff406710a47aa3d6e168'; + case 'git branch --show-current': + // getCurrentBranch + return 'main'; + default: + break; + } + } + ); + } + + /** @test */ + public function testRemoveHeader(): void + { + $class = new \ReflectionClass($this->changelog); + $method = $class->getMethod('removeHeader'); + $method->setAccessible(true); + + $content = << +# Changelog + +All notable changes to this project will be documented in this file. + + +## 0.0.1 (1970-01-01) + +### Features + +* Here we go ! +EOF; + + $expectedContent = <<invokeArgs( + $this->changelog, + [$content] + ); + + $this->assertEquals($expectedContent, $actualContent); + } + + /** @test */ + public function testExecMock() + { + $this->setUpMocks(function ($command) { + if ($command == 'bar') { + return 'foo'; + } + }); + + $output = shell_exec('git config --get remote.origin.url'); + $this->assertEquals('https://fake.remote.com/user/repo.git', $output); + + $output = shell_exec('bar'); + $this->assertEquals('foo', $output); + } +} diff --git a/vendor/psr/container/.gitignore b/vendor/psr/container/.gitignore new file mode 100644 index 00000000..b2395aa0 --- /dev/null +++ b/vendor/psr/container/.gitignore @@ -0,0 +1,3 @@ +composer.lock +composer.phar +/vendor/ diff --git a/vendor/psr/container/LICENSE b/vendor/psr/container/LICENSE new file mode 100644 index 00000000..2877a489 --- /dev/null +++ b/vendor/psr/container/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2013-2016 container-interop +Copyright (c) 2016 PHP Framework Interoperability Group + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/psr/container/README.md b/vendor/psr/container/README.md new file mode 100644 index 00000000..1b9d9e57 --- /dev/null +++ b/vendor/psr/container/README.md @@ -0,0 +1,13 @@ +Container interface +============== + +This repository holds all interfaces related to [PSR-11 (Container Interface)][psr-url]. + +Note that this is not a Container implementation of its own. It is merely abstractions that describe the components of a Dependency Injection Container. + +The installable [package][package-url] and [implementations][implementation-url] are listed on Packagist. + +[psr-url]: https://www.php-fig.org/psr/psr-11/ +[package-url]: https://packagist.org/packages/psr/container +[implementation-url]: https://packagist.org/providers/psr/container-implementation + diff --git a/vendor/psr/container/composer.json b/vendor/psr/container/composer.json new file mode 100644 index 00000000..baf6cd1a --- /dev/null +++ b/vendor/psr/container/composer.json @@ -0,0 +1,27 @@ +{ + "name": "psr/container", + "type": "library", + "description": "Common Container Interface (PHP FIG PSR-11)", + "keywords": ["psr", "psr-11", "container", "container-interop", "container-interface"], + "homepage": "https://github.com/php-fig/container", + "license": "MIT", + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "require": { + "php": ">=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 00000000..0f213f2f --- /dev/null +++ b/vendor/psr/container/src/ContainerExceptionInterface.php @@ -0,0 +1,12 @@ + + * + * 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\Completion\Suggestion; +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\DescriptorHelper; +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 ?Command $runningCommand = null; + private string $name; + private string $version; + private ?CommandLoaderInterface $commandLoader = null; + private bool $catchExceptions = true; + private bool $catchErrors = false; + private bool $autoExit = true; + private InputDefinition $definition; + private HelperSet $helperSet; + private ?EventDispatcherInterface $dispatcher = null; + private Terminal $terminal; + private string $defaultCommand; + private bool $singleCommand = false; + private bool $initialized = false; + private ?SignalRegistry $signalRegistry = null; + 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): void + { + $this->dispatcher = $dispatcher; + } + + /** + * @return void + */ + 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; + } + + /** + * @return void + */ + 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()); + } + + $input ??= new ArgvInput(); + $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 (\Throwable $e) { + if ($e instanceof \Exception && !$this->catchExceptions) { + throw $e; + } + if (!$e instanceof \Exception && !$this->catchErrors) { + 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) { + // 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()) { + $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); + } else { + 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(); + } + + try { + if ($e instanceof CommandNotFoundException && $namespace = $this->findNamespace($name)) { + $helper = new DescriptorHelper(); + $helper->describe($output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output, $this, [ + 'format' => 'txt', + 'raw_text' => false, + 'namespace' => $namespace, + 'short' => false, + ]); + + return isset($event) ? $event->getExitCode() : 1; + } + + throw $e; + } catch (NamespaceNotFoundException) { + throw $e; + } + } + } + + if ($command instanceof LazyCommand) { + $command = $command->getCommand(); + } + + $this->runningCommand = $command; + $exitCode = $this->doRunCommand($command, $input, $output); + $this->runningCommand = null; + + return $exitCode; + } + + /** + * @return void + */ + public function reset() + { + } + + /** + * @return void + */ + 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(); + } + + /** + * @return void + */ + 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() + ) { + 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; + } + $suggestions->suggestValue(new Suggestion($command->getName(), $command->getDescription())); + foreach ($command->getAliases() as $name) { + $suggestions->suggestValue(new Suggestion($name, $command->getDescription())); + } + } + + 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. + * + * @return void + */ + public function setCatchExceptions(bool $boolean) + { + $this->catchExceptions = $boolean; + } + + /** + * Sets whether to catch errors or not during commands execution. + */ + public function setCatchErrors(bool $catchErrors = true): void + { + $this->catchErrors = $catchErrors; + } + + /** + * 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. + * + * @return void + */ + 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. + * + * @return void + */ + public function setName(string $name) + { + $this->name = $name; + } + + /** + * Gets the application version. + */ + public function getVersion(): string + { + return $this->version; + } + + /** + * Sets the application version. + * + * @return void + */ + 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 + * + * @return void + */ + 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?->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, fn ($name) => !$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]++/', fn ($m) => 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. + * + * @return void + */ + 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); + } + } + + $commandSignals = $command instanceof SignalableCommandInterface ? $command->getSubscribedSignals() : []; + if ($commandSignals || $this->dispatcher && $this->signalsToDispatchEvent) { + 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 fn () => shell_exec('stty '.$sttyMode)); + } + } + + if ($this->dispatcher) { + // We register application signals, so that we can dispatch the event + foreach ($this->signalsToDispatchEvent as $signal) { + $event = new ConsoleSignalEvent($command, $input, $output, $signal); + + $this->signalRegistry->register($signal, function ($signal) use ($event, $command, $commandSignals) { + $this->dispatcher->dispatch($event, ConsoleEvents::SIGNAL); + $exitCode = $event->getExitCode(); + + // If the command is signalable, we call the handleSignal() method + if (\in_array($signal, $commandSignals, true)) { + $exitCode = $command->handleSignal($signal, $exitCode); + // BC layer for Symfony <= 5 + if (null === $exitCode) { + trigger_deprecation('symfony/console', '6.3', 'Not returning an exit code from "%s::handleSignal()" is deprecated, return "false" to keep the command running or "0" to exit successfully.', get_debug_type($command)); + $exitCode = 0; + } + } + + if (false !== $exitCode) { + $event = new ConsoleTerminateEvent($command, $event->getInput(), $event->getOutput(), $exitCode, $signal); + $this->dispatcher->dispatch($event, ConsoleEvents::TERMINATE); + + exit($event->getExitCode()); + } + }); + } + + // then we register command signals, but not if already handled after the dispatcher + $commandSignals = array_diff($commandSignals, $this->signalsToDispatchEvent); + } + + foreach ($commandSignals as $signal) { + $this->signalRegistry->register($signal, function (int $signal) use ($command): void { + $exitCode = $command->handleSignal($signal); + // BC layer for Symfony <= 5 + if (null === $exitCode) { + trigger_deprecation('symfony/console', '6.3', 'Not returning an exit code from "%s::handleSignal()" is deprecated, return "false" to keep the command running or "0" to exit successfully.', get_debug_type($command)); + $exitCode = 0; + } + + if (false !== $exitCode) { + exit($exitCode); + } + }); + } + } + + 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) { + // 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, fn ($lev) => $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(): void + { + 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 00000000..b337f548 --- /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 00000000..9ccb41d9 --- /dev/null +++ b/vendor/symfony/console/CHANGELOG.md @@ -0,0 +1,261 @@ +CHANGELOG +========= + +6.4 +--- + + * Add `SignalMap` to map signal value to its name + * Multi-line text in vertical tables is aligned properly + * The application can also catch errors with `Application::setCatchErrors(true)` + * Add `RunCommandMessage` and `RunCommandMessageHandler` + * Dispatch `ConsoleTerminateEvent` after an exit on signal handling and add `ConsoleTerminateEvent::getInterruptingSignal()` + +6.3 +--- + + * Add support for choosing exit code while handling signal, or to not exit at all + * Add `ProgressBar::setPlaceholderFormatter` to set a placeholder attached to a instance, instead of being global. + * Add `ReStructuredTextDescriptor` + +6.2 +--- + + * Improve truecolor terminal detection in some cases + * Add support for 256 color terminals (conversion from Ansi24 to Ansi8 if terminal is capable of it) + * Deprecate calling `*Command::setApplication()`, `*FormatterStyle::setForeground/setBackground()`, `Helper::setHelpSet()`, `Input*::setDefault()`, `Question::setAutocompleterCallback/setValidator()`without any arguments + * Change the signature of `OutputFormatterStyleInterface::setForeground/setBackground()` to `setForeground/setBackground(?string)` + * Change the signature of `HelperInterface::setHelperSet()` to `setHelperSet(?HelperSet)` + +6.1 +--- + + * Add support to display table vertically when calling setVertical() + * Add method `__toString()` to `InputInterface` + * Added `OutputWrapper` to prevent truncated URL in `SymfonyStyle::createBlock`. + * Deprecate `Command::$defaultName` and `Command::$defaultDescription`, use the `AsCommand` attribute instead + * Add suggested values for arguments and options in input definition, for input completion + * Add `$resumeAt` parameter to `ProgressBar#start()`, so that one can easily 'resume' progress on longer tasks, and still get accurate `getEstimate()` and `getRemaining()` results. + +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 00000000..7e556546 --- /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 OutputInterface $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 00000000..60ed046a --- /dev/null +++ b/vendor/symfony/console/Color.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 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]) { + return ($background ? '4' : '3').Terminal::getColorMode()->convertFromHexToAnsiColorCode($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))))); + } +} diff --git a/vendor/symfony/console/Command/Command.php b/vendor/symfony/console/Command/Command.php new file mode 100644 index 00000000..704b112d --- /dev/null +++ b/vendor/symfony/console/Command/Command.php @@ -0,0 +1,725 @@ + + * + * 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\Completion\Suggestion; +use Symfony\Component\Console\Exception\ExceptionInterface; +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; +use Symfony\Component\Console\Helper\HelperInterface; +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 + * + * @deprecated since Symfony 6.1, use the AsCommand attribute instead + */ + protected static $defaultName; + + /** + * @var string|null The default command description + * + * @deprecated since Symfony 6.1, use the AsCommand attribute instead + */ + protected static $defaultDescription; + + private ?Application $application = null; + private ?string $name = null; + private ?string $processTitle = null; + private array $aliases = []; + private InputDefinition $definition; + private bool $hidden = false; + private string $help = ''; + private string $description = ''; + private ?InputDefinition $fullDefinition = null; + private bool $ignoreValidationErrors = false; + private ?\Closure $code = null; + private array $synopsis = []; + private array $usages = []; + private ?HelperSet $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'); + + if ($class !== $r->class || null === static::$defaultName) { + return null; + } + + trigger_deprecation('symfony/console', '6.1', 'Relying on the static property "$defaultName" for setting a command name is deprecated. Add the "%s" attribute to the "%s" class instead.', AsCommand::class, static::class); + + return static::$defaultName; + } + + 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'); + + if ($class !== $r->class || null === static::$defaultDescription) { + return null; + } + + trigger_deprecation('symfony/console', '6.1', 'Relying on the static property "$defaultDescription" for setting a command description is deprecated. Add the "%s" attribute to the "%s" class instead.', AsCommand::class, static::class); + + return static::$defaultDescription; + } + + /** + * @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. + * + * @return void + */ + public function ignoreValidationErrors() + { + $this->ignoreValidationErrors = true; + } + + /** + * @return void + */ + public function setApplication(Application $application = null) + { + if (1 > \func_num_args()) { + trigger_deprecation('symfony/console', '6.2', 'Calling "%s()" without any arguments is deprecated, pass null explicitly instead.', __METHOD__); + } + $this->application = $application; + if ($application) { + $this->setHelperSet($application->getHelperSet()); + } else { + $this->helperSet = null; + } + + $this->fullDefinition = null; + } + + /** + * @return void + */ + 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. + * + * @return void + */ + 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. + * + * @return void + */ + 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() + * + * @return void + */ + 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 + { + $definition = $this->getDefinition(); + if (CompletionInput::TYPE_OPTION_VALUE === $input->getCompletionType() && $definition->hasOption($input->getCompletionName())) { + $definition->getOption($input->getCompletionName())->complete($input, $suggestions); + } elseif (CompletionInput::TYPE_ARGUMENT_VALUE === $input->getCompletionType() && $definition->hasArgument($input->getCompletionName())) { + $definition->getArgument($input->getCompletionName())->complete($input, $suggestions); + } + } + + /** + * 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 = $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): void + { + 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 $mode The argument mode: InputArgument::REQUIRED or InputArgument::OPTIONAL + * @param $default The default value (for InputArgument::OPTIONAL mode only) + * @param array|\Closure(CompletionInput,CompletionSuggestions):list $suggestedValues The values used for input completion + * + * @return $this + * + * @throws InvalidArgumentException When argument mode is not valid + */ + public function addArgument(string $name, int $mode = null, string $description = '', mixed $default = null /* array|\Closure $suggestedValues = null */): static + { + $suggestedValues = 5 <= \func_num_args() ? func_get_arg(4) : []; + if (!\is_array($suggestedValues) && !$suggestedValues instanceof \Closure) { + throw new \TypeError(sprintf('Argument 5 passed to "%s()" must be array or \Closure, "%s" given.', __METHOD__, get_debug_type($suggestedValues))); + } + $this->definition->addArgument(new InputArgument($name, $mode, $description, $default, $suggestedValues)); + $this->fullDefinition?->addArgument(new InputArgument($name, $mode, $description, $default, $suggestedValues)); + + 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) + * @param array|\Closure(CompletionInput,CompletionSuggestions):list $suggestedValues The values used for input completion + * + * @return $this + * + * @throws InvalidArgumentException If option mode is invalid or incompatible + */ + public function addOption(string $name, string|array $shortcut = null, int $mode = null, string $description = '', mixed $default = null /* array|\Closure $suggestedValues = [] */): static + { + $suggestedValues = 6 <= \func_num_args() ? func_get_arg(5) : []; + if (!\is_array($suggestedValues) && !$suggestedValues instanceof \Closure) { + throw new \TypeError(sprintf('Argument 5 passed to "%s()" must be array or \Closure, "%s" given.', __METHOD__, get_debug_type($suggestedValues))); + } + $this->definition->addOption(new InputOption($name, $shortcut, $mode, $description, $default, $suggestedValues)); + $this->fullDefinition?->addOption(new InputOption($name, $shortcut, $mode, $description, $default, $suggestedValues)); + + 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?->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. + * + * @return HelperInterface + * + * @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): void + { + 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 00000000..23be5577 --- /dev/null +++ b/vendor/symfony/console/Command/CompleteCommand.php @@ -0,0 +1,223 @@ + + * + * 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\Attribute\AsCommand; +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\Completion\Output\FishCompletionOutput; +use Symfony\Component\Console\Completion\Output\ZshCompletionOutput; +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 + */ +#[AsCommand(name: '|_complete', description: 'Internal command to provide shell completion suggestions')] +final class CompleteCommand extends Command +{ + public const COMPLETION_API_VERSION = '1'; + + /** + * @deprecated since Symfony 6.1 + */ + protected static $defaultName = '|_complete'; + + /** + * @deprecated since Symfony 6.1 + */ + protected static $defaultDescription = 'Internal command to provide shell completion suggestions'; + + private array $completionOutputs; + + private bool $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, + 'fish' => FishCompletionOutput::class, + 'zsh' => ZshCompletionOutput::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('api-version', 'a', InputOption::VALUE_REQUIRED, 'The API version of the completion script') + ->addOption('symfony', 'S', InputOption::VALUE_REQUIRED, 'deprecated') + ; + } + + protected function initialize(InputInterface $input, OutputInterface $output): void + { + $this->isDebug = filter_var(getenv('SYMFONY_COMPLETION_DEBUG'), \FILTER_VALIDATE_BOOL); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + try { + // "symfony" must be kept for compat with the shell scripts generated by Symfony Console 5.4 - 6.1 + $version = $input->getOption('symfony') ? '1' : $input->getOption('api-version'); + if ($version && version_compare($version, self::COMPLETION_API_VERSION, '<')) { + $message = sprintf('Completion script version is not supported ("%s" given, ">=%s" required).', $version, self::COMPLETION_API_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 '.($command instanceof LazyCommand ? $command->getCommand() : $command)::class.' command.'); + + $suggestions->suggestOptions($command->getDefinition()->getOptions()); + } else { + $this->log([ + ' Completing using the '.($command instanceof LazyCommand ? $command->getCommand() : $command)::class.' 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(fn ($o) => $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 2; + } + + return 0; + } + + 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) { + } + + 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) { + } + + 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 00000000..51b613a1 --- /dev/null +++ b/vendor/symfony/console/Command/DumpCompletionCommand.php @@ -0,0 +1,161 @@ + + * + * 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\Attribute\AsCommand; +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 + */ +#[AsCommand(name: 'completion', description: 'Dump the shell completion script')] +final class DumpCompletionCommand extends Command +{ + /** + * @deprecated since Symfony 6.1 + */ + protected static $defaultName = 'completion'; + + /** + * @deprecated since Symfony 6.1 + */ + protected static $defaultDescription = 'Dump the shell completion script'; + + private array $supportedShells; + + protected function configure(): void + { + $fullCommand = $_SERVER['PHP_SELF']; + $commandName = basename($fullCommand); + $fullCommand = @realpath($fullCommand) ?: $fullCommand; + + $shell = $this->guessShell(); + [$rcFile, $completionFile] = match ($shell) { + 'fish' => ['~/.config/fish/config.fish', "/etc/fish/completions/$commandName.fish"], + 'zsh' => ['~/.zshrc', '$fpath[1]/_'.$commandName], + default => ['~/.bashrc', "/etc/bash_completion.d/$commandName"], + }; + + $supportedShells = implode(', ', $this->getSupportedShells()); + + $this + ->setHelp(<<%command.name% command dumps the shell completion script required +to use shell autocompletion (currently, {$supportedShells} completion are supported). + +Static installation +------------------- + +Dump the script to a global completion file and restart your shell: + + %command.full_name% {$shell} | sudo tee {$completionFile} + +Or dump the script to a local file and source it: + + %command.full_name% {$shell} > completion.sh + + # source the file whenever you use the project + source completion.sh + + # or add this line at the end of your "{$rcFile}" file: + source /path/to/completion.sh + +Dynamic installation +-------------------- + +Add this to the end of your shell configuration file (e.g. "{$rcFile}"): + + eval "$({$fullCommand} completion {$shell})" +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', null, $this->getSupportedShells(...)) + ->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 0; + } + + $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 2; + } + + $output->write(str_replace(['{{ COMMAND_NAME }}', '{{ VERSION }}'], [$commandName, CompleteCommand::COMPLETION_API_VERSION], file_get_contents($completionFile))); + + return 0; + } + + 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 + { + if (isset($this->supportedShells)) { + return $this->supportedShells; + } + + $shells = []; + + foreach (new \DirectoryIterator(__DIR__.'/../Resources/') as $file) { + if (str_starts_with($file->getBasename(), 'completion.') && $file->isFile()) { + $shells[] = $file->getExtension(); + } + } + sort($shells); + + return $this->supportedShells = $shells; + } +} diff --git a/vendor/symfony/console/Command/HelpCommand.php b/vendor/symfony/console/Command/HelpCommand.php new file mode 100644 index 00000000..e6447b05 --- /dev/null +++ b/vendor/symfony/console/Command/HelpCommand.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\Console\Command; + +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 $command; + + /** + * @return void + */ + protected function configure() + { + $this->ignoreValidationErrors(); + + $this + ->setName('help') + ->setDefinition([ + new InputArgument('command_name', InputArgument::OPTIONAL, 'The command name', 'help', fn () => array_keys((new ApplicationDescription($this->getApplication()))->getCommands())), + new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt', fn () => (new DescriptorHelper())->getFormats()), + 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 + ) + ; + } + + /** + * @return void + */ + public function setCommand(Command $command) + { + $this->command = $command; + } + + 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; + } +} diff --git a/vendor/symfony/console/Command/LazyCommand.php b/vendor/symfony/console/Command/LazyCommand.php new file mode 100644 index 00000000..d5605822 --- /dev/null +++ b/vendor/symfony/console/Command/LazyCommand.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\Command; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Completion\CompletionInput; +use Symfony\Component\Console\Completion\CompletionSuggestions; +use Symfony\Component\Console\Completion\Suggestion; +use Symfony\Component\Console\Helper\HelperInterface; +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 \Closure|Command $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 (1 > \func_num_args()) { + trigger_deprecation('symfony/console', '6.2', 'Calling "%s()" without any arguments is deprecated, pass null explicitly instead.', __METHOD__); + } + 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(); + } + + /** + * @param array|\Closure(CompletionInput,CompletionSuggestions):list $suggestedValues The values used for input completion + */ + public function addArgument(string $name, int $mode = null, string $description = '', mixed $default = null /* array|\Closure $suggestedValues = [] */): static + { + $suggestedValues = 5 <= \func_num_args() ? func_get_arg(4) : []; + $this->getCommand()->addArgument($name, $mode, $description, $default, $suggestedValues); + + return $this; + } + + /** + * @param array|\Closure(CompletionInput,CompletionSuggestions):list $suggestedValues The values used for input completion + */ + public function addOption(string $name, string|array $shortcut = null, int $mode = null, string $description = '', mixed $default = null /* array|\Closure $suggestedValues = [] */): static + { + $suggestedValues = 6 <= \func_num_args() ? func_get_arg(5) : []; + $this->getCommand()->addOption($name, $shortcut, $mode, $description, $default, $suggestedValues); + + 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): HelperInterface + { + 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 00000000..5850c3d7 --- /dev/null +++ b/vendor/symfony/console/Command/ListCommand.php @@ -0,0 +1,75 @@ + + * + * 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\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 +{ + /** + * @return void + */ + protected function configure() + { + $this + ->setName('list') + ->setDefinition([ + new InputArgument('namespace', InputArgument::OPTIONAL, 'The namespace name', null, fn () => array_keys((new ApplicationDescription($this->getApplication()))->getNamespaces())), + 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', fn () => (new DescriptorHelper())->getFormats()), + 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 + ) + ; + } + + 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; + } +} diff --git a/vendor/symfony/console/Command/LockableTrait.php b/vendor/symfony/console/Command/LockableTrait.php new file mode 100644 index 00000000..c1006a65 --- /dev/null +++ b/vendor/symfony/console/Command/LockableTrait.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\Console\Command; + +use Symfony\Component\Console\Exception\LogicException; +use Symfony\Component\Lock\LockFactory; +use Symfony\Component\Lock\LockInterface; +use Symfony\Component\Lock\Store\FlockStore; +use Symfony\Component\Lock\Store\SemaphoreStore; + +/** + * Basic lock feature for commands. + * + * @author Geoffrey Brier + */ +trait LockableTrait +{ + private ?LockInterface $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. Try running "composer require symfony/lock".'); + } + + 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(): void + { + 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 00000000..f8eb8e52 --- /dev/null +++ b/vendor/symfony/console/Command/SignalableCommandInterface.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\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. + * + * @param int|false $previousExitCode + * + * @return int|false The exit code to return or false to continue the normal execution + */ + public function handleSignal(int $signal, /* int|false $previousExitCode = 0 */); +} diff --git a/vendor/symfony/console/Command/TraceableCommand.php b/vendor/symfony/console/Command/TraceableCommand.php new file mode 100644 index 00000000..d8c46b7f --- /dev/null +++ b/vendor/symfony/console/Command/TraceableCommand.php @@ -0,0 +1,356 @@ + + * + * 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\HelperInterface; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Stopwatch\Stopwatch; + +/** + * @internal + * + * @author Jules Pietri + */ +final class TraceableCommand extends Command implements SignalableCommandInterface +{ + public readonly Command $command; + public int $exitCode; + public ?int $interruptedBySignal = null; + public bool $ignoreValidation; + public bool $isInteractive = false; + public string $duration = 'n/a'; + public string $maxMemoryUsage = 'n/a'; + public InputInterface $input; + public OutputInterface $output; + /** @var array */ + public array $arguments; + /** @var array */ + public array $options; + /** @var array */ + public array $interactiveInputs = []; + public array $handledSignals = []; + + public function __construct( + Command $command, + private readonly Stopwatch $stopwatch, + ) { + if ($command instanceof LazyCommand) { + $command = $command->getCommand(); + } + + $this->command = $command; + + // prevent call to self::getDefaultDescription() + $this->setDescription($command->getDescription()); + + parent::__construct($command->getName()); + + // init below enables calling {@see parent::run()} + [$code, $processTitle, $ignoreValidationErrors] = \Closure::bind(function () { + return [$this->code, $this->processTitle, $this->ignoreValidationErrors]; + }, $command, Command::class)(); + + if (\is_callable($code)) { + $this->setCode($code); + } + + if ($processTitle) { + parent::setProcessTitle($processTitle); + } + + if ($ignoreValidationErrors) { + parent::ignoreValidationErrors(); + } + + $this->ignoreValidation = $ignoreValidationErrors; + } + + public function __call(string $name, array $arguments): mixed + { + return $this->command->{$name}(...$arguments); + } + + public function getSubscribedSignals(): array + { + return $this->command instanceof SignalableCommandInterface ? $this->command->getSubscribedSignals() : []; + } + + public function handleSignal(int $signal, int|false $previousExitCode = 0): int|false + { + if (!$this->command instanceof SignalableCommandInterface) { + return false; + } + + $event = $this->stopwatch->start($this->getName().'.handle_signal'); + + $exit = $this->command->handleSignal($signal, $previousExitCode); + + $event->stop(); + + if (!isset($this->handledSignals[$signal])) { + $this->handledSignals[$signal] = [ + 'handled' => 0, + 'duration' => 0, + 'memory' => 0, + ]; + } + + ++$this->handledSignals[$signal]['handled']; + $this->handledSignals[$signal]['duration'] += $event->getDuration(); + $this->handledSignals[$signal]['memory'] = max( + $this->handledSignals[$signal]['memory'], + $event->getMemory() >> 20 + ); + + return $exit; + } + + /** + * {@inheritdoc} + * + * Calling parent method is required to be used in {@see parent::run()}. + */ + public function ignoreValidationErrors(): void + { + $this->ignoreValidation = true; + $this->command->ignoreValidationErrors(); + + parent::ignoreValidationErrors(); + } + + public function setApplication(Application $application = null): void + { + $this->command->setApplication($application); + } + + public function getApplication(): ?Application + { + return $this->command->getApplication(); + } + + public function setHelperSet(HelperSet $helperSet): void + { + $this->command->setHelperSet($helperSet); + } + + public function getHelperSet(): ?HelperSet + { + return $this->command->getHelperSet(); + } + + public function isEnabled(): bool + { + return $this->command->isEnabled(); + } + + public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void + { + $this->command->complete($input, $suggestions); + } + + /** + * {@inheritdoc} + * + * Calling parent method is required to be used in {@see parent::run()}. + */ + public function setCode(callable $code): static + { + $this->command->setCode($code); + + return parent::setCode(function (InputInterface $input, OutputInterface $output) use ($code): int { + $event = $this->stopwatch->start($this->getName().'.code'); + + $this->exitCode = $code($input, $output); + + $event->stop(); + + return $this->exitCode; + }); + } + + /** + * @internal + */ + public function mergeApplicationDefinition(bool $mergeArgs = true): void + { + $this->command->mergeApplicationDefinition($mergeArgs); + } + + public function setDefinition(array|InputDefinition $definition): static + { + $this->command->setDefinition($definition); + + return $this; + } + + public function getDefinition(): InputDefinition + { + return $this->command->getDefinition(); + } + + public function getNativeDefinition(): InputDefinition + { + return $this->command->getNativeDefinition(); + } + + public function addArgument(string $name, int $mode = null, string $description = '', mixed $default = null, array|\Closure $suggestedValues = []): static + { + $this->command->addArgument($name, $mode, $description, $default, $suggestedValues); + + return $this; + } + + public function addOption(string $name, string|array $shortcut = null, int $mode = null, string $description = '', mixed $default = null, array|\Closure $suggestedValues = []): static + { + $this->command->addOption($name, $shortcut, $mode, $description, $default, $suggestedValues); + + return $this; + } + + /** + * {@inheritdoc} + * + * Calling parent method is required to be used in {@see parent::run()}. + */ + public function setProcessTitle(string $title): static + { + $this->command->setProcessTitle($title); + + return parent::setProcessTitle($title); + } + + public function setHelp(string $help): static + { + $this->command->setHelp($help); + + return $this; + } + + public function getHelp(): string + { + return $this->command->getHelp(); + } + + public function getProcessedHelp(): string + { + return $this->command->getProcessedHelp(); + } + + public function getSynopsis(bool $short = false): string + { + return $this->command->getSynopsis($short); + } + + public function addUsage(string $usage): static + { + $this->command->addUsage($usage); + + return $this; + } + + public function getUsages(): array + { + return $this->command->getUsages(); + } + + public function getHelper(string $name): HelperInterface + { + return $this->command->getHelper($name); + } + + public function run(InputInterface $input, OutputInterface $output): int + { + $this->input = $input; + $this->output = $output; + $this->arguments = $input->getArguments(); + $this->options = $input->getOptions(); + $event = $this->stopwatch->start($this->getName(), 'command'); + + try { + $this->exitCode = parent::run($input, $output); + } finally { + $event->stop(); + + if ($output instanceof ConsoleOutputInterface && $output->isDebug()) { + $output->getErrorOutput()->writeln((string) $event); + } + + $this->duration = $event->getDuration().' ms'; + $this->maxMemoryUsage = ($event->getMemory() >> 20).' MiB'; + + if ($this->isInteractive) { + $this->extractInteractiveInputs($input->getArguments(), $input->getOptions()); + } + } + + return $this->exitCode; + } + + protected function initialize(InputInterface $input, OutputInterface $output): void + { + $event = $this->stopwatch->start($this->getName().'.init', 'command'); + + $this->command->initialize($input, $output); + + $event->stop(); + } + + protected function interact(InputInterface $input, OutputInterface $output): void + { + if (!$this->isInteractive = Command::class !== (new \ReflectionMethod($this->command, 'interact'))->getDeclaringClass()->getName()) { + return; + } + + $event = $this->stopwatch->start($this->getName().'.interact', 'command'); + + $this->command->interact($input, $output); + + $event->stop(); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $event = $this->stopwatch->start($this->getName().'.execute', 'command'); + + $exitCode = $this->command->execute($input, $output); + + $event->stop(); + + return $exitCode; + } + + private function extractInteractiveInputs(array $arguments, array $options): void + { + foreach ($arguments as $argName => $argValue) { + if (\array_key_exists($argName, $this->arguments) && $this->arguments[$argName] === $argValue) { + continue; + } + + $this->interactiveInputs[$argName] = $argValue; + } + + foreach ($options as $optName => $optValue) { + if (\array_key_exists($optName, $this->options) && $this->options[$optName] === $optValue) { + continue; + } + + $this->interactiveInputs['--'.$optName] = $optValue; + } + } +} diff --git a/vendor/symfony/console/CommandLoader/CommandLoaderInterface.php b/vendor/symfony/console/CommandLoader/CommandLoaderInterface.php new file mode 100644 index 00000000..b6b637ce --- /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 00000000..bfa0ac46 --- /dev/null +++ b/vendor/symfony/console/CommandLoader/ContainerCommandLoader.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\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 ContainerInterface $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; + } + + 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]); + } + + public function has(string $name): bool + { + return isset($this->commandMap[$name]) && $this->container->has($this->commandMap[$name]); + } + + 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 00000000..9ced75ae --- /dev/null +++ b/vendor/symfony/console/CommandLoader/FactoryCommandLoader.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\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; + } + + public function has(string $name): bool + { + return isset($this->factories[$name]); + } + + 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(); + } + + 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 00000000..7ba41c08 --- /dev/null +++ b/vendor/symfony/console/Completion/CompletionInput.php @@ -0,0 +1,248 @@ + + * + * 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 array $tokens; + private int $currentIndex; + private string $completionType; + private ?string $completionName = null; + private string $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; + } + + 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 ($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 ($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 + * + * TYPE_OPTION_NAME and TYPE_NONE are already implemented by the Console component. + * + * @return self::TYPE_* + */ + 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) { + // 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 00000000..549bbafb --- /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 array $valueSuggestions = []; + private array $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 00000000..c6f76eb8 --- /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 00000000..659e5965 --- /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/Output/FishCompletionOutput.php b/vendor/symfony/console/Completion/Output/FishCompletionOutput.php new file mode 100644 index 00000000..d2c414e4 --- /dev/null +++ b/vendor/symfony/console/Completion/Output/FishCompletionOutput.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 Guillaume Aveline + */ +class FishCompletionOutput 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->write(implode("\n", $values)); + } +} diff --git a/vendor/symfony/console/Completion/Output/ZshCompletionOutput.php b/vendor/symfony/console/Completion/Output/ZshCompletionOutput.php new file mode 100644 index 00000000..bb4ce70b --- /dev/null +++ b/vendor/symfony/console/Completion/Output/ZshCompletionOutput.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\Console\Completion\Output; + +use Symfony\Component\Console\Completion\CompletionSuggestions; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * @author Jitendra A + */ +class ZshCompletionOutput implements CompletionOutputInterface +{ + public function write(CompletionSuggestions $suggestions, OutputInterface $output): void + { + $values = []; + foreach ($suggestions->getValueSuggestions() as $value) { + $values[] = $value->getValue().($value->getDescription() ? "\t".$value->getDescription() : ''); + } + foreach ($suggestions->getOptionSuggestions() as $option) { + $values[] = '--'.$option->getName().($option->getDescription() ? "\t".$option->getDescription() : ''); + if ($option->isNegatable()) { + $values[] = '--no-'.$option->getName().($option->getDescription() ? "\t".$option->getDescription() : ''); + } + } + $output->write(implode("\n", $values)."\n"); + } +} diff --git a/vendor/symfony/console/Completion/Suggestion.php b/vendor/symfony/console/Completion/Suggestion.php new file mode 100644 index 00000000..7392965a --- /dev/null +++ b/vendor/symfony/console/Completion/Suggestion.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\Console\Completion; + +/** + * Represents a single suggested value. + * + * @author Wouter de Jong + */ +class Suggestion implements \Stringable +{ + public function __construct( + private readonly string $value, + private readonly string $description = '' + ) { + } + + public function getValue(): string + { + return $this->value; + } + + public function getDescription(): string + { + return $this->description; + } + + 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 00000000..6ae8f32b --- /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 00000000..69fd3821 --- /dev/null +++ b/vendor/symfony/console/Cursor.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\Console; + +use Symfony\Component\Console\Output\OutputInterface; + +/** + * @author Pierre du Plessis + */ +final class Cursor +{ + private OutputInterface $output; + /** @var resource */ + 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 (!$isTtySupported ??= '/' === \DIRECTORY_SEPARATOR && stream_isatty(\STDOUT)) { + 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/DataCollector/CommandDataCollector.php b/vendor/symfony/console/DataCollector/CommandDataCollector.php new file mode 100644 index 00000000..16a0eadf --- /dev/null +++ b/vendor/symfony/console/DataCollector/CommandDataCollector.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\Console\DataCollector; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Debug\CliRequest; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\SignalRegistry\SignalMap; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\DataCollector\DataCollector; +use Symfony\Component\VarDumper\Cloner\Data; + +/** + * @internal + * + * @author Jules Pietri + */ +final class CommandDataCollector extends DataCollector +{ + public function collect(Request $request, Response $response, \Throwable $exception = null): void + { + if (!$request instanceof CliRequest) { + return; + } + + $command = $request->command; + $application = $command->getApplication(); + + $this->data = [ + 'command' => $this->cloneVar($command->command), + 'exit_code' => $command->exitCode, + 'interrupted_by_signal' => $command->interruptedBySignal, + 'duration' => $command->duration, + 'max_memory_usage' => $command->maxMemoryUsage, + 'verbosity_level' => match ($command->output->getVerbosity()) { + OutputInterface::VERBOSITY_QUIET => 'quiet', + OutputInterface::VERBOSITY_NORMAL => 'normal', + OutputInterface::VERBOSITY_VERBOSE => 'verbose', + OutputInterface::VERBOSITY_VERY_VERBOSE => 'very verbose', + OutputInterface::VERBOSITY_DEBUG => 'debug', + }, + 'interactive' => $command->isInteractive, + 'validate_input' => !$command->ignoreValidation, + 'enabled' => $command->isEnabled(), + 'visible' => !$command->isHidden(), + 'input' => $this->cloneVar($command->input), + 'output' => $this->cloneVar($command->output), + 'interactive_inputs' => array_map($this->cloneVar(...), $command->interactiveInputs), + 'signalable' => $command->getSubscribedSignals(), + 'handled_signals' => $command->handledSignals, + 'helper_set' => array_map($this->cloneVar(...), iterator_to_array($command->getHelperSet())), + ]; + + $baseDefinition = $application->getDefinition(); + + foreach ($command->arguments as $argName => $argValue) { + if ($baseDefinition->hasArgument($argName)) { + $this->data['application_inputs'][$argName] = $this->cloneVar($argValue); + } else { + $this->data['arguments'][$argName] = $this->cloneVar($argValue); + } + } + + foreach ($command->options as $optName => $optValue) { + if ($baseDefinition->hasOption($optName)) { + $this->data['application_inputs']['--'.$optName] = $this->cloneVar($optValue); + } else { + $this->data['options'][$optName] = $this->cloneVar($optValue); + } + } + } + + public function getName(): string + { + return 'command'; + } + + /** + * @return array{ + * class?: class-string, + * executor?: string, + * file: string, + * line: int, + * } + */ + public function getCommand(): array + { + $class = $this->data['command']->getType(); + $r = new \ReflectionMethod($class, 'execute'); + + if (Command::class !== $r->getDeclaringClass()) { + return [ + 'executor' => $class.'::'.$r->name, + 'file' => $r->getFileName(), + 'line' => $r->getStartLine(), + ]; + } + + $r = new \ReflectionClass($class); + + return [ + 'class' => $class, + 'file' => $r->getFileName(), + 'line' => $r->getStartLine(), + ]; + } + + public function getInterruptedBySignal(): ?string + { + if (isset($this->data['interrupted_by_signal'])) { + return sprintf('%s (%d)', SignalMap::getSignalName($this->data['interrupted_by_signal']), $this->data['interrupted_by_signal']); + } + + return null; + } + + public function getDuration(): string + { + return $this->data['duration']; + } + + public function getMaxMemoryUsage(): string + { + return $this->data['max_memory_usage']; + } + + public function getVerbosityLevel(): string + { + return $this->data['verbosity_level']; + } + + public function getInteractive(): bool + { + return $this->data['interactive']; + } + + public function getValidateInput(): bool + { + return $this->data['validate_input']; + } + + public function getEnabled(): bool + { + return $this->data['enabled']; + } + + public function getVisible(): bool + { + return $this->data['visible']; + } + + public function getInput(): Data + { + return $this->data['input']; + } + + public function getOutput(): Data + { + return $this->data['output']; + } + + /** + * @return Data[] + */ + public function getArguments(): array + { + return $this->data['arguments'] ?? []; + } + + /** + * @return Data[] + */ + public function getOptions(): array + { + return $this->data['options'] ?? []; + } + + /** + * @return Data[] + */ + public function getApplicationInputs(): array + { + return $this->data['application_inputs'] ?? []; + } + + /** + * @return Data[] + */ + public function getInteractiveInputs(): array + { + return $this->data['interactive_inputs'] ?? []; + } + + public function getSignalable(): array + { + return array_map( + static fn (int $signal): string => sprintf('%s (%d)', SignalMap::getSignalName($signal), $signal), + $this->data['signalable'] + ); + } + + public function getHandledSignals(): array + { + $keys = array_map( + static fn (int $signal): string => sprintf('%s (%d)', SignalMap::getSignalName($signal), $signal), + array_keys($this->data['handled_signals']) + ); + + return array_combine($keys, array_values($this->data['handled_signals'])); + } + + /** + * @return Data[] + */ + public function getHelperSet(): array + { + return $this->data['helper_set'] ?? []; + } + + public function reset(): void + { + $this->data = []; + } +} diff --git a/vendor/symfony/console/Debug/CliRequest.php b/vendor/symfony/console/Debug/CliRequest.php new file mode 100644 index 00000000..b023db07 --- /dev/null +++ b/vendor/symfony/console/Debug/CliRequest.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\Console\Debug; + +use Symfony\Component\Console\Command\TraceableCommand; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * @internal + */ +final class CliRequest extends Request +{ + public function __construct( + public readonly TraceableCommand $command, + ) { + parent::__construct( + attributes: ['_controller' => \get_class($command->command), '_virtual_type' => 'command'], + server: $_SERVER, + ); + } + + // Methods below allow to populate a profile, thus enable search and filtering + public function getUri(): string + { + if ($this->server->has('SYMFONY_CLI_BINARY_NAME')) { + $binary = $this->server->get('SYMFONY_CLI_BINARY_NAME').' console'; + } else { + $binary = $this->server->get('argv')[0]; + } + + return $binary.' '.$this->command->input; + } + + public function getMethod(): string + { + return $this->command->isInteractive ? 'INTERACTIVE' : 'BATCH'; + } + + public function getResponse(): Response + { + return new class($this->command->exitCode) extends Response { + public function __construct(private readonly int $exitCode) + { + parent::__construct(); + } + + public function getStatusCode(): int + { + return $this->exitCode; + } + }; + } + + public function getClientIp(): string + { + $application = $this->command->getApplication(); + + return $application->getName().' '.$application->getVersion(); + } +} diff --git a/vendor/symfony/console/DependencyInjection/AddConsoleCommandPass.php b/vendor/symfony/console/DependencyInjection/AddConsoleCommandPass.php new file mode 100644 index 00000000..27705ddb --- /dev/null +++ b/vendor/symfony/console/DependencyInjection/AddConsoleCommandPass.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 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 +{ + /** + * @return void + */ + 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 ??= $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 00000000..f8ed1804 --- /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 $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(): void + { + $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 00000000..7b2509c6 --- /dev/null +++ b/vendor/symfony/console/Descriptor/Descriptor.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\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 +{ + protected OutputInterface $output; + + public function describe(OutputInterface $output, object $object, array $options = []): void + { + $this->output = $output; + + match (true) { + $object instanceof InputArgument => $this->describeInputArgument($object, $options), + $object instanceof InputOption => $this->describeInputOption($object, $options), + $object instanceof InputDefinition => $this->describeInputDefinition($object, $options), + $object instanceof Command => $this->describeCommand($object, $options), + $object instanceof Application => $this->describeApplication($object, $options), + default => throw new InvalidArgumentException(sprintf('Object of type "%s" is not describable.', get_debug_type($object))), + }; + } + + protected function write(string $content, bool $decorated = false): void + { + $this->output->write($content, false, $decorated ? OutputInterface::OUTPUT_NORMAL : OutputInterface::OUTPUT_RAW); + } + + /** + * Describes an InputArgument instance. + */ + abstract protected function describeInputArgument(InputArgument $argument, array $options = []): void; + + /** + * Describes an InputOption instance. + */ + abstract protected function describeInputOption(InputOption $option, array $options = []): void; + + /** + * Describes an InputDefinition instance. + */ + abstract protected function describeInputDefinition(InputDefinition $definition, array $options = []): void; + + /** + * Describes a Command instance. + */ + abstract protected function describeCommand(Command $command, array $options = []): void; + + /** + * Describes an Application instance. + */ + abstract protected function describeApplication(Application $application, array $options = []): void; +} diff --git a/vendor/symfony/console/Descriptor/DescriptorInterface.php b/vendor/symfony/console/Descriptor/DescriptorInterface.php new file mode 100644 index 00000000..ab468a25 --- /dev/null +++ b/vendor/symfony/console/Descriptor/DescriptorInterface.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\Console\Descriptor; + +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Descriptor interface. + * + * @author Jean-François Simon + */ +interface DescriptorInterface +{ + /** + * @return void + */ + 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 00000000..95630370 --- /dev/null +++ b/vendor/symfony/console/Descriptor/JsonDescriptor.php @@ -0,0 +1,166 @@ + + * + * 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 +{ + protected function describeInputArgument(InputArgument $argument, array $options = []): void + { + $this->writeData($this->getInputArgumentData($argument), $options); + } + + protected function describeInputOption(InputOption $option, array $options = []): void + { + $this->writeData($this->getInputOptionData($option), $options); + if ($option->isNegatable()) { + $this->writeData($this->getInputOptionData($option, true), $options); + } + } + + protected function describeInputDefinition(InputDefinition $definition, array $options = []): void + { + $this->writeData($this->getInputDefinitionData($definition), $options); + } + + protected function describeCommand(Command $command, array $options = []): void + { + $this->writeData($this->getCommandData($command, $options['short'] ?? false), $options); + } + + protected function describeApplication(Application $application, array $options = []): void + { + $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): void + { + $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 00000000..b3f16ee9 --- /dev/null +++ b/vendor/symfony/console/Descriptor/MarkdownDescriptor.php @@ -0,0 +1,173 @@ + + * + * 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 +{ + public function describe(OutputInterface $output, object $object, array $options = []): void + { + $decorated = $output->isDecorated(); + $output->setDecorated(false); + + parent::describe($output, $object, $options); + + $output->setDecorated($decorated); + } + + protected function write(string $content, bool $decorated = true): void + { + parent::write($content, $decorated); + } + + protected function describeInputArgument(InputArgument $argument, array $options = []): void + { + $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)).'`' + ); + } + + protected function describeInputOption(InputOption $option, array $options = []): void + { + $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)).'`' + ); + } + + protected function describeInputDefinition(InputDefinition $definition, array $options = []): void + { + if ($showArguments = \count($definition->getArguments()) > 0) { + $this->write('### Arguments'); + foreach ($definition->getArguments() as $argument) { + $this->write("\n\n"); + $this->describeInputArgument($argument); + } + } + + if (\count($definition->getOptions()) > 0) { + if ($showArguments) { + $this->write("\n\n"); + } + + $this->write('### Options'); + foreach ($definition->getOptions() as $option) { + $this->write("\n\n"); + $this->describeInputOption($option); + } + } + } + + protected function describeCommand(Command $command, array $options = []): void + { + 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(), fn ($carry, $usage) => $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()), fn ($carry, $usage) => $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); + } + } + + protected function describeApplication(Application $application, array $options = []): void + { + $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(fn ($commandName) => sprintf('* [`%s`](#%s)', $commandName, str_replace(':', '', $description->getCommand($commandName)->getName())), $namespace['commands']))); + } + + foreach ($description->getCommands() as $command) { + $this->write("\n\n"); + $this->describeCommand($command, $options); + } + } + + 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/ReStructuredTextDescriptor.php b/vendor/symfony/console/Descriptor/ReStructuredTextDescriptor.php new file mode 100644 index 00000000..d4423fd3 --- /dev/null +++ b/vendor/symfony/console/Descriptor/ReStructuredTextDescriptor.php @@ -0,0 +1,272 @@ + + * + * 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; +use Symfony\Component\String\UnicodeString; + +class ReStructuredTextDescriptor extends Descriptor +{ + //

+ private string $partChar = '='; + //

+ private string $chapterChar = '-'; + //

+ private string $sectionChar = '~'; + //

+ private string $subsectionChar = '.'; + //

+ private string $subsubsectionChar = '^'; + //
+ private string $paragraphsChar = '"'; + + private array $visibleNamespaces = []; + + public function describe(OutputInterface $output, object $object, array $options = []): void + { + $decorated = $output->isDecorated(); + $output->setDecorated(false); + + parent::describe($output, $object, $options); + + $output->setDecorated($decorated); + } + + /** + * Override parent method to set $decorated = true. + */ + protected function write(string $content, bool $decorated = true): void + { + parent::write($content, $decorated); + } + + protected function describeInputArgument(InputArgument $argument, array $options = []): void + { + $this->write( + $argument->getName() ?: ''."\n".str_repeat($this->paragraphsChar, Helper::width($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)).'``' + ); + } + + protected function describeInputOption(InputOption $option, array $options = []): void + { + $name = '\-\-'.$option->getName(); + if ($option->isNegatable()) { + $name .= '|\-\-no-'.$option->getName(); + } + if ($option->getShortcut()) { + $name .= '|-'.str_replace('|', '|-', $option->getShortcut()); + } + + $optionDescription = $option->getDescription() ? preg_replace('/\s*[\r\n]\s*/', "\n\n", $option->getDescription())."\n\n" : ''; + $optionDescription = (new UnicodeString($optionDescription))->ascii(); + $this->write( + $name."\n".str_repeat($this->paragraphsChar, Helper::width($name))."\n\n" + .$optionDescription + .'- **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)).'``'."\n" + ); + } + + protected function describeInputDefinition(InputDefinition $definition, array $options = []): void + { + if ($showArguments = ((bool) $definition->getArguments())) { + $this->write("Arguments\n".str_repeat($this->subsubsectionChar, 9))."\n\n"; + foreach ($definition->getArguments() as $argument) { + $this->write("\n\n"); + $this->describeInputArgument($argument); + } + } + + if ($nonDefaultOptions = $this->getNonDefaultOptions($definition)) { + if ($showArguments) { + $this->write("\n\n"); + } + + $this->write("Options\n".str_repeat($this->subsubsectionChar, 7)."\n\n"); + foreach ($nonDefaultOptions as $option) { + $this->describeInputOption($option); + $this->write("\n"); + } + } + } + + protected function describeCommand(Command $command, array $options = []): void + { + if ($options['short'] ?? false) { + $this->write( + '``'.$command->getName()."``\n" + .str_repeat($this->subsectionChar, Helper::width($command->getName()))."\n\n" + .($command->getDescription() ? $command->getDescription()."\n\n" : '') + ."Usage\n".str_repeat($this->paragraphsChar, 5)."\n\n" + .array_reduce($command->getAliases(), static fn ($carry, $usage) => $carry.'- ``'.$usage.'``'."\n") + ); + + return; + } + + $command->mergeApplicationDefinition(false); + + foreach ($command->getAliases() as $alias) { + $this->write('.. _'.$alias.":\n\n"); + } + $this->write( + $command->getName()."\n" + .str_repeat($this->subsectionChar, Helper::width($command->getName()))."\n\n" + .($command->getDescription() ? $command->getDescription()."\n\n" : '') + ."Usage\n".str_repeat($this->subsubsectionChar, 5)."\n\n" + .array_reduce(array_merge([$command->getSynopsis()], $command->getAliases(), $command->getUsages()), static fn ($carry, $usage) => $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); + } + } + + protected function describeApplication(Application $application, array $options = []): void + { + $description = new ApplicationDescription($application, $options['namespace'] ?? null); + $title = $this->getApplicationTitle($application); + + $this->write($title."\n".str_repeat($this->partChar, Helper::width($title))); + $this->createTableOfContents($description, $application); + $this->describeCommands($application, $options); + } + + private function getApplicationTitle(Application $application): string + { + if ('UNKNOWN' === $application->getName()) { + return 'Console Tool'; + } + if ('UNKNOWN' !== $application->getVersion()) { + return sprintf('%s %s', $application->getName(), $application->getVersion()); + } + + return $application->getName(); + } + + private function describeCommands($application, array $options): void + { + $title = 'Commands'; + $this->write("\n\n$title\n".str_repeat($this->chapterChar, Helper::width($title))."\n\n"); + foreach ($this->visibleNamespaces as $namespace) { + if ('_global' === $namespace) { + $commands = $application->all(''); + $this->write('Global'."\n".str_repeat($this->sectionChar, Helper::width('Global'))."\n\n"); + } else { + $commands = $application->all($namespace); + $this->write($namespace."\n".str_repeat($this->sectionChar, Helper::width($namespace))."\n\n"); + } + + foreach ($this->removeAliasesAndHiddenCommands($commands) as $command) { + $this->describeCommand($command, $options); + $this->write("\n\n"); + } + } + } + + private function createTableOfContents(ApplicationDescription $description, Application $application): void + { + $this->setVisibleNamespaces($description); + $chapterTitle = 'Table of Contents'; + $this->write("\n\n$chapterTitle\n".str_repeat($this->chapterChar, Helper::width($chapterTitle))."\n\n"); + foreach ($this->visibleNamespaces as $namespace) { + if ('_global' === $namespace) { + $commands = $application->all(''); + } else { + $commands = $application->all($namespace); + $this->write("\n\n"); + $this->write($namespace."\n".str_repeat($this->sectionChar, Helper::width($namespace))."\n\n"); + } + $commands = $this->removeAliasesAndHiddenCommands($commands); + + $this->write("\n\n"); + $this->write(implode("\n", array_map(static fn ($commandName) => sprintf('- `%s`_', $commandName), array_keys($commands)))); + } + } + + private function getNonDefaultOptions(InputDefinition $definition): array + { + $globalOptions = [ + 'help', + 'quiet', + 'verbose', + 'version', + 'ansi', + 'no-interaction', + ]; + $nonDefaultOptions = []; + foreach ($definition->getOptions() as $option) { + // Skip global options. + if (!\in_array($option->getName(), $globalOptions)) { + $nonDefaultOptions[] = $option; + } + } + + return $nonDefaultOptions; + } + + private function setVisibleNamespaces(ApplicationDescription $description): void + { + $commands = $description->getCommands(); + foreach ($description->getNamespaces() as $namespace) { + try { + $namespaceCommands = $namespace['commands']; + foreach ($namespaceCommands as $key => $commandName) { + if (!\array_key_exists($commandName, $commands)) { + // If the array key does not exist, then this is an alias. + unset($namespaceCommands[$key]); + } elseif ($commands[$commandName]->isHidden()) { + unset($namespaceCommands[$key]); + } + } + if (!$namespaceCommands) { + // If the namespace contained only aliases or hidden commands, skip the namespace. + continue; + } + } catch (\Exception) { + } + $this->visibleNamespaces[] = $namespace['id']; + } + } + + private function removeAliasesAndHiddenCommands(array $commands): array + { + foreach ($commands as $key => $command) { + if ($command->isHidden() || \in_array($key, $command->getAliases(), true)) { + unset($commands[$key]); + } + } + unset($commands['completion']); + + return $commands; + } +} diff --git a/vendor/symfony/console/Descriptor/TextDescriptor.php b/vendor/symfony/console/Descriptor/TextDescriptor.php new file mode 100644 index 00000000..d04d1023 --- /dev/null +++ b/vendor/symfony/console/Descriptor/TextDescriptor.php @@ -0,0 +1,317 @@ + + * + * 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 +{ + protected function describeInputArgument(InputArgument $argument, array $options = []): void + { + 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); + } + + protected function describeInputOption(InputOption $option, array $options = []): void + { + 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); + } + + protected function describeInputDefinition(InputDefinition $definition, array $options = []): void + { + $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])); + } + } + } + + protected function describeCommand(Command $command, array $options = []): void + { + $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"); + } + } + + protected function describeApplication(Application $application, array $options = []): void + { + $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(fn ($namespace) => 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'], fn ($name) => 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"); + } + } + + private function writeText(string $content, array $options = []): void + { + $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 00000000..72580fd9 --- /dev/null +++ b/vendor/symfony/console/Descriptor/XmlDescriptor.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\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; + } + + protected function describeInputArgument(InputArgument $argument, array $options = []): void + { + $this->writeDocument($this->getInputArgumentDocument($argument)); + } + + protected function describeInputOption(InputOption $option, array $options = []): void + { + $this->writeDocument($this->getInputOptionDocument($option)); + } + + protected function describeInputDefinition(InputDefinition $definition, array $options = []): void + { + $this->writeDocument($this->getInputDefinitionDocument($definition)); + } + + protected function describeCommand(Command $command, array $options = []): void + { + $this->writeDocument($this->getCommandDocument($command, $options['short'] ?? false)); + } + + protected function describeApplication(Application $application, array $options = []): void + { + $this->writeDocument($this->getApplicationDocument($application, $options['namespace'] ?? null, $options['short'] ?? false)); + } + + /** + * Appends document children to parent node. + */ + private function appendDocument(\DOMNode $parentNode, \DOMNode $importedParent): void + { + foreach ($importedParent->childNodes as $childNode) { + $parentNode->appendChild($parentNode->ownerDocument->importNode($childNode, true)); + } + } + + /** + * Writes DOM document. + */ + private function writeDocument(\DOMDocument $dom): void + { + $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 00000000..0757a23f --- /dev/null +++ b/vendor/symfony/console/Event/ConsoleCommandEvent.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\Console\Event; + +/** + * Allows to do things before the command is executed, like skipping the command or executing code before the command is + * going to be executed. + * + * Changing the input arguments will have no effect. + * + * @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 00000000..d4a69121 --- /dev/null +++ b/vendor/symfony/console/Event/ConsoleErrorEvent.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\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->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 00000000..6ba1615f --- /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 InputInterface $input; + private OutputInterface $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 00000000..95af1f91 --- /dev/null +++ b/vendor/symfony/console/Event/ConsoleSignalEvent.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\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; + private int|false $exitCode; + + public function __construct(Command $command, InputInterface $input, OutputInterface $output, int $handlingSignal, int|false $exitCode = 0) + { + parent::__construct($command, $input, $output); + $this->handlingSignal = $handlingSignal; + $this->exitCode = $exitCode; + } + + public function getHandlingSignal(): int + { + return $this->handlingSignal; + } + + public function setExitCode(int $exitCode): void + { + if ($exitCode < 0 || $exitCode > 255) { + throw new \InvalidArgumentException('Exit code must be between 0 and 255.'); + } + + $this->exitCode = $exitCode; + } + + public function abortExit(): void + { + $this->exitCode = false; + } + + public function getExitCode(): int|false + { + return $this->exitCode; + } +} diff --git a/vendor/symfony/console/Event/ConsoleTerminateEvent.php b/vendor/symfony/console/Event/ConsoleTerminateEvent.php new file mode 100644 index 00000000..38f7253a --- /dev/null +++ b/vendor/symfony/console/Event/ConsoleTerminateEvent.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\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 + * @author Jules Pietri + */ +final class ConsoleTerminateEvent extends ConsoleEvent +{ + public function __construct( + Command $command, + InputInterface $input, + OutputInterface $output, + private int $exitCode, + private readonly ?int $interruptingSignal = null, + ) { + parent::__construct($command, $input, $output); + } + + public function setExitCode(int $exitCode): void + { + $this->exitCode = $exitCode; + } + + public function getExitCode(): int + { + return $this->exitCode; + } + + public function getInterruptingSignal(): ?int + { + return $this->interruptingSignal; + } +} diff --git a/vendor/symfony/console/EventListener/ErrorListener.php b/vendor/symfony/console/EventListener/ErrorListener.php new file mode 100644 index 00000000..9925a5f7 --- /dev/null +++ b/vendor/symfony/console/EventListener/ErrorListener.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\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 ?LoggerInterface $logger; + + public function __construct(LoggerInterface $logger = null) + { + $this->logger = $logger; + } + + /** + * @return void + */ + 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()]); + } + + /** + * @return void + */ + 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()?->getName(); + $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 00000000..1e9f1c79 --- /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 00000000..1624e13d --- /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 00000000..07cc0b61 --- /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 00000000..5cf62792 --- /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 00000000..fc37b8d8 --- /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 00000000..04f02ade --- /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 00000000..dd16e450 --- /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/RunCommandFailedException.php b/vendor/symfony/console/Exception/RunCommandFailedException.php new file mode 100644 index 00000000..5d87ec94 --- /dev/null +++ b/vendor/symfony/console/Exception/RunCommandFailedException.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\Console\Exception; + +use Symfony\Component\Console\Messenger\RunCommandContext; + +/** + * @author Kevin Bond + */ +final class RunCommandFailedException extends RuntimeException +{ + public function __construct(\Throwable|string $exception, public readonly RunCommandContext $context) + { + parent::__construct( + $exception instanceof \Throwable ? $exception->getMessage() : $exception, + $exception instanceof \Throwable ? $exception->getCode() : 0, + $exception instanceof \Throwable ? $exception : null, + ); + } +} diff --git a/vendor/symfony/console/Exception/RuntimeException.php b/vendor/symfony/console/Exception/RuntimeException.php new file mode 100644 index 00000000..51d7d80a --- /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 00000000..5c11c764 --- /dev/null +++ b/vendor/symfony/console/Formatter/NullOutputFormatter.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\Formatter; + +/** + * @author Tien Xuan Vo + */ +final class NullOutputFormatter implements OutputFormatterInterface +{ + private NullOutputFormatterStyle $style; + + public function format(?string $message): ?string + { + return null; + } + + public function getStyle(string $name): OutputFormatterStyleInterface + { + // to comply with the interface we must return a OutputFormatterStyleInterface + return $this->style ??= new NullOutputFormatterStyle(); + } + + public function hasStyle(string $name): bool + { + return false; + } + + public function isDecorated(): bool + { + return false; + } + + public function setDecorated(bool $decorated): void + { + // do nothing + } + + 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 00000000..c2ce7d14 --- /dev/null +++ b/vendor/symfony/console/Formatter/NullOutputFormatterStyle.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\Console\Formatter; + +/** + * @author Tien Xuan Vo + */ +final class NullOutputFormatterStyle implements OutputFormatterStyleInterface +{ + public function apply(string $text): string + { + return $text; + } + + public function setBackground(string $color = null): void + { + if (1 > \func_num_args()) { + trigger_deprecation('symfony/console', '6.2', 'Calling "%s()" without any arguments is deprecated, pass null explicitly instead.', __METHOD__); + } + // do nothing + } + + public function setForeground(string $color = null): void + { + if (1 > \func_num_args()) { + trigger_deprecation('symfony/console', '6.2', 'Calling "%s()" without any arguments is deprecated, pass null explicitly instead.', __METHOD__); + } + // do nothing + } + + public function setOption(string $option): void + { + // do nothing + } + + public function setOptions(array $options): void + { + // do nothing + } + + 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 00000000..3e4897c3 --- /dev/null +++ b/vendor/symfony/console/Formatter/OutputFormatter.php @@ -0,0 +1,277 @@ + + * + * 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 function Symfony\Component\String\b; + +/** + * Formatter class for console output. + * + * @author Konstantin Kudryashov + * @author Roland Franssen + */ +class OutputFormatter implements WrappableOutputFormatterInterface +{ + private bool $decorated; + private array $styles = []; + private OutputFormatterStyleStack $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(); + } + + /** + * @return void + */ + public function setDecorated(bool $decorated) + { + $this->decorated = $decorated; + } + + public function isDecorated(): bool + { + return $this->decorated; + } + + /** + * @return void + */ + public function setStyle(string $name, OutputFormatterStyleInterface $style) + { + $this->styles[strtolower($name)] = $style; + } + + public function hasStyle(string $name): bool + { + return isset($this->styles[strtolower($name)]); + } + + public function getStyle(string $name): OutputFormatterStyleInterface + { + if (!$this->hasStyle($name)) { + throw new InvalidArgumentException(sprintf('Undefined style: "%s".', $name)); + } + + return $this->styles[strtolower($name)]; + } + + public function format(?string $message): ?string + { + return $this->formatAndWrap($message, 0); + } + + /** + * @return string + */ + 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.$this->addLineBreaks($text, $width); + $text = rtrim($text, "\n").($matches[1] ?? ''); + + if (!$currentLineLength && '' !== $current && !str_ends_with($current, "\n")) { + $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); + } + + private function addLineBreaks(string $text, int $width): string + { + $encoding = mb_detect_encoding($text, null, true) ?: 'UTF-8'; + + return b($text)->toCodePointString($encoding)->wordwrap($width, "\n", true)->toByteString($encoding); + } +} diff --git a/vendor/symfony/console/Formatter/OutputFormatterInterface.php b/vendor/symfony/console/Formatter/OutputFormatterInterface.php new file mode 100644 index 00000000..433cd419 --- /dev/null +++ b/vendor/symfony/console/Formatter/OutputFormatterInterface.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\Formatter; + +/** + * Formatter interface for console output. + * + * @author Konstantin Kudryashov + */ +interface OutputFormatterInterface +{ + /** + * Sets the decorated flag. + * + * @return void + */ + public function setDecorated(bool $decorated); + + /** + * Whether the output will decorate messages. + */ + public function isDecorated(): bool; + + /** + * Sets a new style. + * + * @return void + */ + 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 00000000..346a474c --- /dev/null +++ b/vendor/symfony/console/Formatter/OutputFormatterStyle.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\Formatter; + +use Symfony\Component\Console\Color; + +/** + * Formatter style class for defining styles. + * + * @author Konstantin Kudryashov + */ +class OutputFormatterStyle implements OutputFormatterStyleInterface +{ + private Color $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); + } + + /** + * @return void + */ + public function setForeground(string $color = null) + { + if (1 > \func_num_args()) { + trigger_deprecation('symfony/console', '6.2', 'Calling "%s()" without any arguments is deprecated, pass null explicitly instead.', __METHOD__); + } + $this->color = new Color($this->foreground = $color ?: '', $this->background, $this->options); + } + + /** + * @return void + */ + public function setBackground(string $color = null) + { + if (1 > \func_num_args()) { + trigger_deprecation('symfony/console', '6.2', 'Calling "%s()" without any arguments is deprecated, pass null explicitly instead.', __METHOD__); + } + $this->color = new Color($this->foreground, $this->background = $color ?: '', $this->options); + } + + public function setHref(string $url): void + { + $this->href = $url; + } + + /** + * @return void + */ + public function setOption(string $option) + { + $this->options[] = $option; + $this->color = new Color($this->foreground, $this->background, $this->options); + } + + /** + * @return void + */ + 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); + } + + /** + * @return void + */ + public function setOptions(array $options) + { + $this->color = new Color($this->foreground, $this->background, $this->options = $options); + } + + public function apply(string $text): string + { + $this->handlesHrefGracefully ??= 'JetBrains-JediTerm' !== getenv('TERMINAL_EMULATOR') + && (!getenv('KONSOLE_VERSION') || (int) getenv('KONSOLE_VERSION') > 201100) + && !isset($_SERVER['IDEA_INITIAL_DIRECTORY']); + + 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 00000000..3b15098c --- /dev/null +++ b/vendor/symfony/console/Formatter/OutputFormatterStyleInterface.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\Console\Formatter; + +/** + * Formatter style interface for defining styles. + * + * @author Konstantin Kudryashov + */ +interface OutputFormatterStyleInterface +{ + /** + * Sets style foreground color. + * + * @return void + */ + public function setForeground(?string $color); + + /** + * Sets style background color. + * + * @return void + */ + public function setBackground(?string $color); + + /** + * Sets some specific style option. + * + * @return void + */ + public function setOption(string $option); + + /** + * Unsets some specific style option. + * + * @return void + */ + public function unsetOption(string $option); + + /** + * Sets multiple style options at once. + * + * @return void + */ + 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 00000000..f98c2eff --- /dev/null +++ b/vendor/symfony/console/Formatter/OutputFormatterStyleStack.php @@ -0,0 +1,107 @@ + + * + * 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 OutputFormatterStyleInterface $emptyStyle; + + public function __construct(OutputFormatterStyleInterface $emptyStyle = null) + { + $this->emptyStyle = $emptyStyle ?? new OutputFormatterStyle(); + $this->reset(); + } + + /** + * Resets stack (ie. empty internal arrays). + * + * @return void + */ + public function reset() + { + $this->styles = []; + } + + /** + * Pushes a style in the stack. + * + * @return void + */ + 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 (!$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 (!$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 00000000..746cd27e --- /dev/null +++ b/vendor/symfony/console/Formatter/WrappableOutputFormatterInterface.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\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). + * + * @return string + */ + 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 00000000..9ea7fb91 --- /dev/null +++ b/vendor/symfony/console/Helper/DebugFormatterHelper.php @@ -0,0 +1,98 @@ + + * + * 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']]); + } + + 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 00000000..eb32bce8 --- /dev/null +++ b/vendor/symfony/console/Helper/DescriptorHelper.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 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\ReStructuredTextDescriptor; +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()) + ->register('rst', new ReStructuredTextDescriptor()) + ; + } + + /** + * Describes an object if supported. + * + * Available options are: + * * format: string, the output format name + * * raw_text: boolean, sets output type as raw + * + * @return void + * + * @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; + } + + 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 00000000..8c6a94d5 --- /dev/null +++ b/vendor/symfony/console/Helper/Dumper.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\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 OutputInterface $output; + private ?CliDumper $dumper; + private ?ClonerInterface $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 ??= new CliDumper(null, null, CliDumper::DUMP_LIGHT_ARRAY | CliDumper::DUMP_COMMA_SEPARATOR); + $dumper->setColors($this->output->isDecorated()); + + return rtrim($dumper->dump(($this->cloner ??= new VarCloner())->cloneVar($var)->withRefHandles(false), true)); + }; + } else { + $this->handler = fn ($var): string => match (true) { + null === $var => 'null', + true === $var => 'true', + false === $var => 'false', + \is_string($var) => '"'.$var.'"', + default => 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 00000000..279e4c79 --- /dev/null +++ b/vendor/symfony/console/Helper/FormatterHelper.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\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; + } + + 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 00000000..c4b3df72 --- /dev/null +++ b/vendor/symfony/console/Helper/Helper.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\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; + + /** + * @return void + */ + public function setHelperSet(HelperSet $helperSet = null) + { + if (1 > \func_num_args()) { + trigger_deprecation('symfony/console', '6.2', 'Calling "%s()" without any arguments is deprecated, pass null explicitly instead.', __METHOD__); + } + $this->helperSet = $helperSet; + } + + 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 ??= ''; + + 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 ??= ''; + + 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 ??= ''; + + if (false === $encoding = mb_detect_encoding($string, null, true)) { + return substr($string, $from, $length); + } + + return mb_substr($string, $from, $length, $encoding); + } + + /** + * @return string + */ + public static function formatTime(int|float $secs, int $precision = 1) + { + $secs = (int) floor($secs); + + if (0 === $secs) { + return '< 1 sec'; + } + + static $timeFormats = [ + [1, '1 sec', 'secs'], + [60, '1 min', 'mins'], + [3600, '1 hr', 'hrs'], + [86400, '1 day', 'days'], + ]; + + $times = []; + foreach ($timeFormats as $index => $format) { + $seconds = isset($timeFormats[$index + 1]) ? $secs % $timeFormats[$index + 1][0] : $secs; + + if (isset($times[$index - $precision])) { + unset($times[$index - $precision]); + } + + if (0 === $seconds) { + continue; + } + + $unitCount = ($seconds / $format[0]); + $times[$index] = 1 === $unitCount ? $format[1] : $unitCount.' '.$format[2]; + + if ($secs === $seconds) { + break; + } + + $secs -= $seconds; + } + + return implode(', ', array_reverse($times)); + } + + /** + * @return string + */ + 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); + } + + /** + * @return string + */ + 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 00000000..ab626c93 --- /dev/null +++ b/vendor/symfony/console/Helper/HelperInterface.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\Helper; + +/** + * HelperInterface is the interface all helpers must implement. + * + * @author Fabien Potencier + */ +interface HelperInterface +{ + /** + * Sets the helper set associated with this helper. + * + * @return void + */ + public function setHelperSet(?HelperSet $helperSet); + + /** + * 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 00000000..dc5d499c --- /dev/null +++ b/vendor/symfony/console/Helper/HelperSet.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\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 HelperInterface[] $helpers + */ + public function __construct(array $helpers = []) + { + foreach ($helpers as $alias => $helper) { + $this->set($helper, \is_int($alias) ? null : $alias); + } + } + + /** + * @return void + */ + 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 00000000..6f822597 --- /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; + + /** + * @return void + */ + public function setInput(InputInterface $input) + { + $this->input = $input; + } +} diff --git a/vendor/symfony/console/Helper/OutputWrapper.php b/vendor/symfony/console/Helper/OutputWrapper.php new file mode 100644 index 00000000..2ec819c7 --- /dev/null +++ b/vendor/symfony/console/Helper/OutputWrapper.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\Helper; + +/** + * Simple output wrapper for "tagged outputs" instead of wordwrap(). This solution is based on a StackOverflow + * answer: https://stackoverflow.com/a/20434776/1476819 from user557597 (alias SLN). + * + * (?: + * # -- Words/Characters + * ( # (1 start) + * (?> # Atomic Group - Match words with valid breaks + * .{1,16} # 1-N characters + * # Followed by one of 4 prioritized, non-linebreak whitespace + * (?: # break types: + * (?<= [^\S\r\n] ) # 1. - Behind a non-linebreak whitespace + * [^\S\r\n]? # ( optionally accept an extra non-linebreak whitespace ) + * | (?= \r? \n ) # 2. - Ahead a linebreak + * | $ # 3. - EOS + * | [^\S\r\n] # 4. - Accept an extra non-linebreak whitespace + * ) + * ) # End atomic group + * | + * .{1,16} # No valid word breaks, just break on the N'th character + * ) # (1 end) + * (?: \r? \n )? # Optional linebreak after Words/Characters + * | + * # -- Or, Linebreak + * (?: \r? \n | $ ) # Stand alone linebreak or at EOS + * ) + * + * @author Krisztián Ferenczi + * + * @see https://stackoverflow.com/a/20434776/1476819 + */ +final class OutputWrapper +{ + private const TAG_OPEN_REGEX_SEGMENT = '[a-z](?:[^\\\\<>]*+ | \\\\.)*'; + private const TAG_CLOSE_REGEX_SEGMENT = '[a-z][^<>]*+'; + private const URL_PATTERN = 'https?://\S+'; + + public function __construct( + private bool $allowCutUrls = false + ) { + } + + public function wrap(string $text, int $width, string $break = "\n"): string + { + if (!$width) { + return $text; + } + + $tagPattern = sprintf('<(?:(?:%s)|/(?:%s)?)>', self::TAG_OPEN_REGEX_SEGMENT, self::TAG_CLOSE_REGEX_SEGMENT); + $limitPattern = "{1,$width}"; + $patternBlocks = [$tagPattern]; + if (!$this->allowCutUrls) { + $patternBlocks[] = self::URL_PATTERN; + } + $patternBlocks[] = '.'; + $blocks = implode('|', $patternBlocks); + $rowPattern = "(?:$blocks)$limitPattern"; + $pattern = sprintf('#(?:((?>(%1$s)((?<=[^\S\r\n])[^\S\r\n]?|(?=\r?\n)|$|[^\S\r\n]))|(%1$s))(?:\r?\n)?|(?:\r?\n|$))#imux', $rowPattern); + $output = rtrim(preg_replace($pattern, '\\1'.$break, $text), $break); + + return str_replace(' '.$break, $break, $output); + } +} diff --git a/vendor/symfony/console/Helper/ProcessHelper.php b/vendor/symfony/console/Helper/ProcessHelper.php new file mode 100644 index 00000000..26d35a1a --- /dev/null +++ b/vendor/symfony/console/Helper/ProcessHelper.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\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); + } + + 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 00000000..64389c4a --- /dev/null +++ b/vendor/symfony/console/Helper/ProgressBar.php @@ -0,0 +1,618 @@ + + * + * 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 OutputInterface $output; + private int $step = 0; + private int $startingStep = 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 $terminal; + private ?string $previousMessage = null; + private Cursor $cursor; + private array $placeholders = []; + + 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, globally for all instances of ProgressBar. + * + * This method also allow you to override an existing placeholder. + * + * @param string $name The placeholder name (including the delimiter char like %) + * @param callable(ProgressBar):string $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 placeholder formatter for a given name, for this instance only. + * + * @param callable(ProgressBar):string $callable A PHP callable + */ + public function setPlaceholderFormatter(string $name, callable $callable): void + { + $this->placeholders[$name] = $callable; + } + + /** + * Gets the placeholder formatter for a given name. + * + * @param string $name The placeholder name (including the delimiter char like %) + */ + public function getPlaceholderFormatter(string $name): ?callable + { + return $this->placeholders[$name] ?? $this::getPlaceholderFormatterDefinition($name); + } + + /** + * 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'): void + { + $this->messages[$name] = $message; + } + + public function getMessage(string $name = 'message'): string + { + 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 (0 === $this->step || $this->step === $this->startingStep) { + return 0; + } + + return round((time() - $this->startTime) / ($this->step - $this->startingStep) * $this->max); + } + + public function getRemaining(): float + { + if (!$this->step) { + return 0; + } + + return round((time() - $this->startTime) / ($this->step - $this->startingStep) * ($this->max - $this->step)); + } + + public function setBarWidth(int $size): void + { + $this->barWidth = max(1, $size); + } + + public function getBarWidth(): int + { + return $this->barWidth; + } + + public function setBarCharacter(string $char): void + { + $this->barChar = $char; + } + + public function getBarCharacter(): string + { + return $this->barChar ?? ($this->max ? '=' : $this->emptyBarChar); + } + + public function setEmptyBarCharacter(string $char): void + { + $this->emptyBarChar = $char; + } + + public function getEmptyBarCharacter(): string + { + return $this->emptyBarChar; + } + + public function setProgressCharacter(string $char): void + { + $this->progressChar = $char; + } + + public function getProgressCharacter(): string + { + return $this->progressChar; + } + + public function setFormat(string $format): void + { + $this->format = null; + $this->internalFormat = $format; + } + + /** + * Sets the redraw frequency. + * + * @param int|null $freq The frequency in steps + */ + public function setRedrawFrequency(?int $freq): void + { + $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. + * + * @template TKey + * @template TValue + * + * @param iterable $iterable + * @param int|null $max Number of steps to complete the bar (0 if indeterminate), if null it will be inferred from $iterable + * + * @return 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 + * @param int $startAt The starting point of the bar (useful e.g. when resuming a previously started bar) + */ + public function start(int $max = null, int $startAt = 0): void + { + $this->startTime = time(); + $this->step = $startAt; + $this->startingStep = $startAt; + + $startAt > 0 ? $this->setProgress($startAt) : $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): void + { + $this->setProgress($this->step + $step); + } + + /** + * Sets whether to overwrite the progressbar, false for new line. + */ + public function setOverwrite(bool $overwrite): void + { + $this->overwrite = $overwrite; + } + + public function setProgress(int $step): void + { + 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): void + { + $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): void + { + // 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 + { + return match ($this->output->getVerbosity()) { + // OutputInterface::VERBOSITY_QUIET: display is disabled anyway + OutputInterface::VERBOSITY_VERBOSE => $this->max ? self::FORMAT_VERBOSE : self::FORMAT_VERBOSE_NOMAX, + OutputInterface::VERBOSITY_VERY_VERBOSE => $this->max ? self::FORMAT_VERY_VERBOSE : self::FORMAT_VERY_VERBOSE_NOMAX, + OutputInterface::VERBOSITY_DEBUG => $this->max ? self::FORMAT_DEBUG : self::FORMAT_DEBUG_NOMAX, + default => $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' => fn (self $bar) => Helper::formatTime(time() - $bar->getStartTime(), 2), + '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(), 2); + }, + '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(), 2); + }, + 'memory' => fn (self $bar) => Helper::formatMemory(memory_get_usage(true)), + 'current' => fn (self $bar) => str_pad($bar->getProgress(), $bar->getStepWidth(), ' ', \STR_PAD_LEFT), + 'max' => fn (self $bar) => $bar->getMaxSteps(), + 'percent' => fn (self $bar) => 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->getPlaceholderFormatter($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(fn ($subLine) => 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 00000000..79d47643 --- /dev/null +++ b/vendor/symfony/console/Helper/ProgressIndicator.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\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 OutputInterface $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; + + $format ??= $this->determineBestFormat(); + $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. + * + * @return void + */ + public function setMessage(?string $message) + { + $this->message = $message; + + $this->display(); + } + + /** + * Starts the indicator output. + * + * @return void + */ + 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. + * + * @return void + */ + 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. + * + * @return void + */ + 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. + * + * @return void + */ + 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(): void + { + 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 + { + return match ($this->output->getVerbosity()) { + // OutputInterface::VERBOSITY_QUIET: display is disabled anyway + OutputInterface::VERBOSITY_VERBOSE => $this->output->isDecorated() ? 'verbose' : 'verbose_no_ansi', + OutputInterface::VERBOSITY_VERY_VERBOSE, + OutputInterface::VERBOSITY_DEBUG => $this->output->isDecorated() ? 'very_verbose' : 'very_verbose_no_ansi', + default => $this->output->isDecorated() ? 'normal' : 'normal_no_ansi', + }; + } + + /** + * Overwrites a previous message to the output. + */ + private function overwrite(string $message): void + { + 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' => fn (self $indicator) => $indicator->indicatorValues[$indicator->indicatorCurrent % \count($indicator->indicatorValues)], + 'message' => fn (self $indicator) => $indicator->message, + 'elapsed' => fn (self $indicator) => Helper::formatTime(time() - $indicator->startTime, 2), + 'memory' => fn () => 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 00000000..f32813c6 --- /dev/null +++ b/vendor/symfony/console/Helper/QuestionHelper.php @@ -0,0 +1,612 @@ + + * + * 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 = fn () => $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; + } + } + + public function getName(): string + { + return 'question'; + } + + /** + * Prevents usage of stty. + * + * @return void + */ + 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) { + $isBlocked = stream_get_meta_data($inputStream)['blocked'] ?? true; + + if (!$isBlocked) { + stream_set_blocking($inputStream, true); + } + + $ret = $this->readInput($inputStream, $question); + + if (!$isBlocked) { + stream_set_blocking($inputStream, false); + } + + 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(''); // add EOL to the question + $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($validator, $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. + * + * @return void + */ + 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. + * + * @return void + */ + 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), + fn ($match) => '' === $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 (str_starts_with(__FILE__, 'phar:')) { + $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 (4095 === \strlen($value)) { + $errOutput = $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output; + $errOutput->warning('The value was possibly truncated by your shell or terminal emulator'); + } + + 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('shell_exec')) { + return self::$stdinIsInteractive = true; + } + + return self::$stdinIsInteractive = (bool) shell_exec('stty 2> '.('\\' === \DIRECTORY_SEPARATOR ? 'NUL' : '/dev/null')); + } + + /** + * 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 00000000..8ebc8437 --- /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 +{ + /** + * @return void + */ + 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); + } + + /** + * @return void + */ + 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 00000000..3c189580 --- /dev/null +++ b/vendor/symfony/console/Helper/Table.php @@ -0,0 +1,926 @@ + + * + * 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 const DISPLAY_ORIENTATION_DEFAULT = 'default'; + private const DISPLAY_ORIENTATION_HORIZONTAL = 'horizontal'; + private const DISPLAY_ORIENTATION_VERTICAL = 'vertical'; + + private ?string $headerTitle = null; + private ?string $footerTitle = null; + private array $headers = []; + private array $rows = []; + private array $effectiveColumnWidths = []; + private int $numberOfColumns; + private OutputInterface $output; + private TableStyle $style; + private array $columnStyles = []; + private array $columnWidths = []; + private array $columnMaxWidths = []; + private bool $rendered = false; + private string $displayOrientation = self::DISPLAY_ORIENTATION_DEFAULT; + + private static array $styles; + + public function __construct(OutputInterface $output) + { + $this->output = $output; + + self::$styles ??= self::initStyles(); + + $this->setStyle('default'); + } + + /** + * Sets a style definition. + * + * @return void + */ + 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 ($headers && !\is_array($headers[0])) { + $headers = [$headers]; + } + + $this->headers = $headers; + + return $this; + } + + /** + * @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->displayOrientation = $horizontal ? self::DISPLAY_ORIENTATION_HORIZONTAL : self::DISPLAY_ORIENTATION_DEFAULT; + + return $this; + } + + /** + * @return $this + */ + public function setVertical(bool $vertical = true): static + { + $this->displayOrientation = $vertical ? self::DISPLAY_ORIENTATION_VERTICAL : self::DISPLAY_ORIENTATION_DEFAULT; + + 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 | + * +---------------+-----------------------+------------------+ + * + * @return void + */ + public function render() + { + $divider = new TableSeparator(); + $isCellWithColspan = static fn ($cell) => $cell instanceof TableCell && $cell->getColspan() >= 2; + + $horizontal = self::DISPLAY_ORIENTATION_HORIZONTAL === $this->displayOrientation; + $vertical = self::DISPLAY_ORIENTATION_VERTICAL === $this->displayOrientation; + + $rows = []; + if ($horizontal) { + 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 ($isCellWithColspan($rows[$i][0])) { + // Noop, there is a "title" + } else { + $rows[$i][] = null; + } + } + } + } elseif ($vertical) { + $formatter = $this->output->getFormatter(); + $maxHeaderLength = array_reduce($this->headers[0] ?? [], static fn ($max, $header) => max($max, Helper::width(Helper::removeDecoration($formatter, $header))), 0); + + foreach ($this->rows as $row) { + if ($row instanceof TableSeparator) { + continue; + } + + if ($rows) { + $rows[] = [$divider]; + } + + $containsColspan = false; + foreach ($row as $cell) { + if ($containsColspan = $isCellWithColspan($cell)) { + break; + } + } + + $headers = $this->headers[0] ?? []; + $maxRows = max(\count($headers), \count($row)); + for ($i = 0; $i < $maxRows; ++$i) { + $cell = (string) ($row[$i] ?? ''); + + $parts = explode("\n", $cell); + foreach ($parts as $idx => $part) { + if ($headers && !$containsColspan) { + if (0 === $idx) { + $rows[] = [sprintf( + '%s: %s', + str_pad($headers[$i] ?? '', $maxHeaderLength, ' ', \STR_PAD_LEFT), + $part + )]; + } else { + $rows[] = [sprintf( + '%s %s', + str_pad('', $maxHeaderLength, ' ', \STR_PAD_LEFT), + $part + )]; + } + } elseif ('' !== $cell) { + $rows[] = [$part]; + } + } + } + } + } else { + $rows = array_merge($this->headers, [$divider], $this->rows); + } + + $this->calculateNumberOfColumns($rows); + + $rowGroups = $this->buildTableRows($rows); + $this->calculateColumnsWidth($rowGroups); + + $isHeader = !$horizontal; + $isFirstRow = $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( + self::SEPARATOR_TOP, + $hasTitle ? $this->headerTitle : null, + $hasTitle ? $this->style->getHeaderTitleFormat() : null + ); + $hasTitle = false; + $isHeaderSeparatorRendered = true; + } + + if ($isFirstRow) { + $this->renderRowSeparator( + $horizontal ? self::SEPARATOR_TOP : self::SEPARATOR_TOP_BOTTOM, + $hasTitle ? $this->headerTitle : null, + $hasTitle ? $this->style->getHeaderTitleFormat() : null + ); + $isFirstRow = false; + $hasTitle = false; + } + + if ($vertical) { + $isHeader = false; + $isFirstRow = false; + } + + if ($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): void + { + if (!$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): void + { + $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 (str_contains($content, '')) { + $content = str_replace('', '', $content); + $width -= 3; + } + if (str_contains($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): void + { + $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 (!str_contains($cell ?? '', "\n")) { + continue; + } + $escaped = implode("\n", array_map(OutputFormatter::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 ($this->rows) { + ++$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 (str_contains($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): iterable + { + $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): void + { + 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 = mb_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(): void + { + $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 00000000..394b2bc9 --- /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 00000000..9419dcb4 --- /dev/null +++ b/vendor/symfony/console/Helper/TableCellStyle.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\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(), + fn ($key) => \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 00000000..97d07726 --- /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 00000000..e541c531 --- /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 00000000..bbad98e7 --- /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 00000000..59f9217e --- /dev/null +++ b/vendor/symfony/console/Input/ArgvInput.php @@ -0,0 +1,370 @@ + + * + * 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 ??= $_SERVER['argv'] ?? []; + + // strip the application name + array_shift($argv); + + $this->tokens = $argv; + + parent::__construct($definition); + } + + /** + * @return void + */ + protected function setTokens(array $tokens) + { + $this->tokens = $tokens; + } + + /** + * @return void + */ + 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): void + { + $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): void + { + $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): void + { + $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): void + { + $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): void + { + if (!$this->definition->hasShortcut($shortcut)) { + throw new RuntimeException(sprintf('The "-%s" option does not exist.', $shortcut)); + } + + $this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value); + } + + /** + * Adds a long option value. + * + * @throws RuntimeException When option given doesn't exist + */ + private function addLongOption(string $name, mixed $value): void + { + 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; + } + } + + 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; + } + + 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; + } + + 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 00000000..355de61d --- /dev/null +++ b/vendor/symfony/console/Input/ArrayInput.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 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); + } + + public function getFirstArgument(): ?string + { + foreach ($this->parameters as $param => $value) { + if ($param && \is_string($param) && '-' === $param[0]) { + continue; + } + + return $value; + } + + return null; + } + + 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; + } + + 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); + } + + /** + * @return void + */ + 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): void + { + 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): void + { + 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): void + { + 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 00000000..c7959a6c --- /dev/null +++ b/vendor/symfony/console/Input/Input.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\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; + /** @var resource */ + 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(); + } + } + + /** + * @return void + */ + public function bind(InputDefinition $definition) + { + $this->arguments = []; + $this->options = []; + $this->definition = $definition; + + $this->parse(); + } + + /** + * Processes command line arguments. + * + * @return void + */ + abstract protected function parse(); + + /** + * @return void + */ + public function validate() + { + $definition = $this->definition; + $givenArguments = $this->arguments; + + $missingArguments = array_filter(array_keys($definition->getArguments()), fn ($argument) => !\array_key_exists($argument, $givenArguments) && $definition->getArgument($argument)->isRequired()); + + if (\count($missingArguments) > 0) { + throw new RuntimeException(sprintf('Not enough arguments (missing: "%s").', implode(', ', $missingArguments))); + } + } + + public function isInteractive(): bool + { + return $this->interactive; + } + + /** + * @return void + */ + public function setInteractive(bool $interactive) + { + $this->interactive = $interactive; + } + + public function getArguments(): array + { + return array_merge($this->definition->getArgumentDefaults(), $this->arguments); + } + + 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(); + } + + /** + * @return void + */ + 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; + } + + public function hasArgument(string $name): bool + { + return $this->definition->hasArgument($name); + } + + public function getOptions(): array + { + return array_merge($this->definition->getOptionDefaults(), $this->options); + } + + 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(); + } + + /** + * @return void + */ + 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; + } + + 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); + } + + /** + * @param resource $stream + * + * @return void + */ + public function setStream($stream) + { + $this->stream = $stream; + } + + /** + * @return resource + */ + 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 00000000..5cb15148 --- /dev/null +++ b/vendor/symfony/console/Input/InputArgument.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\Input; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Completion\CompletionInput; +use Symfony\Component\Console\Completion\CompletionSuggestions; +use Symfony\Component\Console\Completion\Suggestion; +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 array|\Closure $suggestedValues; + private string $description; + + /** + * @param string $name The argument name + * @param int|null $mode The argument mode: a bit mask of self::REQUIRED, self::OPTIONAL and self::IS_ARRAY + * @param string $description A description text + * @param string|bool|int|float|array|null $default The default value (for self::OPTIONAL mode only) + * @param array|\Closure(CompletionInput,CompletionSuggestions):list $suggestedValues The values used for input completion + * + * @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, \Closure|array $suggestedValues = []) + { + 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->suggestedValues = $suggestedValues; + + $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. + * + * @return void + * + * @throws LogicException When incorrect default value is given + */ + public function setDefault(string|bool|int|float|array $default = null) + { + if (1 > \func_num_args()) { + trigger_deprecation('symfony/console', '6.2', 'Calling "%s()" without any arguments is deprecated, pass null explicitly instead.', __METHOD__); + } + 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; + } + + public function hasCompletion(): bool + { + return [] !== $this->suggestedValues; + } + + /** + * Adds suggestions to $suggestions for the current completion input. + * + * @see Command::complete() + */ + public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void + { + $values = $this->suggestedValues; + if ($values instanceof \Closure && !\is_array($values = $values($input))) { + throw new LogicException(sprintf('Closure for argument "%s" must return an array. Got "%s".', $this->name, get_debug_type($values))); + } + if ($values) { + $suggestions->suggestValues($values); + } + } + + /** + * 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 00000000..0ad27b45 --- /dev/null +++ b/vendor/symfony/console/Input/InputAwareInterface.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\Console\Input; + +/** + * InputAwareInterface should be implemented by classes that depends on the + * Console Input. + * + * @author Wouter J + */ +interface InputAwareInterface +{ + /** + * Sets the Console Input. + * + * @return void + */ + 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 00000000..b7162d77 --- /dev/null +++ b/vendor/symfony/console/Input/InputDefinition.php @@ -0,0 +1,416 @@ + + * + * 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 ?InputArgument $lastArrayArgument = null; + private ?InputArgument $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. + * + * @return void + */ + 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 + * + * @return void + */ + 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 + * + * @return void + */ + public function addArguments(?array $arguments = []) + { + if (null !== $arguments) { + foreach ($arguments as $argument) { + $this->addArgument($argument); + } + } + } + + /** + * @return void + * + * @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 + * + * @return void + */ + 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 + * + * @return void + */ + public function addOptions(array $options = []) + { + foreach ($options as $option) { + $this->addOption($option); + } + } + + /** + * @return void + * + * @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 00000000..aaed5fd0 --- /dev/null +++ b/vendor/symfony/console/Input/InputInterface.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\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 + * + * @method string __toString() Returns a stringified representation of the args passed to the command. + * InputArguments MUST be escaped as well as the InputOption values passed to the command. + */ +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. + * + * @return void + * + * @throws RuntimeException + */ + public function bind(InputDefinition $definition); + + /** + * Validates the input. + * + * @return void + * + * @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. + * + * @return void + * + * @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. + * + * @return void + * + * @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. + * + * @return void + */ + 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 00000000..fdf88dcc --- /dev/null +++ b/vendor/symfony/console/Input/InputOption.php @@ -0,0 +1,255 @@ + + * + * 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\Command\Command; +use Symfony\Component\Console\Completion\CompletionInput; +use Symfony\Component\Console\Completion\CompletionSuggestions; +use Symfony\Component\Console\Completion\Suggestion; +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 array|\Closure $suggestedValues; + 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) + * @param array|\Closure(CompletionInput,CompletionSuggestions):list $suggestedValues The values used for input completion + * + * @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, array|\Closure $suggestedValues = []) + { + 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; + $this->suggestedValues = $suggestedValues; + + if ($suggestedValues && !$this->acceptValue()) { + throw new LogicException('Cannot set suggested values if the option does not accept a value.'); + } + 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); + } + + /** + * @return void + */ + public function setDefault(string|bool|int|float|array $default = null) + { + if (1 > \func_num_args()) { + trigger_deprecation('symfony/console', '6.2', 'Calling "%s()" without any arguments is deprecated, pass null explicitly instead.', __METHOD__); + } + 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; + } + + public function hasCompletion(): bool + { + return [] !== $this->suggestedValues; + } + + /** + * Adds suggestions to $suggestions for the current completion input. + * + * @see Command::complete() + */ + public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void + { + $values = $this->suggestedValues; + if ($values instanceof \Closure && !\is_array($values = $values($input))) { + throw new LogicException(sprintf('Closure for option "%s" must return an array. Got "%s".', $this->name, get_debug_type($values))); + } + if ($values) { + $suggestions->suggestValues($values); + } + } + + /** + * 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 00000000..4b95fcb1 --- /dev/null +++ b/vendor/symfony/console/Input/StreamableInputInterface.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\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 + * + * @return void + */ + 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 00000000..82bd2144 --- /dev/null +++ b/vendor/symfony/console/Input/StringInput.php @@ -0,0 +1,87 @@ + + * + * 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 +{ + /** + * @deprecated since Symfony 6.1 + */ + 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 00000000..0138f8f0 --- /dev/null +++ b/vendor/symfony/console/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-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/console/Logger/ConsoleLogger.php b/vendor/symfony/console/Logger/ConsoleLogger.php new file mode 100644 index 00000000..fddef50c --- /dev/null +++ b/vendor/symfony/console/Logger/ConsoleLogger.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\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 OutputInterface $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; + } + + 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(\DateTimeInterface::RFC3339); + } elseif (\is_object($val)) { + $replacements["{{$key}}"] = '[object '.$val::class.']'; + } else { + $replacements["{{$key}}"] = '['.\gettype($val).']'; + } + } + + return strtr($message, $replacements); + } +} diff --git a/vendor/symfony/console/Messenger/RunCommandContext.php b/vendor/symfony/console/Messenger/RunCommandContext.php new file mode 100644 index 00000000..2ee5415c --- /dev/null +++ b/vendor/symfony/console/Messenger/RunCommandContext.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\Messenger; + +/** + * @author Kevin Bond + */ +final class RunCommandContext +{ + public function __construct( + public readonly RunCommandMessage $message, + public readonly int $exitCode, + public readonly string $output, + ) { + } +} diff --git a/vendor/symfony/console/Messenger/RunCommandMessage.php b/vendor/symfony/console/Messenger/RunCommandMessage.php new file mode 100644 index 00000000..b530c438 --- /dev/null +++ b/vendor/symfony/console/Messenger/RunCommandMessage.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\Console\Messenger; + +use Symfony\Component\Console\Exception\RunCommandFailedException; + +/** + * @author Kevin Bond + */ +class RunCommandMessage implements \Stringable +{ + /** + * @param bool $throwOnFailure If the command has a non-zero exit code, throw {@see RunCommandFailedException} + * @param bool $catchExceptions @see Application::setCatchExceptions() + */ + public function __construct( + public readonly string $input, + public readonly bool $throwOnFailure = true, + public readonly bool $catchExceptions = false, + ) { + } + + public function __toString(): string + { + return $this->input; + } +} diff --git a/vendor/symfony/console/Messenger/RunCommandMessageHandler.php b/vendor/symfony/console/Messenger/RunCommandMessageHandler.php new file mode 100644 index 00000000..14f9c176 --- /dev/null +++ b/vendor/symfony/console/Messenger/RunCommandMessageHandler.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 Symfony\Component\Console\Messenger; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Exception\RunCommandFailedException; +use Symfony\Component\Console\Input\StringInput; +use Symfony\Component\Console\Output\BufferedOutput; + +/** + * @author Kevin Bond + */ +final class RunCommandMessageHandler +{ + public function __construct(private readonly Application $application) + { + } + + public function __invoke(RunCommandMessage $message): RunCommandContext + { + $input = new StringInput($message->input); + $output = new BufferedOutput(); + + $this->application->setCatchExceptions($message->catchExceptions); + + try { + $exitCode = $this->application->run($input, $output); + } catch (\Throwable $e) { + throw new RunCommandFailedException($e, new RunCommandContext($message, Command::FAILURE, $output->fetch())); + } + + if ($message->throwOnFailure && Command::SUCCESS !== $exitCode) { + throw new RunCommandFailedException(sprintf('Command "%s" exited with code "%s".', $message->input, $exitCode), new RunCommandContext($message, $exitCode, $output->fetch())); + } + + return new RunCommandContext($message, $exitCode, $output->fetch()); + } +} diff --git a/vendor/symfony/console/Output/AnsiColorMode.php b/vendor/symfony/console/Output/AnsiColorMode.php new file mode 100644 index 00000000..5f9f744f --- /dev/null +++ b/vendor/symfony/console/Output/AnsiColorMode.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\Output; + +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * @author Fabien Potencier + * @author Julien Boudry + */ +enum AnsiColorMode +{ + /* + * Classical 4-bit Ansi colors, including 8 classical colors and 8 bright color. Output syntax is "ESC[${foreGroundColorcode};${backGroundColorcode}m" + * Must be compatible with all terminals and it's the minimal version supported. + */ + case Ansi4; + + /* + * 8-bit Ansi colors (240 different colors + 16 duplicate color codes, ensuring backward compatibility). + * Output syntax is: "ESC[38;5;${foreGroundColorcode};48;5;${backGroundColorcode}m" + * Should be compatible with most terminals. + */ + case Ansi8; + + /* + * 24-bit Ansi colors (RGB). + * Output syntax is: "ESC[38;2;${foreGroundColorcodeRed};${foreGroundColorcodeGreen};${foreGroundColorcodeBlue};48;2;${backGroundColorcodeRed};${backGroundColorcodeGreen};${backGroundColorcodeBlue}m" + * May be compatible with many modern terminals. + */ + case Ansi24; + + /** + * Converts an RGB hexadecimal color to the corresponding Ansi code. + */ + public function convertFromHexToAnsiColorCode(string $hexColor): string + { + $hexColor = str_replace('#', '', $hexColor); + + if (3 === \strlen($hexColor)) { + $hexColor = $hexColor[0].$hexColor[0].$hexColor[1].$hexColor[1].$hexColor[2].$hexColor[2]; + } + + if (6 !== \strlen($hexColor)) { + throw new InvalidArgumentException(sprintf('Invalid "#%s" color.', $hexColor)); + } + + $color = hexdec($hexColor); + + $r = ($color >> 16) & 255; + $g = ($color >> 8) & 255; + $b = $color & 255; + + return match ($this) { + self::Ansi4 => (string) $this->convertFromRGB($r, $g, $b), + self::Ansi8 => '8;5;'.((string) $this->convertFromRGB($r, $g, $b)), + self::Ansi24 => sprintf('8;2;%d;%d;%d', $r, $g, $b) + }; + } + + private function convertFromRGB(int $r, int $g, int $b): int + { + return match ($this) { + self::Ansi4 => $this->degradeHexColorToAnsi4($r, $g, $b), + self::Ansi8 => $this->degradeHexColorToAnsi8($r, $g, $b), + default => throw new InvalidArgumentException("RGB cannot be converted to {$this->name}.") + }; + } + + private function degradeHexColorToAnsi4(int $r, int $g, int $b): int + { + return round($b / 255) << 2 | (round($g / 255) << 1) | round($r / 255); + } + + /** + * Inspired from https://github.com/ajalt/colormath/blob/e464e0da1b014976736cf97250063248fc77b8e7/colormath/src/commonMain/kotlin/com/github/ajalt/colormath/model/Ansi256.kt code (MIT license). + */ + private function degradeHexColorToAnsi8(int $r, int $g, int $b): int + { + if ($r === $g && $g === $b) { + if ($r < 8) { + return 16; + } + + if ($r > 248) { + return 231; + } + + return (int) round(($r - 8) / 247 * 24) + 232; + } else { + return 16 + + (36 * (int) round($r / 255 * 5)) + + (6 * (int) round($g / 255 * 5)) + + (int) round($b / 255 * 5); + } + } +} diff --git a/vendor/symfony/console/Output/BufferedOutput.php b/vendor/symfony/console/Output/BufferedOutput.php new file mode 100644 index 00000000..ef5099bf --- /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; + } + + /** + * @return void + */ + 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 00000000..c1eb7cd1 --- /dev/null +++ b/vendor/symfony/console/Output/ConsoleOutput.php @@ -0,0 +1,165 @@ + + * + * 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 OutputInterface $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()); + } + + /** + * @return void + */ + public function setDecorated(bool $decorated) + { + parent::setDecorated($decorated); + $this->stderr->setDecorated($decorated); + } + + /** + * @return void + */ + public function setFormatter(OutputFormatterInterface $formatter) + { + parent::setFormatter($formatter); + $this->stderr->setFormatter($formatter); + } + + /** + * @return void + */ + public function setVerbosity(int $level) + { + parent::setVerbosity($level); + $this->stderr->setVerbosity($level); + } + + public function getErrorOutput(): OutputInterface + { + return $this->stderr; + } + + /** + * @return void + */ + 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 00000000..9c0049c8 --- /dev/null +++ b/vendor/symfony/console/Output/ConsoleOutputInterface.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\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; + + /** + * @return void + */ + 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 00000000..21c4a44a --- /dev/null +++ b/vendor/symfony/console/Output/ConsoleSectionOutput.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\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 $terminal; + private int $maxHeight = 0; + + /** + * @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(); + } + + /** + * Defines a maximum number of lines for this section. + * + * When more lines are added, the section will automatically scroll to the + * end (i.e. remove the first lines to comply with the max height). + */ + public function setMaxHeight(int $maxHeight): void + { + // when changing max height, clear output of current section and redraw again with the new height + $previousMaxHeight = $this->maxHeight; + $this->maxHeight = $maxHeight; + $existingContent = $this->popStreamContentUntilCurrentSection($previousMaxHeight ? min($previousMaxHeight, $this->lines) : $this->lines); + + parent::doWrite($this->getVisibleContent(), false); + parent::doWrite($existingContent, false); + } + + /** + * 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 + * + * @return void + */ + public function clear(int $lines = null) + { + if (empty($this->content) || !$this->isDecorated()) { + return; + } + + if ($lines) { + array_splice($this->content, -$lines); + } else { + $lines = $this->lines; + $this->content = []; + } + + $this->lines -= $lines; + + parent::doWrite($this->popStreamContentUntilCurrentSection($this->maxHeight ? min($this->maxHeight, $lines) : $lines), false); + } + + /** + * Overwrites the previous output with a new message. + * + * @return void + */ + public function overwrite(string|iterable $message) + { + $this->clear(); + $this->writeln($message); + } + + public function getContent(): string + { + return implode('', $this->content); + } + + public function getVisibleContent(): string + { + if (0 === $this->maxHeight) { + return $this->getContent(); + } + + return implode('', \array_slice($this->content, -$this->maxHeight)); + } + + /** + * @internal + */ + public function addContent(string $input, bool $newline = true): int + { + $width = $this->terminal->getWidth(); + $lines = explode(\PHP_EOL, $input); + $linesAdded = 0; + $count = \count($lines) - 1; + foreach ($lines as $i => $lineContent) { + // re-add the line break (that has been removed in the above `explode()` for + // - every line that is not the last line + // - if $newline is required, also add it to the last line + if ($i < $count || $newline) { + $lineContent .= \PHP_EOL; + } + + // skip line if there is no text (or newline for that matter) + if ('' === $lineContent) { + continue; + } + + // For the first line, check if the previous line (last entry of `$this->content`) + // needs to be continued (i.e. does not end with a line break). + if (0 === $i + && (false !== $lastLine = end($this->content)) + && !str_ends_with($lastLine, \PHP_EOL) + ) { + // deduct the line count of the previous line + $this->lines -= (int) ceil($this->getDisplayLength($lastLine) / $width) ?: 1; + // concatenate previous and new line + $lineContent = $lastLine.$lineContent; + // replace last entry of `$this->content` with the new expanded line + array_splice($this->content, -1, 1, $lineContent); + } else { + // otherwise just add the new content + $this->content[] = $lineContent; + } + + $linesAdded += (int) ceil($this->getDisplayLength($lineContent) / $width) ?: 1; + } + + $this->lines += $linesAdded; + + return $linesAdded; + } + + /** + * @internal + */ + public function addNewLineOfInputSubmit(): void + { + $this->content[] = \PHP_EOL; + ++$this->lines; + } + + /** + * @return void + */ + protected function doWrite(string $message, bool $newline) + { + // Simulate newline behavior for consistent output formatting, avoiding extra logic + if (!$newline && str_ends_with($message, \PHP_EOL)) { + $message = substr($message, 0, -\strlen(\PHP_EOL)); + $newline = true; + } + + if (!$this->isDecorated()) { + parent::doWrite($message, $newline); + + return; + } + + // Check if the previous line (last entry of `$this->content`) needs to be continued + // (i.e. does not end with a line break). In which case, it needs to be erased first. + $linesToClear = $deleteLastLine = ($lastLine = end($this->content) ?: '') && !str_ends_with($lastLine, \PHP_EOL) ? 1 : 0; + + $linesAdded = $this->addContent($message, $newline); + + if ($lineOverflow = $this->maxHeight > 0 && $this->lines > $this->maxHeight) { + // on overflow, clear the whole section and redraw again (to remove the first lines) + $linesToClear = $this->maxHeight; + } + + $erasedContent = $this->popStreamContentUntilCurrentSection($linesToClear); + + if ($lineOverflow) { + // redraw existing lines of the section + $previousLinesOfSection = \array_slice($this->content, $this->lines - $this->maxHeight, $this->maxHeight - $linesAdded); + parent::doWrite(implode('', $previousLinesOfSection), false); + } + + // if the last line was removed, re-print its content together with the new content. + // otherwise, just print the new content. + parent::doWrite($deleteLastLine ? $lastLine.$message : $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->maxHeight ? min($section->lines, $section->maxHeight) : $section->lines; + if ('' !== $sectionContent = $section->getVisibleContent()) { + if (!str_ends_with($sectionContent, \PHP_EOL)) { + $sectionContent .= \PHP_EOL; + } + $erasedContent[] = $sectionContent; + } + } + + 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 00000000..f3aa15b1 --- /dev/null +++ b/vendor/symfony/console/Output/NullOutput.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\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 NullOutputFormatter $formatter; + + /** + * @return void + */ + public function setFormatter(OutputFormatterInterface $formatter) + { + // do nothing + } + + public function getFormatter(): OutputFormatterInterface + { + // to comply with the interface we must return a OutputFormatterInterface + return $this->formatter ??= new NullOutputFormatter(); + } + + /** + * @return void + */ + public function setDecorated(bool $decorated) + { + // do nothing + } + + public function isDecorated(): bool + { + return false; + } + + /** + * @return void + */ + public function setVerbosity(int $level) + { + // do nothing + } + + public function getVerbosity(): int + { + return self::VERBOSITY_QUIET; + } + + public function isQuiet(): bool + { + return true; + } + + public function isVerbose(): bool + { + return false; + } + + public function isVeryVerbose(): bool + { + return false; + } + + public function isDebug(): bool + { + return false; + } + + /** + * @return void + */ + public function writeln(string|iterable $messages, int $options = self::OUTPUT_NORMAL) + { + // do nothing + } + + /** + * @return void + */ + 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 00000000..3a06311a --- /dev/null +++ b/vendor/symfony/console/Output/Output.php @@ -0,0 +1,155 @@ + + * + * 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 OutputFormatterInterface $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); + } + + /** + * @return void + */ + public function setFormatter(OutputFormatterInterface $formatter) + { + $this->formatter = $formatter; + } + + public function getFormatter(): OutputFormatterInterface + { + return $this->formatter; + } + + /** + * @return void + */ + public function setDecorated(bool $decorated) + { + $this->formatter->setDecorated($decorated); + } + + public function isDecorated(): bool + { + return $this->formatter->isDecorated(); + } + + /** + * @return void + */ + public function setVerbosity(int $level) + { + $this->verbosity = $level; + } + + public function getVerbosity(): int + { + return $this->verbosity; + } + + public function isQuiet(): bool + { + return self::VERBOSITY_QUIET === $this->verbosity; + } + + public function isVerbose(): bool + { + return self::VERBOSITY_VERBOSE <= $this->verbosity; + } + + public function isVeryVerbose(): bool + { + return self::VERBOSITY_VERY_VERBOSE <= $this->verbosity; + } + + public function isDebug(): bool + { + return self::VERBOSITY_DEBUG <= $this->verbosity; + } + + /** + * @return void + */ + public function writeln(string|iterable $messages, int $options = self::OUTPUT_NORMAL) + { + $this->write($messages, true, $options); + } + + /** + * @return void + */ + 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. + * + * @return void + */ + 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 00000000..19a81790 --- /dev/null +++ b/vendor/symfony/console/Output/OutputInterface.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 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 bool $newline Whether to add a newline + * @param int $options A bitmask of options (one of the OUTPUT or VERBOSITY constants), + * 0 is considered the same as self::OUTPUT_NORMAL | self::VERBOSITY_NORMAL + * + * @return void + */ + 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 int $options A bitmask of options (one of the OUTPUT or VERBOSITY constants), + * 0 is considered the same as self::OUTPUT_NORMAL | self::VERBOSITY_NORMAL + * + * @return void + */ + public function writeln(string|iterable $messages, int $options = 0); + + /** + * Sets the verbosity of the output. + * + * @param self::VERBOSITY_* $level + * + * @return void + */ + public function setVerbosity(int $level); + + /** + * Gets the current verbosity of the output. + * + * @return self::VERBOSITY_* + */ + 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. + * + * @return void + */ + public function setDecorated(bool $decorated); + + /** + * Gets the decorated flag. + */ + public function isDecorated(): bool; + + /** + * @return void + */ + 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 00000000..da5eefb6 --- /dev/null +++ b/vendor/symfony/console/Output/StreamOutput.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\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 +{ + /** @var resource */ + 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; + + $decorated ??= $this->hasColorSupport(); + + parent::__construct($verbosity, $decorated, $formatter); + } + + /** + * Gets the stream attached to this StreamOutput instance. + * + * @return resource + */ + public function getStream() + { + return $this->stream; + } + + /** + * @return void + */ + 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 00000000..b00445ec --- /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; + } + + /** + * @return void + */ + 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 00000000..e449ff68 --- /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 00000000..40eab242 --- /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 00000000..26896bb5 --- /dev/null +++ b/vendor/symfony/console/Question/Question.php @@ -0,0 +1,291 @@ + + * + * 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 fn () => $values; + } elseif ($values instanceof \Traversable) { + $callback = static function () use ($values) { + static $valueCache; + + return $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 (1 > \func_num_args()) { + trigger_deprecation('symfony/console', '6.2', 'Calling "%s()" without any arguments is deprecated, pass null explicitly instead.', __METHOD__); + } + if ($this->hidden && null !== $callback) { + throw new LogicException('A hidden question cannot use the autocompleter.'); + } + + $this->autocompleterCallback = null === $callback ? null : $callback(...); + + return $this; + } + + /** + * Sets a validator for the question. + * + * @return $this + */ + public function setValidator(callable $validator = null): static + { + if (1 > \func_num_args()) { + trigger_deprecation('symfony/console', '6.2', 'Calling "%s()" without any arguments is deprecated, pass null explicitly instead.', __METHOD__); + } + $this->validator = null === $validator ? null : $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(...); + + 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; + } + + /** + * @return bool + */ + 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 00000000..92f70e71 --- /dev/null +++ b/vendor/symfony/console/README.md @@ -0,0 +1,27 @@ +Console Component +================= + +The Console component eases the creation of beautiful and testable command line +interfaces. + +Sponsor +------- + +Help Symfony by [sponsoring][1] 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/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 00000000..0d76eacc --- /dev/null +++ b/vendor/symfony/console/Resources/completion.bash @@ -0,0 +1,94 @@ +# 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 the default completion for shell redirect operators. + for w in '>' '>>' '&>' '<'; do + if [[ $w = "${COMP_WORDS[COMP_CWORD-1]}" ]]; then + compopt -o filenames + COMPREPLY=($(compgen -f -- "${COMP_WORDS[COMP_CWORD]}")) + return 0 + fi + done + + # 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" "-a{{ 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/Resources/completion.fish b/vendor/symfony/console/Resources/completion.fish new file mode 100644 index 00000000..1c34292a --- /dev/null +++ b/vendor/symfony/console/Resources/completion.fish @@ -0,0 +1,29 @@ +# 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 + +function _sf_{{ COMMAND_NAME }} + set sf_cmd (commandline -o) + set c (count (commandline -oc)) + + set completecmd "$sf_cmd[1]" "_complete" "--no-interaction" "-sfish" "-a{{ VERSION }}" + + for i in $sf_cmd + if [ $i != "" ] + set completecmd $completecmd "-i$i" + end + end + + set completecmd $completecmd "-c$c" + + set sfcomplete ($completecmd) + + for i in $sfcomplete + echo $i + end +end + +complete -c '{{ COMMAND_NAME }}' -a '(_sf_{{ COMMAND_NAME }})' -f diff --git a/vendor/symfony/console/Resources/completion.zsh b/vendor/symfony/console/Resources/completion.zsh new file mode 100644 index 00000000..ff76fe5f --- /dev/null +++ b/vendor/symfony/console/Resources/completion.zsh @@ -0,0 +1,82 @@ +#compdef {{ COMMAND_NAME }} + +# 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 + +# +# zsh completions for {{ COMMAND_NAME }} +# +# References: +# - https://github.com/spf13/cobra/blob/master/zsh_completions.go +# - https://github.com/symfony/symfony/blob/5.4/src/Symfony/Component/Console/Resources/completion.bash +# +_sf_{{ COMMAND_NAME }}() { + local lastParam flagPrefix requestComp out comp + local -a completions + + # The user could have moved the cursor backwards on the command-line. + # We need to trigger completion from the $CURRENT location, so we need + # to truncate the command-line ($words) up to the $CURRENT location. + # (We cannot use $CURSOR as its value does not work when a command is an alias.) + words=("${=words[1,CURRENT]}") lastParam=${words[-1]} + + # For zsh, when completing a flag with an = (e.g., {{ COMMAND_NAME }} -n=) + # completions must be prefixed with the flag + setopt local_options BASH_REMATCH + if [[ "${lastParam}" =~ '-.*=' ]]; then + # We are dealing with a flag with an = + flagPrefix="-P ${BASH_REMATCH}" + fi + + # Prepare the command to obtain completions + requestComp="${words[0]} ${words[1]} _complete --no-interaction -szsh -a{{ VERSION }} -c$((CURRENT-1))" i="" + 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 + i="${i}-i${w} " + fi + done + + # Ensure at least 1 input + if [ "${i}" = "" ]; then + requestComp="${requestComp} -i\" \"" + else + requestComp="${requestComp} ${i}" + fi + + # Use eval to handle any environment variables and such + out=$(eval ${requestComp} 2>/dev/null) + + while IFS='\n' read -r comp; do + if [ -n "$comp" ]; then + # If requested, completions are returned with a description. + # The description is preceded by a TAB character. + # For zsh's _describe, we need to use a : instead of a TAB. + # We first need to escape any : as part of the completion itself. + comp=${comp//:/\\:} + local tab=$(printf '\t') + comp=${comp//$tab/:} + completions+=${comp} + fi + done < <(printf "%s\n" "${out[@]}") + + # Let inbuilt _describe handle completions + eval _describe "completions" completions $flagPrefix + return $? +} + +compdef _sf_{{ COMMAND_NAME }} {{ COMMAND_NAME }} diff --git a/vendor/symfony/console/SignalRegistry/SignalMap.php b/vendor/symfony/console/SignalRegistry/SignalMap.php new file mode 100644 index 00000000..de419bda --- /dev/null +++ b/vendor/symfony/console/SignalRegistry/SignalMap.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\Console\SignalRegistry; + +/** + * @author Grégoire Pineau + */ +class SignalMap +{ + private static array $map; + + public static function getSignalName(int $signal): ?string + { + if (!\extension_loaded('pcntl')) { + return null; + } + + if (!isset(self::$map)) { + $r = new \ReflectionExtension('pcntl'); + $c = $r->getConstants(); + $map = array_filter($c, fn ($k) => str_starts_with($k, 'SIG') && !str_starts_with($k, 'SIG_'), \ARRAY_FILTER_USE_KEY); + self::$map = array_flip($map); + } + + return self::$map[$signal] ?? null; + } +} diff --git a/vendor/symfony/console/SignalRegistry/SignalRegistry.php b/vendor/symfony/console/SignalRegistry/SignalRegistry.php new file mode 100644 index 00000000..ef2e5f04 --- /dev/null +++ b/vendor/symfony/console/SignalRegistry/SignalRegistry.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\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 + { + return \function_exists('pcntl_signal'); + } + + /** + * @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 00000000..4f0b5ba3 --- /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 00000000..ddfa8dec --- /dev/null +++ b/vendor/symfony/console/Style/OutputStyle.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 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 OutputInterface $output; + + public function __construct(OutputInterface $output) + { + $this->output = $output; + } + + /** + * @return void + */ + 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); + } + + /** + * @return void + */ + public function write(string|iterable $messages, bool $newline = false, int $type = self::OUTPUT_NORMAL) + { + $this->output->write($messages, $newline, $type); + } + + /** + * @return void + */ + public function writeln(string|iterable $messages, int $type = self::OUTPUT_NORMAL) + { + $this->output->writeln($messages, $type); + } + + /** + * @return void + */ + public function setVerbosity(int $level) + { + $this->output->setVerbosity($level); + } + + public function getVerbosity(): int + { + return $this->output->getVerbosity(); + } + + /** + * @return void + */ + public function setDecorated(bool $decorated) + { + $this->output->setDecorated($decorated); + } + + public function isDecorated(): bool + { + return $this->output->isDecorated(); + } + + /** + * @return void + */ + public function setFormatter(OutputFormatterInterface $formatter) + { + $this->output->setFormatter($formatter); + } + + public function getFormatter(): OutputFormatterInterface + { + return $this->output->getFormatter(); + } + + public function isQuiet(): bool + { + return $this->output->isQuiet(); + } + + public function isVerbose(): bool + { + return $this->output->isVerbose(); + } + + public function isVeryVerbose(): bool + { + return $this->output->isVeryVerbose(); + } + + public function isDebug(): bool + { + return $this->output->isDebug(); + } + + /** + * @return OutputInterface + */ + 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 00000000..e25a65bd --- /dev/null +++ b/vendor/symfony/console/Style/StyleInterface.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\Console\Style; + +/** + * Output style helpers. + * + * @author Kevin Bond + */ +interface StyleInterface +{ + /** + * Formats a command title. + * + * @return void + */ + public function title(string $message); + + /** + * Formats a section title. + * + * @return void + */ + public function section(string $message); + + /** + * Formats a list. + * + * @return void + */ + public function listing(array $elements); + + /** + * Formats informational text. + * + * @return void + */ + public function text(string|array $message); + + /** + * Formats a success result bar. + * + * @return void + */ + public function success(string|array $message); + + /** + * Formats an error result bar. + * + * @return void + */ + public function error(string|array $message); + + /** + * Formats an warning result bar. + * + * @return void + */ + public function warning(string|array $message); + + /** + * Formats a note admonition. + * + * @return void + */ + public function note(string|array $message); + + /** + * Formats a caution admonition. + * + * @return void + */ + public function caution(string|array $message); + + /** + * Formats a table. + * + * @return void + */ + 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). + * + * @return void + */ + public function newLine(int $count = 1); + + /** + * Starts the progress output. + * + * @return void + */ + public function progressStart(int $max = 0); + + /** + * Advances the progress output X steps. + * + * @return void + */ + public function progressAdvance(int $step = 1); + + /** + * Finishes the progress output. + * + * @return void + */ + public function progressFinish(); +} diff --git a/vendor/symfony/console/Style/SymfonyStyle.php b/vendor/symfony/console/Style/SymfonyStyle.php new file mode 100644 index 00000000..43d2edf5 --- /dev/null +++ b/vendor/symfony/console/Style/SymfonyStyle.php @@ -0,0 +1,514 @@ + + * + * 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\OutputWrapper; +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\ConsoleSectionOutput; +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 InputInterface $input; + private OutputInterface $output; + private SymfonyQuestionHelper $questionHelper; + private ProgressBar $progressBar; + private int $lineLength; + private TrimmedBufferOutput $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. + * + * @return void + */ + 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(); + } + + /** + * @return void + */ + 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(); + } + + /** + * @return void + */ + 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(); + } + + /** + * @return void + */ + public function listing(array $elements) + { + $this->autoPrependText(); + $elements = array_map(fn ($element) => sprintf(' * %s', $element), $elements); + + $this->writeln($elements); + $this->newLine(); + } + + /** + * @return void + */ + 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. + * + * @return void + */ + public function comment(string|array $message) + { + $this->block($message, null, null, ' // ', false, false); + } + + /** + * @return void + */ + public function success(string|array $message) + { + $this->block($message, 'OK', 'fg=black;bg=green', ' ', true); + } + + /** + * @return void + */ + public function error(string|array $message) + { + $this->block($message, 'ERROR', 'fg=white;bg=red', ' ', true); + } + + /** + * @return void + */ + public function warning(string|array $message) + { + $this->block($message, 'WARNING', 'fg=black;bg=yellow', ' ', true); + } + + /** + * @return void + */ + public function note(string|array $message) + { + $this->block($message, 'NOTE', 'fg=yellow', ' ! '); + } + + /** + * Formats an info message. + * + * @return void + */ + public function info(string|array $message) + { + $this->block($message, 'INFO', 'fg=green', ' ', true); + } + + /** + * @return void + */ + public function caution(string|array $message) + { + $this->block($message, 'CAUTION', 'fg=white;bg=red', ' ! ', true); + } + + /** + * @return void + */ + public function table(array $headers, array $rows) + { + $this->createTable() + ->setHeaders($headers) + ->setRows($rows) + ->render() + ; + + $this->newLine(); + } + + /** + * Formats a horizontal table. + * + * @return void + */ + 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() + * + * @return void + */ + 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]); + } + + public function ask(string $question, string $default = null, callable $validator = null): mixed + { + $question = new Question($question, $default); + $question->setValidator($validator); + + return $this->askQuestion($question); + } + + public function askHidden(string $question, callable $validator = null): mixed + { + $question = new Question($question); + + $question->setHidden(true); + $question->setValidator($validator); + + return $this->askQuestion($question); + } + + public function confirm(string $question, bool $default = true): bool + { + return $this->askQuestion(new ConfirmationQuestion($question, $default)); + } + + public function choice(string $question, array $choices, mixed $default = null, bool $multiSelect = false): mixed + { + if (null !== $default) { + $values = array_flip($choices); + $default = $values[$default] ?? $default; + } + + $questionChoice = new ChoiceQuestion($question, $choices, $default); + $questionChoice->setMultiselect($multiSelect); + + return $this->askQuestion($questionChoice); + } + + /** + * @return void + */ + public function progressStart(int $max = 0) + { + $this->progressBar = $this->createProgressBar($max); + $this->progressBar->start(); + } + + /** + * @return void + */ + public function progressAdvance(int $step = 1) + { + $this->getProgressBar()->advance($step); + } + + /** + * @return void + */ + public function progressFinish() + { + $this->getProgressBar()->finish(); + $this->newLine(2); + unset($this->progressBar); + } + + 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() + * + * @template TKey + * @template TValue + * + * @param iterable $iterable + * @param int|null $max Number of steps to complete the bar (0 if indeterminate), if null it will be inferred from $iterable + * + * @return iterable + */ + 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()) { + if ($this->output instanceof ConsoleSectionOutput) { + // add the new line of the `return` to submit the input to ConsoleSectionOutput, because ConsoleSectionOutput is holding all it's lines. + // this is relevant when a `ConsoleSectionOutput::clear` is called. + $this->output->addNewLineOfInputSubmit(); + } + $this->newLine(); + $this->bufferedOutput->write("\n"); + } + + return $answer; + } + + /** + * @return void + */ + 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); + } + } + + /** + * @return void + */ + 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); + } + } + + /** + * @return void + */ + 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 ($fetched && !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 = Helper::width($type); + $lineIndentation = str_repeat(' ', $indentLength); + } + + // wrap and add newlines for each element + $outputWrapper = new OutputWrapper(); + foreach ($messages as $key => $message) { + if ($escape) { + $message = OutputFormatter::escape($message); + } + + $lines = array_merge( + $lines, + explode(\PHP_EOL, $outputWrapper->wrap( + $message, + $this->lineLength - $prefixLength - $indentLength, + \PHP_EOL + )) + ); + + 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 00000000..3eda0376 --- /dev/null +++ b/vendor/symfony/console/Terminal.php @@ -0,0 +1,236 @@ + + * + * 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\AnsiColorMode; + +class Terminal +{ + public const DEFAULT_COLOR_MODE = AnsiColorMode::Ansi4; + + private static ?AnsiColorMode $colorMode = null; + private static ?int $width = null; + private static ?int $height = null; + private static ?bool $stty = null; + + /** + * About Ansi color types: https://en.wikipedia.org/wiki/ANSI_escape_code#Colors + * For more information about true color support with terminals https://github.com/termstandard/colors/. + */ + public static function getColorMode(): AnsiColorMode + { + // Use Cache from previous run (or user forced mode) + if (null !== self::$colorMode) { + return self::$colorMode; + } + + // Try with $COLORTERM first + if (\is_string($colorterm = getenv('COLORTERM'))) { + $colorterm = strtolower($colorterm); + + if (str_contains($colorterm, 'truecolor')) { + self::setColorMode(AnsiColorMode::Ansi24); + + return self::$colorMode; + } + + if (str_contains($colorterm, '256color')) { + self::setColorMode(AnsiColorMode::Ansi8); + + return self::$colorMode; + } + } + + // Try with $TERM + if (\is_string($term = getenv('TERM'))) { + $term = strtolower($term); + + if (str_contains($term, 'truecolor')) { + self::setColorMode(AnsiColorMode::Ansi24); + + return self::$colorMode; + } + + if (str_contains($term, '256color')) { + self::setColorMode(AnsiColorMode::Ansi8); + + return self::$colorMode; + } + } + + self::setColorMode(self::DEFAULT_COLOR_MODE); + + return self::$colorMode; + } + + /** + * Force a terminal color mode rendering. + */ + public static function setColorMode(?AnsiColorMode $colorMode): void + { + self::$colorMode = $colorMode; + } + + /** + * 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 shell_exec function is disabled + if (!\function_exists('shell_exec')) { + return false; + } + + return self::$stty = (bool) shell_exec('stty 2> '.('\\' === \DIRECTORY_SEPARATOR ? 'NUL' : '/dev/null')); + } + + private static function initDimensions(): void + { + if ('\\' === \DIRECTORY_SEPARATOR) { + $ansicon = getenv('ANSICON'); + if (false !== $ansicon && preg_match('/^(\d+)x(\d+)(?: \((\d+)x(\d+)\))?$/', trim($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(): void + { + if ($sttyString = self::getSttyColumns()) { + if (preg_match('/rows.(\d+);.columns.(\d+);/is', $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/is', $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']); + } + + private static function readFromProcess(string|array $command): ?string + { + if (!\function_exists('proc_open')) { + return null; + } + + $descriptorspec = [ + 1 => ['pipe', 'w'], + 2 => ['pipe', 'w'], + ]; + + $cp = \function_exists('sapi_windows_cp_set') ? sapi_windows_cp_get() : 0; + + $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); + + if ($cp) { + sapi_windows_cp_set($cp); + } + + return $info; + } +} diff --git a/vendor/symfony/console/Tester/ApplicationTester.php b/vendor/symfony/console/Tester/ApplicationTester.php new file mode 100644 index 00000000..58aee54d --- /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 $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 00000000..a90fe52e --- /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 $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 00000000..2ff813b7 --- /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 $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 00000000..09c6194b --- /dev/null +++ b/vendor/symfony/console/Tester/Constraint/CommandIsSuccessful.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\Tester\Constraint; + +use PHPUnit\Framework\Constraint\Constraint; +use Symfony\Component\Console\Command\Command; + +final class CommandIsSuccessful extends Constraint +{ + public function toString(): string + { + return 'is successful'; + } + + protected function matches($other): bool + { + return Command::SUCCESS === $other; + } + + protected function failureDescription($other): string + { + return 'the command '.$this->toString(); + } + + 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 00000000..1ab7a70a --- /dev/null +++ b/vendor/symfony/console/Tester/TesterTrait.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\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 StreamOutput $output; + private array $inputs = []; + private bool $captureStreamsIndependently = false; + private InputInterface $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): void + { + $this->captureStreamsIndependently = $options['capture_stderr_separately'] ?? false; + 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->setValue($this->output, $errorOutput); + + $reflectedParent = $reflectedOutput->getParentClass(); + $streamProperty = $reflectedParent->getProperty('stream'); + $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 00000000..1610f734 --- /dev/null +++ b/vendor/symfony/console/composer.json @@ -0,0 +1,55 @@ +{ + "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.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^5.4|^6.0|^7.0" + }, + "require-dev": { + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^5.4|^6.0|^7.0", + "symfony/messenger": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/stopwatch": "^5.4|^6.0|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0", + "psr/log": "^1|^2|^3" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "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/CHANGELOG.md b/vendor/symfony/deprecation-contracts/CHANGELOG.md new file mode 100644 index 00000000..7932e261 --- /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 00000000..0ed3a246 --- /dev/null +++ b/vendor/symfony/deprecation-contracts/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/deprecation-contracts/README.md b/vendor/symfony/deprecation-contracts/README.md new file mode 100644 index 00000000..9814864c --- /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 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 00000000..c6d02d87 --- /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.1" + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-main": "3.4-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 00000000..2d56512b --- /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/polyfill-ctype/Ctype.php b/vendor/symfony/polyfill-ctype/Ctype.php new file mode 100644 index 00000000..ba75a2c9 --- /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 00000000..7536caea --- /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 00000000..b144d03c --- /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 00000000..d54524b3 --- /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 00000000..ab2f8611 --- /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 00000000..e5c978f1 --- /dev/null +++ b/vendor/symfony/polyfill-ctype/composer.json @@ -0,0 +1,41 @@ +{ + "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": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "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 00000000..5373f168 --- /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 00000000..6e3afce6 --- /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 00000000..f55d92c5 --- /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 00000000..a9ea03c7 --- /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 00000000..b8c07867 --- /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 00000000..c00d4e9e --- /dev/null +++ b/vendor/symfony/polyfill-intl-grapheme/composer.json @@ -0,0 +1,38 @@ +{ + "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": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + } +} diff --git a/vendor/symfony/polyfill-intl-normalizer/LICENSE b/vendor/symfony/polyfill-intl-normalizer/LICENSE new file mode 100644 index 00000000..6e3afce6 --- /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 00000000..81704ab3 --- /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 00000000..b9b762e8 --- /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 00000000..0fdfc890 --- /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 00000000..5a3e8e09 --- /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 00000000..ec90f36e --- /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 00000000..15749028 --- /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 00000000..3608e5c0 --- /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 00000000..e36d1a94 --- /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 00000000..2c4de2c8 --- /dev/null +++ b/vendor/symfony/polyfill-intl-normalizer/composer.json @@ -0,0 +1,39 @@ +{ + "name": "symfony/polyfill-intl-normalizer", + "type": "library", + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "keywords": ["polyfill", "shim", "compatibility", "portable", "intl", "normalizer"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=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": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + } +} diff --git a/vendor/symfony/polyfill-mbstring/LICENSE b/vendor/symfony/polyfill-mbstring/LICENSE new file mode 100644 index 00000000..6e3afce6 --- /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 00000000..2e0b9694 --- /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 00000000..478b40da --- /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 00000000..512bba0b --- /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 00000000..fac60b08 --- /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 00000000..2a8f6e73 --- /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 00000000..ecf1a035 --- /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 00000000..2f9fb5b4 --- /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 00000000..943e5029 --- /dev/null +++ b/vendor/symfony/polyfill-mbstring/composer.json @@ -0,0 +1,41 @@ +{ + "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": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + } +} diff --git a/vendor/symfony/service-contracts/Attribute/Required.php b/vendor/symfony/service-contracts/Attribute/Required.php new file mode 100644 index 00000000..9df85118 --- /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 00000000..d98e1dfd --- /dev/null +++ b/vendor/symfony/service-contracts/Attribute/SubscribedService.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\Contracts\Service\Attribute; + +use Symfony\Contracts\Service\ServiceSubscriberInterface; +use Symfony\Contracts\Service\ServiceSubscriberTrait; + +/** + * For use as the return value for {@see ServiceSubscriberInterface}. + * + * @example new SubscribedService('http_client', HttpClientInterface::class, false, new Target('githubApi')) + * + * 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 +{ + /** @var object[] */ + public array $attributes; + + /** + * @param string|null $key The key to use for the service + * @param class-string|null $type The service class + * @param bool $nullable Whether the service is optional + * @param object|object[] $attributes One or more dependency injection attributes to use + */ + public function __construct( + public ?string $key = null, + public ?string $type = null, + public bool $nullable = false, + array|object $attributes = [], + ) { + $this->attributes = \is_array($attributes) ? $attributes : [$attributes]; + } +} diff --git a/vendor/symfony/service-contracts/CHANGELOG.md b/vendor/symfony/service-contracts/CHANGELOG.md new file mode 100644 index 00000000..7932e261 --- /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 00000000..7536caea --- /dev/null +++ b/vendor/symfony/service-contracts/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/service-contracts/README.md b/vendor/symfony/service-contracts/README.md new file mode 100644 index 00000000..42841a57 --- /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 00000000..a4f389b0 --- /dev/null +++ b/vendor/symfony/service-contracts/ResetInterface.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; + +/** + * 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 +{ + /** + * @return void + */ + public function reset(); +} diff --git a/vendor/symfony/service-contracts/ServiceLocatorTrait.php b/vendor/symfony/service-contracts/ServiceLocatorTrait.php new file mode 100644 index 00000000..b62ec3e5 --- /dev/null +++ b/vendor/symfony/service-contracts/ServiceLocatorTrait.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\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 array $factories + */ + public function __construct(array $factories) + { + $this->factories = $factories; + } + + public function has(string $id): bool + { + return isset($this->factories[$id]); + } + + 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]); + } + } + + 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 00000000..2e71f00c --- /dev/null +++ b/vendor/symfony/service-contracts/ServiceProviderInterface.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\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 + * + * @template-covariant T of mixed + */ +interface ServiceProviderInterface extends ContainerInterface +{ + /** + * @return T + */ + public function get(string $id): mixed; + + public function has(string $id): bool; + + /** + * 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 array 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 00000000..3da19169 --- /dev/null +++ b/vendor/symfony/service-contracts/ServiceSubscriberInterface.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\Contracts\Service; + +use Symfony\Contracts\Service\Attribute\SubscribedService; + +/** + * 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 (or {@see SubscribedService} objects) 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'] + * + * additionally, an array of {@see SubscribedService}'s can be returned: + * + * * [new SubscribedService('logger', Psr\Log\LoggerInterface::class)] + * * [new SubscribedService(type: Psr\Log\LoggerInterface::class, nullable: true)] + * * [new SubscribedService('http_client', HttpClientInterface::class, attributes: new Target('githubApi'))] + * + * @return string[]|SubscribedService[] 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 00000000..f3b450cd --- /dev/null +++ b/vendor/symfony/service-contracts/ServiceSubscriberTrait.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\Service; + +use Psr\Container\ContainerInterface; +use Symfony\Contracts\Service\Attribute\Required; +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; + + 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)); + } + + /* @var SubscribedService $attribute */ + $attribute = $attribute->newInstance(); + $attribute->key ??= self::class.'::'.$method->name; + $attribute->type ??= $returnType instanceof \ReflectionNamedType ? $returnType->getName() : (string) $returnType; + $attribute->nullable = $returnType->allowsNull(); + + if ($attribute->attributes) { + $services[] = $attribute; + } else { + $services[$attribute->key] = ($attribute->nullable ? '?' : '').$attribute->type; + } + } + + return $services; + } + + #[Required] + public function setContainer(ContainerInterface $container): ?ContainerInterface + { + $ret = null; + if (method_exists(get_parent_class(self::class) ?: '', __FUNCTION__)) { + $ret = parent::setContainer($container); + } + + $this->container = $container; + + return $ret; + } +} diff --git a/vendor/symfony/service-contracts/Test/ServiceLocatorTest.php b/vendor/symfony/service-contracts/Test/ServiceLocatorTest.php new file mode 100644 index 00000000..07d12b4a --- /dev/null +++ b/vendor/symfony/service-contracts/Test/ServiceLocatorTest.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\Service\Test; + +class_alias(ServiceLocatorTestCase::class, ServiceLocatorTest::class); + +if (false) { + /** + * @deprecated since PHPUnit 9.6 + */ + class ServiceLocatorTest + { + } +} diff --git a/vendor/symfony/service-contracts/Test/ServiceLocatorTestCase.php b/vendor/symfony/service-contracts/Test/ServiceLocatorTestCase.php new file mode 100644 index 00000000..583f72a7 --- /dev/null +++ b/vendor/symfony/service-contracts/Test/ServiceLocatorTestCase.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 ServiceLocatorTestCase extends TestCase +{ + protected function getServiceLocator(array $factories): ContainerInterface + { + return new class($factories) implements ContainerInterface { + use ServiceLocatorTrait; + }; + } + + public function testHas() + { + $locator = $this->getServiceLocator([ + 'foo' => fn () => 'bar', + 'bar' => fn () => 'baz', + fn () => 'dummy', + ]); + + $this->assertTrue($locator->has('foo')); + $this->assertTrue($locator->has('bar')); + $this->assertFalse($locator->has('dummy')); + } + + public function testGet() + { + $locator = $this->getServiceLocator([ + 'foo' => fn () => 'bar', + 'bar' => fn () => '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 00000000..a64188b5 --- /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.1", + "psr/container": "^2.0" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "autoload": { + "psr-4": { "Symfony\\Contracts\\Service\\": "" }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-main": "3.4-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 00000000..10231a21 --- /dev/null +++ b/vendor/symfony/string/AbstractString.php @@ -0,0 +1,702 @@ + + * + * 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 $string = ''; + protected ?bool $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 ??= 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 fn ($t, $m) => throw new InvalidArgumentException($m)); + + try { + if (false === $chunks = preg_split($delimiter, $this->string, $limit, $flags)) { + throw new RuntimeException('Splitting failed with error: '.preg_last_error_msg()); + } + } 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; + } + + try { + $b->string = mb_convert_encoding($this->string, $toEncoding, 'UTF-8'); + } catch (\ValueError $e) { + if (!\function_exists('iconv')) { + throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e); + } + + $b->string = iconv('UTF-8', $toEncoding, $this->string); + } + + 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) { // don't use is_iterable(), it's slow + 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) { // don't use is_iterable(), it's slow + 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 00000000..df7265f3 --- /dev/null +++ b/vendor/symfony/string/AbstractUnicodeString.php @@ -0,0 +1,590 @@ + + * + * 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', 'ʼn', 'ǰ', 'ΐ', 'ΰ', 'եւ', 'ẖ', 'ẗ', 'ẘ', 'ẙ', 'aʾ', 'ss', 'ὐ', 'ὒ', 'ὔ', 'ὖ', 'ἀι', 'ἁι', 'ἂι', 'ἃι', 'ἄι', 'ἅι', 'ἆι', 'ἇι', 'ἀι', 'ἁι', 'ἂι', 'ἃι', 'ἄι', 'ἅι', 'ἆι', 'ἇι', 'ἠι', 'ἡι', 'ἢι', 'ἣι', 'ἤι', 'ἥι', 'ἦι', 'ἧι', 'ἠι', 'ἡι', 'ἢι', 'ἣι', 'ἤι', 'ἥι', 'ἦι', 'ἧι', 'ὠι', 'ὡι', 'ὢι', 'ὣι', 'ὤι', 'ὥι', 'ὦι', 'ὧι', 'ὠι', 'ὡι', 'ὢι', 'ὣι', 'ὤι', 'ὥι', 'ὦι', 'ὧι', 'ὰι', 'αι', 'άι', 'ᾶ', 'ᾶι', 'αι', 'ὴι', 'ηι', 'ήι', 'ῆ', 'ῆι', 'ηι', 'ῒ', 'ῖ', 'ῗ', 'ῢ', 'ῤ', 'ῦ', 'ῧ', 'ὼι', 'ωι', 'ώι', 'ῶ', 'ῶι', 'ωι', 'ff', 'fi', 'fl', 'ffi', 'ffl', 'st', 'st', 'մն', 'մե', 'մի', 'վն', 'մխ']; + + // 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 array $transliterators = []; + private static array $tableZero; + private static array $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] ??= \Transliterator::create($rule)) { + if ('any-latin/bgn' === $rule) { + $rule = 'any-latin'; + $transliterator = 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) { + static $i = 0; + + 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 fn ($t, $m) => throw new InvalidArgumentException($m)); + + try { + if (false === $match($regexp.'u', $this->string, $matches, $flags | \PREG_UNMATCHED_AS_NULL, $offset)) { + throw new RuntimeException('Matching failed with error: '.preg_last_error_msg()); + } + } 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 fn ($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 && str_ends_with($k, '_ERROR')) { + 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 fn (array $m): string => 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 (str_contains($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; + } + + 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; + } + } + } + + 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 00000000..f5050ddf --- /dev/null +++ b/vendor/symfony/string/ByteString.php @@ -0,0 +1,485 @@ + + * + * 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 ??= 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 fn ($t, $m) => throw new InvalidArgumentException($m)); + + try { + if (false === $match($regexp, $this->string, $matches, $flags | \PREG_UNMATCHED_AS_NULL, $offset)) { + throw new RuntimeException('Matching failed with error: '.preg_last_error_msg()); + } + } 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 fn ($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 && str_ends_with($k, '_ERROR')) { + 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 ??= \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 fn ($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 00000000..31a3b54d --- /dev/null +++ b/vendor/symfony/string/CHANGELOG.md @@ -0,0 +1,40 @@ +CHANGELOG +========= + +6.2 +--- + + * Add support for emoji in `AsciiSlugger` + +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 00000000..f5c900fb --- /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 ??= \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 00000000..36197865 --- /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 00000000..6aa586bc --- /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 00000000..77cb091f --- /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 00000000..feea7f66 --- /dev/null +++ b/vendor/symfony/string/Inflector/EnglishInflector.php @@ -0,0 +1,541 @@ + + * + * 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 vowel + // 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']], + + // codes (code) + ['sedoc', 5, false, true, 'code'], + + // selfies (selfie) + ['seifles', 7, true, true, 'selfie'], + + // zombies (zombie) + ['seibmoz', 7, true, true, 'zombie'], + + // movies (movie) + ['seivom', 6, true, true, 'movie'], + + // names (name) + ['seman', 5, true, false, 'name'], + + // 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'], + + // statuses (status) + ['sesutats', 8, true, true, 'status'], + + // 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']], + + // status (status) + ['sutats', 6, true, true, 'status'], + + // 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 vowel + // 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'], + + // icons (icon) + ['noc', 3, true, true, 'cons'], + + // 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'], + + // status (status) + ['sutats', 6, true, true, ['status', 'statuses']], + + // 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', + + // equipment + 'tnempiuqe', + + // feedback + 'kcabdeef', + + // fish + 'hsif', + + // health + 'htlaeh', + + // history + 'yrotsih', + + // info + 'ofni', + + // information + 'noitamrofni', + + // money + 'yenom', + + // moose + 'esoom', + + // series + 'seires', + + // sheep + 'peehs', + + // species + 'seiceps', + + // traffic + 'ciffart', + ]; + + 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) { + $nextIsVowel = str_contains('aeiou', $lowerPluralRev[$j]); + + if (!$map[2] && $nextIsVowel) { + // suffix may not succeed a vowel but next char is one + break; + } + + if (!$map[3] && !$nextIsVowel) { + // 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]; + } + + 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) { + $nextIsVowel = str_contains('aeiou', $lowerSingularRev[$j]); + + if (!$map[2] && $nextIsVowel) { + // suffix may not succeed a vowel but next char is one + break; + } + + if (!$map[3] && !$nextIsVowel) { + // 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 00000000..955abbf4 --- /dev/null +++ b/vendor/symfony/string/Inflector/FrenchInflector.php @@ -0,0 +1,151 @@ + + * + * 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'; + + 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]; + } + + 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 00000000..67f28340 --- /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 00000000..f37c76b5 --- /dev/null +++ b/vendor/symfony/string/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2019-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/string/LazyString.php b/vendor/symfony/string/LazyString.php new file mode 100644 index 00000000..0341beaf --- /dev/null +++ b/vendor/symfony/string/LazyString.php @@ -0,0 +1,145 @@ + + * + * 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): string { + static $value; + + if (null !== $arguments) { + if (!\is_callable($callback)) { + $callback[0] = $callback[0](); + $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 === $e::class && __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 (str_contains($r->name, '{closure}') || !$class = $r->getClosureCalledClass()) { + 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 00000000..9c7e1e19 --- /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 00000000..8314c8fd --- /dev/null +++ b/vendor/symfony/string/Resources/data/wcswidth_table_wide.php @@ -0,0 +1,1155 @@ + + * + * 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 ??= ''; + + 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 00000000..9f7eba94 --- /dev/null +++ b/vendor/symfony/string/Slugger/AsciiSlugger.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\String\Slugger; + +use Symfony\Component\Intl\Transliterator\EmojiTransliterator; +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'], + ]; + private bool|string $emoji = false; + + /** + * 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; + } + + public function setLocale(string $locale): void + { + $this->defaultLocale = $locale; + } + + public function getLocale(): string + { + return $this->defaultLocale; + } + + /** + * @param bool|string $emoji true will use the same locale, + * false will disable emoji, + * and a string to use a specific locale + */ + public function withEmoji(bool|string $emoji = true): static + { + if (false !== $emoji && !class_exists(EmojiTransliterator::class)) { + throw new \LogicException(sprintf('You cannot use the "%s()" method as the "symfony/intl" package is not installed. Try running "composer require symfony/intl".', __METHOD__)); + } + + $new = clone $this; + $new->emoji = $emoji; + + return $new; + } + + public function slug(string $string, string $separator = '-', string $locale = null): AbstractUnicodeString + { + $locale ??= $this->defaultLocale; + + $transliterator = []; + if ($locale && ('de' === $locale || str_starts_with($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 ($emojiTransliterator = $this->createEmojiTransliterator($locale)) { + $transliterator[] = $emojiTransliterator; + } + + 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 fn ($s) => $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 function createEmojiTransliterator(?string $locale): ?EmojiTransliterator + { + if (\is_string($this->emoji)) { + $locale = $this->emoji; + } elseif (!$this->emoji) { + return null; + } + + while (null !== $locale) { + try { + return EmojiTransliterator::create("emoji-$locale"); + } catch (\IntlException) { + $locale = self::getParentLocale($locale); + } + } + + return 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 00000000..c679ed93 --- /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 00000000..bc09627c --- /dev/null +++ b/vendor/symfony/string/UnicodeString.php @@ -0,0 +1,382 @@ + + * + * 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 = '') + { + if ('' === $string || normalizer_is_normalized($this->string = $string)) { + return; + } + + if (false === $string = normalizer_normalize($string)) { + throw new InvalidArgumentException('Invalid UTF-8 string.'); + } + + $this->string = $string; + } + + public function append(string ...$suffix): static + { + $str = clone $this; + $str->string = $this->string.(1 >= \count($suffix) ? ($suffix[0] ?? '') : implode('', $suffix)); + + if (normalizer_is_normalized($str->string)) { + return $str; + } + + if (false === $string = normalizer_normalize($str->string)) { + throw new InvalidArgumentException('Invalid UTF-8 string.'); + } + + $str->string = $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) { + 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; + + if (normalizer_is_normalized($str->string)) { + return $str; + } + + if (false === $string = normalizer_normalize($str->string)) { + throw new InvalidArgumentException('Invalid UTF-8 string.'); + } + + $str->string = $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; + + if (normalizer_is_normalized($str->string)) { + return $str; + } + + if (false === $string = normalizer_normalize($str->string)) { + throw new InvalidArgumentException('Invalid UTF-8 string.'); + } + + $str->string = $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); + + if (normalizer_is_normalized($str->string)) { + return $str; + } + + if (false === $string = normalizer_normalize($str->string)) { + throw new InvalidArgumentException('Invalid UTF-8 string.'); + } + + $str->string = $string; + + return $str; + } + + public function split(string $delimiter, int $limit = null, int $flags = null): array + { + if (1 > $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(): void + { + 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 00000000..26ce26da --- /dev/null +++ b/vendor/symfony/string/composer.json @@ -0,0 +1,43 @@ +{ + "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.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": "^6.4|^7.0", + "symfony/intl": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^6.4|^7.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.5" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\String\\": "" }, + "files": [ "Resources/functions.php" ], + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev" +}