From 9d76ad6859c2d2e8988d448ede7ee3d219b61ccd Mon Sep 17 00:00:00 2001 From: Josh Pollock Date: Thu, 15 Nov 2018 05:43:36 -0500 Subject: [PATCH] Create a proper job manager for deleting transients etc. (#2802) * #2801 add a job manager * #2801 run queue manager * schedule delete with job manager #2801 * Remove var_dump #2801 * mild cleanup for starting the runner #2801 * add queue run endpoint #2801 * rest api route for running queue now checked with CF PRo api keys #2801 * #2801 more tests for the run queue endpoint and fixed its response * Resolve Conflicts #2802 * Resolve conflicts #2802 * Resolve conflicts #2802 --- caldera-core.php | 22 +- cf2/CalderaFormsV2.php | 43 +- cf2/CalderaFormsV2Contract.php | 71 +- cf2/Jobs/DatabaseConnection.php | 27 + cf2/Jobs/DeleteTransientJob.php | 21 + cf2/Jobs/Job.php | 10 + cf2/Jobs/Scheduler.php | 91 + cf2/Jobs/XJob.php | 20 + .../AuthorizesRestApiRequestWithCfProKeys.php | 64 + cf2/RestApi/Queue/RunQueue.php | 74 + cf2/RestApi/Register.php | 2 + cf2/Services/QueueSchedulerService.php | 30 + cf2/Services/QueueService.php | 25 + cf2/Services/Service.php | 16 + cf2/Services/ServiceContract.php | 43 + cf2/functions.php | 15 + classes/db/tables.php | 60 +- classes/transient.php | 7 +- .../render/build/index.min.js~feature_2771 | 77 - composer.json | 8 +- composer.lock | 254 +- .../cf-pro-client/classes/api/local/files.php | 1 + .../Caldera_Forms_TransientTest.php | 95 + .../ContainerServicesAreLoadedTest.php | 37 + tests/Integration/FunctionsTest.php | 31 + .../Jobs/DatabaseConnectionTest.php | 49 + .../Jobs/DeleteTransientJobTest.php | 25 + tests/Integration/Jobs/SchedulerTest.php | 195 + tests/Integration/RestApi/RunQueueTest.php | 379 ++ .../Service/QueueSchedulerServiceTest.php | 53 + .../Integration/Service/QueueServiceTest.php | 53 + tests/Unit/CalderaFormsV2Test.php | 21 + tests/Util/Mocks/MockJob.php | 27 + tests/Util/Mocks/MockQueueConnection.php | 33 + tests/Util/Mocks/MockService.php | 25 + tests/bootstrap.php | 3 - tests/test-forms-api.php | 8 +- vendor/a5hleyrich/wp-queue/.gitignore | 3 + vendor/a5hleyrich/wp-queue/.scrutinizer.yml | 6 + vendor/a5hleyrich/wp-queue/.travis.yml | 16 + vendor/a5hleyrich/wp-queue/LICENSE.md | 21 + vendor/a5hleyrich/wp-queue/README.md | 112 + vendor/a5hleyrich/wp-queue/composer.json | 28 + vendor/a5hleyrich/wp-queue/phpunit.xml | 16 + .../Connections/ConnectionInterface.php | 65 + .../Connections/DatabaseConnection.php | 259 + .../WP_Queue/Connections/RedisConnection.php | 77 + .../WP_Queue/Connections/SyncConnection.php | 83 + .../a5hleyrich/wp-queue/src/WP_Queue/Cron.php | 194 + .../ConnectionNotFoundException.php | 9 + .../WorkerAttemptsExceededException.php | 9 + .../a5hleyrich/wp-queue/src/WP_Queue/Job.php | 197 + .../wp-queue/src/WP_Queue/Queue.php | 67 + .../wp-queue/src/WP_Queue/QueueManager.php | 66 + .../wp-queue/src/WP_Queue/Worker.php | 71 + vendor/a5hleyrich/wp-queue/src/functions.php | 57 + .../wp-queue/tests/TestDatabaseConnection.php | 139 + .../wp-queue/tests/TestFunctions.php | 23 + vendor/a5hleyrich/wp-queue/tests/TestJob.php | 30 + .../a5hleyrich/wp-queue/tests/TestQueue.php | 62 + .../wp-queue/tests/TestQueueManager.php | 33 + .../a5hleyrich/wp-queue/tests/TestWorker.php | 35 + vendor/composer/ClassLoader.php | 4 +- vendor/composer/autoload_files.php | 3 + vendor/composer/autoload_psr4.php | 4 + vendor/composer/autoload_static.php | 23 + vendor/composer/installed.json | 268 +- vendor/monolog/monolog/CHANGELOG.md | 28 + vendor/monolog/monolog/README.md | 1 - .../doc/02-handlers-formatters-processors.md | 1 + vendor/monolog/monolog/doc/03-utilities.md | 2 + .../monolog/src/Monolog/ErrorHandler.php | 13 +- .../Monolog/Formatter/FluentdFormatter.php | 1 + .../src/Monolog/Formatter/HtmlFormatter.php | 2 +- .../src/Monolog/Formatter/JsonFormatter.php | 18 +- .../src/Monolog/Formatter/LineFormatter.php | 8 +- .../Monolog/Formatter/MongoDBFormatter.php | 6 +- .../Monolog/Formatter/NormalizerFormatter.php | 31 +- .../Monolog/Formatter/WildfireFormatter.php | 4 +- .../src/Monolog/Handler/AbstractHandler.php | 26 +- .../Handler/AbstractProcessingHandler.php | 2 + .../Monolog/Handler/AbstractSyslogHandler.php | 6 +- .../Monolog/Handler/BrowserConsoleHandler.php | 56 +- .../src/Monolog/Handler/BufferHandler.php | 16 +- .../src/Monolog/Handler/ChromePHPHandler.php | 10 +- .../Monolog/Handler/DeduplicationHandler.php | 2 +- .../Monolog/Handler/ElasticSearchHandler.php | 8 +- .../src/Monolog/Handler/ErrorLogHandler.php | 8 +- .../src/Monolog/Handler/FilterHandler.php | 4 +- .../ActivationStrategyInterface.php | 2 +- .../Monolog/Handler/FingersCrossedHandler.php | 46 +- .../src/Monolog/Handler/FirePHPHandler.php | 2 +- .../src/Monolog/Handler/GelfHandler.php | 8 - .../src/Monolog/Handler/GroupHandler.php | 16 +- .../src/Monolog/Handler/HandlerInterface.php | 4 +- .../src/Monolog/Handler/HandlerWrapper.php | 10 +- .../src/Monolog/Handler/HipChatHandler.php | 15 + .../src/Monolog/Handler/IFTTTHandler.php | 8 +- .../src/Monolog/Handler/InsightOpsHandler.php | 62 + .../src/Monolog/Handler/LogEntriesHandler.php | 4 +- .../src/Monolog/Handler/MandrillHandler.php | 2 +- .../src/Monolog/Handler/NewRelicHandler.php | 4 +- .../src/Monolog/Handler/PsrHandler.php | 2 +- .../src/Monolog/Handler/PushoverHandler.php | 6 +- .../src/Monolog/Handler/RavenHandler.php | 12 +- .../src/Monolog/Handler/RollbarHandler.php | 12 + .../Monolog/Handler/RotatingFileHandler.php | 18 +- .../src/Monolog/Handler/Slack/SlackRecord.php | 10 +- .../src/Monolog/Handler/SlackHandler.php | 5 + .../Monolog/Handler/SlackWebhookHandler.php | 5 + .../src/Monolog/Handler/SocketHandler.php | 47 +- .../src/Monolog/Handler/StreamHandler.php | 6 +- .../Monolog/Handler/SwiftMailerHandler.php | 16 +- .../src/Monolog/Handler/SyslogHandler.php | 10 +- .../src/Monolog/Handler/SyslogUdpHandler.php | 12 +- .../src/Monolog/Handler/TestHandler.php | 16 +- .../Handler/WhatFailureGroupHandler.php | 10 + vendor/monolog/monolog/src/Monolog/Logger.php | 239 +- .../src/Monolog/Processor/GitProcessor.php | 2 +- .../Processor/IntrospectionProcessor.php | 2 +- .../src/Monolog/Processor/MemoryProcessor.php | 6 +- .../Monolog/Processor/MercurialProcessor.php | 2 +- .../Monolog/Processor/ProcessIdProcessor.php | 2 +- .../Monolog/Processor/ProcessorInterface.php | 25 + .../Processor/PsrLogMessageProcessor.php | 6 +- .../src/Monolog/Processor/TagProcessor.php | 2 +- .../src/Monolog/Processor/UidProcessor.php | 17 +- .../src/Monolog/Processor/WebProcessor.php | 2 +- .../src/Monolog/ResettableInterface.php | 31 + .../monolog/src/Monolog/SignalHandler.php | 115 + vendor/monolog/monolog/src/Monolog/Utils.php | 25 + .../Formatter/FluentdFormatterTest.php | 4 +- .../Monolog/Formatter/JsonFormatterTest.php | 36 + .../Formatter/NormalizerFormatterTest.php | 60 +- .../Handler/BrowserConsoleHandlerTest.php | 2 +- .../Monolog/Handler/ChromePHPHandlerTest.php | 4 +- .../Handler/FingersCrossedHandlerTest.php | 4 +- .../Monolog/Handler/FirePHPHandlerTest.php | 4 +- .../Monolog/Handler/InsightOpsHandlerTest.php | 80 + .../Handler/RotatingFileHandlerTest.php | 34 + .../Monolog/Handler/Slack/SlackRecordTest.php | 16 +- .../Monolog/Handler/SocketHandlerTest.php | 26 + .../tests/Monolog/Handler/TestHandlerTest.php | 46 + .../Handler/WhatFailureGroupHandlerTest.php | 23 + .../monolog/tests/Monolog/LoggerTest.php | 142 + .../tests/Monolog/SignalHandlerTest.php | 287 + vendor/nesbot/carbon/LICENSE | 19 + vendor/nesbot/carbon/composer.json | 60 + vendor/nesbot/carbon/readme.md | 94 + vendor/nesbot/carbon/src/Carbon/Carbon.php | 4841 +++++++++++++++++ .../carbon/src/Carbon/CarbonInterval.php | 1130 ++++ .../nesbot/carbon/src/Carbon/CarbonPeriod.php | 1445 +++++ .../Exceptions/InvalidDateException.php | 67 + vendor/nesbot/carbon/src/Carbon/Lang/af.php | 31 + vendor/nesbot/carbon/src/Carbon/Lang/ar.php | 31 + .../carbon/src/Carbon/Lang/ar_Shakl.php | 31 + vendor/nesbot/carbon/src/Carbon/Lang/az.php | 40 + vendor/nesbot/carbon/src/Carbon/Lang/bg.php | 31 + vendor/nesbot/carbon/src/Carbon/Lang/bn.php | 38 + .../nesbot/carbon/src/Carbon/Lang/bs_BA.php | 31 + vendor/nesbot/carbon/src/Carbon/Lang/ca.php | 40 + vendor/nesbot/carbon/src/Carbon/Lang/cs.php | 31 + vendor/nesbot/carbon/src/Carbon/Lang/cy.php | 29 + vendor/nesbot/carbon/src/Carbon/Lang/da.php | 31 + vendor/nesbot/carbon/src/Carbon/Lang/de.php | 46 + .../nesbot/carbon/src/Carbon/Lang/dv_MV.php | 31 + vendor/nesbot/carbon/src/Carbon/Lang/el.php | 31 + vendor/nesbot/carbon/src/Carbon/Lang/en.php | 40 + vendor/nesbot/carbon/src/Carbon/Lang/eo.php | 31 + vendor/nesbot/carbon/src/Carbon/Lang/es.php | 36 + vendor/nesbot/carbon/src/Carbon/Lang/et.php | 38 + vendor/nesbot/carbon/src/Carbon/Lang/eu.php | 31 + vendor/nesbot/carbon/src/Carbon/Lang/fa.php | 31 + vendor/nesbot/carbon/src/Carbon/Lang/fi.php | 31 + vendor/nesbot/carbon/src/Carbon/Lang/fo.php | 31 + vendor/nesbot/carbon/src/Carbon/Lang/fr.php | 40 + vendor/nesbot/carbon/src/Carbon/Lang/gl.php | 24 + vendor/nesbot/carbon/src/Carbon/Lang/gu.php | 31 + vendor/nesbot/carbon/src/Carbon/Lang/he.php | 31 + vendor/nesbot/carbon/src/Carbon/Lang/hi.php | 31 + vendor/nesbot/carbon/src/Carbon/Lang/hr.php | 31 + vendor/nesbot/carbon/src/Carbon/Lang/hu.php | 52 + vendor/nesbot/carbon/src/Carbon/Lang/hy.php | 31 + vendor/nesbot/carbon/src/Carbon/Lang/id.php | 31 + vendor/nesbot/carbon/src/Carbon/Lang/is.php | 31 + vendor/nesbot/carbon/src/Carbon/Lang/it.php | 36 + vendor/nesbot/carbon/src/Carbon/Lang/ja.php | 31 + vendor/nesbot/carbon/src/Carbon/Lang/ka.php | 31 + vendor/nesbot/carbon/src/Carbon/Lang/kk.php | 29 + vendor/nesbot/carbon/src/Carbon/Lang/km.php | 31 + vendor/nesbot/carbon/src/Carbon/Lang/ko.php | 31 + vendor/nesbot/carbon/src/Carbon/Lang/lt.php | 38 + vendor/nesbot/carbon/src/Carbon/Lang/lv.php | 47 + vendor/nesbot/carbon/src/Carbon/Lang/mk.php | 24 + vendor/nesbot/carbon/src/Carbon/Lang/mn.php | 62 + vendor/nesbot/carbon/src/Carbon/Lang/ms.php | 31 + vendor/nesbot/carbon/src/Carbon/Lang/my.php | 37 + vendor/nesbot/carbon/src/Carbon/Lang/ne.php | 31 + vendor/nesbot/carbon/src/Carbon/Lang/nl.php | 36 + vendor/nesbot/carbon/src/Carbon/Lang/no.php | 36 + vendor/nesbot/carbon/src/Carbon/Lang/oc.php | 44 + vendor/nesbot/carbon/src/Carbon/Lang/pl.php | 36 + vendor/nesbot/carbon/src/Carbon/Lang/ps.php | 31 + vendor/nesbot/carbon/src/Carbon/Lang/pt.php | 31 + .../nesbot/carbon/src/Carbon/Lang/pt_BR.php | 40 + vendor/nesbot/carbon/src/Carbon/Lang/ro.php | 31 + vendor/nesbot/carbon/src/Carbon/Lang/ru.php | 31 + vendor/nesbot/carbon/src/Carbon/Lang/sh.php | 35 + vendor/nesbot/carbon/src/Carbon/Lang/sk.php | 38 + vendor/nesbot/carbon/src/Carbon/Lang/sl.php | 43 + vendor/nesbot/carbon/src/Carbon/Lang/sq.php | 31 + vendor/nesbot/carbon/src/Carbon/Lang/sr.php | 37 + .../nesbot/carbon/src/Carbon/Lang/sr_Cyrl.php | 43 + .../carbon/src/Carbon/Lang/sr_Cyrl_ME.php | 43 + .../carbon/src/Carbon/Lang/sr_Latn_ME.php | 43 + .../nesbot/carbon/src/Carbon/Lang/sr_ME.php | 12 + vendor/nesbot/carbon/src/Carbon/Lang/sv.php | 31 + vendor/nesbot/carbon/src/Carbon/Lang/sw.php | 31 + vendor/nesbot/carbon/src/Carbon/Lang/th.php | 31 + vendor/nesbot/carbon/src/Carbon/Lang/tr.php | 31 + vendor/nesbot/carbon/src/Carbon/Lang/uk.php | 40 + vendor/nesbot/carbon/src/Carbon/Lang/ur.php | 24 + vendor/nesbot/carbon/src/Carbon/Lang/uz.php | 31 + vendor/nesbot/carbon/src/Carbon/Lang/vi.php | 31 + vendor/nesbot/carbon/src/Carbon/Lang/zh.php | 31 + .../nesbot/carbon/src/Carbon/Lang/zh_TW.php | 31 + .../src/Carbon/Laravel/ServiceProvider.php | 37 + .../nesbot/carbon/src/Carbon/Translator.php | 143 + vendor/nesbot/carbon/src/JsonSerializable.php | 18 + vendor/symfony/polyfill-mbstring/LICENSE | 19 + vendor/symfony/polyfill-mbstring/Mbstring.php | 800 +++ vendor/symfony/polyfill-mbstring/README.md | 13 + .../Resources/unidata/lowerCase.php | 1096 ++++ .../Resources/unidata/titleCaseRegexp.php | 5 + .../Resources/unidata/upperCase.php | 1104 ++++ .../symfony/polyfill-mbstring/bootstrap.php | 58 + .../symfony/polyfill-mbstring/composer.json | 34 + vendor/symfony/translation/.gitignore | 3 + vendor/symfony/translation/CHANGELOG.md | 108 + .../Catalogue/AbstractOperation.php | 157 + .../translation/Catalogue/MergeOperation.php | 55 + .../Catalogue/OperationInterface.php | 77 + .../translation/Catalogue/TargetOperation.php | 69 + .../translation/Command/XliffLintCommand.php | 270 + .../TranslationDataCollector.php | 167 + .../translation/DataCollectorTranslator.php | 165 + .../TranslationDumperPass.php | 44 + .../TranslationExtractorPass.php | 49 + .../DependencyInjection/TranslatorPass.php | 79 + .../translation/Dumper/CsvFileDumper.php | 63 + .../translation/Dumper/DumperInterface.php | 31 + .../symfony/translation/Dumper/FileDumper.php | 113 + .../translation/Dumper/IcuResFileDumper.php | 106 + .../translation/Dumper/IniFileDumper.php | 45 + .../translation/Dumper/JsonFileDumper.php | 40 + .../translation/Dumper/MoFileDumper.php | 82 + .../translation/Dumper/PhpFileDumper.php | 38 + .../translation/Dumper/PoFileDumper.php | 61 + .../translation/Dumper/QtFileDumper.php | 50 + .../translation/Dumper/XliffFileDumper.php | 205 + .../translation/Dumper/YamlFileDumper.php | 62 + .../Exception/ExceptionInterface.php | 21 + .../Exception/InvalidArgumentException.php | 21 + .../Exception/InvalidResourceException.php | 21 + .../translation/Exception/LogicException.php | 21 + .../Exception/NotFoundResourceException.php | 21 + .../Exception/RuntimeException.php | 21 + .../Extractor/AbstractFileExtractor.php | 80 + .../translation/Extractor/ChainExtractor.php | 60 + .../Extractor/ExtractorInterface.php | 38 + .../translation/Extractor/PhpExtractor.php | 256 + .../Extractor/PhpStringTokenParser.php | 142 + .../ChoiceMessageFormatterInterface.php | 30 + .../Formatter/MessageFormatter.php | 48 + .../Formatter/MessageFormatterInterface.php | 30 + .../translation/IdentityTranslator.php | 63 + vendor/symfony/translation/Interval.php | 109 + vendor/symfony/translation/LICENSE | 19 + .../translation/Loader/ArrayLoader.php | 66 + .../translation/Loader/CsvFileLoader.php | 65 + .../symfony/translation/Loader/FileLoader.php | 65 + .../translation/Loader/IcuDatFileLoader.php | 61 + .../translation/Loader/IcuResFileLoader.php | 91 + .../translation/Loader/IniFileLoader.php | 28 + .../translation/Loader/JsonFileLoader.php | 64 + .../translation/Loader/LoaderInterface.php | 38 + .../translation/Loader/MoFileLoader.php | 145 + .../translation/Loader/PhpFileLoader.php | 28 + .../translation/Loader/PoFileLoader.php | 148 + .../translation/Loader/QtFileLoader.php | 77 + .../translation/Loader/XliffFileLoader.php | 314 ++ .../translation/Loader/YamlFileLoader.php | 50 + .../dic/xliff-core/xliff-core-1.2-strict.xsd | 2223 ++++++++ .../schema/dic/xliff-core/xliff-core-2.0.xsd | 411 ++ .../Loader/schema/dic/xliff-core/xml.xsd | 309 ++ .../symfony/translation/LoggingTranslator.php | 136 + .../symfony/translation/MessageCatalogue.php | 271 + .../translation/MessageCatalogueInterface.php | 136 + .../symfony/translation/MessageSelector.php | 94 + .../translation/MetadataAwareInterface.php | 54 + .../translation/PluralizationRules.php | 210 + vendor/symfony/translation/README.md | 13 + .../translation/Reader/TranslationReader.php | 63 + .../Reader/TranslationReaderInterface.php | 30 + .../schemas/xliff-core-1.2-strict.xsd | 2223 ++++++++ .../Tests/Catalogue/AbstractOperationTest.php | 74 + .../Tests/Catalogue/MergeOperationTest.php | 83 + .../Tests/Catalogue/TargetOperationTest.php | 82 + .../Tests/Command/XliffLintCommandTest.php | 163 + .../TranslationDataCollectorTest.php | 150 + .../Tests/DataCollectorTranslatorTest.php | 92 + .../TranslationDumperPassTest.php | 48 + .../TranslationExtractorPassTest.php | 66 + .../TranslationPassTest.php | 57 + .../Tests/Dumper/CsvFileDumperTest.php | 30 + .../Tests/Dumper/FileDumperTest.php | 66 + .../Tests/Dumper/IcuResFileDumperTest.php | 29 + .../Tests/Dumper/IniFileDumperTest.php | 29 + .../Tests/Dumper/JsonFileDumperTest.php | 39 + .../Tests/Dumper/MoFileDumperTest.php | 29 + .../Tests/Dumper/PhpFileDumperTest.php | 29 + .../Tests/Dumper/PoFileDumperTest.php | 29 + .../Tests/Dumper/QtFileDumperTest.php | 29 + .../Tests/Dumper/XliffFileDumperTest.php | 115 + .../Tests/Dumper/YamlFileDumperTest.php | 47 + .../Tests/Extractor/PhpExtractorTest.php | 95 + .../Tests/Formatter/MessageFormatterTest.php | 82 + .../Tests/IdentityTranslatorTest.php | 96 + .../translation/Tests/IntervalTest.php | 49 + .../Tests/Loader/CsvFileLoaderTest.php | 61 + .../Tests/Loader/IcuDatFileLoaderTest.php | 64 + .../Tests/Loader/IcuResFileLoaderTest.php | 51 + .../Tests/Loader/IniFileLoaderTest.php | 51 + .../Tests/Loader/JsonFileLoaderTest.php | 62 + .../Tests/Loader/LocalizedTestCase.php | 24 + .../Tests/Loader/MoFileLoaderTest.php | 72 + .../Tests/Loader/PhpFileLoaderTest.php | 50 + .../Tests/Loader/PoFileLoaderTest.php | 109 + .../Tests/Loader/QtFileLoaderTest.php | 75 + .../Tests/Loader/XliffFileLoaderTest.php | 260 + .../Tests/Loader/YamlFileLoaderTest.php | 71 + .../Tests/LoggingTranslatorTest.php | 50 + .../Tests/MessageCatalogueTest.php | 222 + .../translation/Tests/MessageSelectorTest.php | 137 + .../Tests/PluralizationRulesTest.php | 122 + .../translation/Tests/TranslatorCacheTest.php | 311 ++ .../translation/Tests/TranslatorTest.php | 549 ++ .../Tests/Util/ArrayConverterTest.php | 74 + .../Tests/Writer/TranslationWriterTest.php | 69 + .../Tests/fixtures/empty-translation.mo | Bin 0 -> 49 bytes .../Tests/fixtures/empty-translation.po | 3 + .../translation/Tests/fixtures/empty.csv | 0 .../translation/Tests/fixtures/empty.ini | 0 .../translation/Tests/fixtures/empty.json | 0 .../translation/Tests/fixtures/empty.mo | 0 .../translation/Tests/fixtures/empty.po | 0 .../translation/Tests/fixtures/empty.xlf | 0 .../translation/Tests/fixtures/empty.yml | 0 .../translation/Tests/fixtures/encoding.xlf | 16 + .../Tests/fixtures/escaped-id-plurals.po | 10 + .../translation/Tests/fixtures/escaped-id.po | 8 + .../fixtures/extractor/resource.format.engine | 0 .../this.is.a.template.format.engine | 0 .../fixtures/extractor/translation.html.php | 49 + .../Tests/fixtures/fuzzy-translations.po | 10 + .../Tests/fixtures/invalid-xml-resources.xlf | 23 + .../translation/Tests/fixtures/malformed.json | 3 + .../translation/Tests/fixtures/messages.yml | 3 + .../Tests/fixtures/messages_linear.yml | 2 + .../translation/Tests/fixtures/non-valid.xlf | 11 + .../translation/Tests/fixtures/non-valid.yml | 1 + .../translation/Tests/fixtures/plurals.mo | Bin 0 -> 74 bytes .../translation/Tests/fixtures/plurals.po | 5 + .../translation/Tests/fixtures/resname.xlf | 19 + .../resourcebundle/corrupted/resources.dat | 1 + .../Tests/fixtures/resourcebundle/dat/en.res | Bin 0 -> 120 bytes .../Tests/fixtures/resourcebundle/dat/en.txt | 3 + .../Tests/fixtures/resourcebundle/dat/fr.res | Bin 0 -> 124 bytes .../Tests/fixtures/resourcebundle/dat/fr.txt | 3 + .../resourcebundle/dat/packagelist.txt | 2 + .../fixtures/resourcebundle/dat/resources.dat | Bin 0 -> 352 bytes .../Tests/fixtures/resourcebundle/res/en.res | Bin 0 -> 84 bytes .../Tests/fixtures/resources-2.0-clean.xlf | 23 + .../resources-2.0-multi-segment-unit.xlf | 17 + .../Tests/fixtures/resources-2.0.xlf | 25 + .../Tests/fixtures/resources-clean.xlf | 25 + .../Tests/fixtures/resources-notes-meta.xlf | 26 + .../fixtures/resources-target-attributes.xlf | 14 + .../Tests/fixtures/resources-tool-info.xlf | 14 + .../translation/Tests/fixtures/resources.csv | 4 + .../Tests/fixtures/resources.dump.json | 1 + .../translation/Tests/fixtures/resources.ini | 1 + .../translation/Tests/fixtures/resources.json | 3 + .../translation/Tests/fixtures/resources.mo | Bin 0 -> 52 bytes .../translation/Tests/fixtures/resources.php | 5 + .../translation/Tests/fixtures/resources.po | 8 + .../translation/Tests/fixtures/resources.ts | 10 + .../translation/Tests/fixtures/resources.xlf | 23 + .../translation/Tests/fixtures/resources.yml | 1 + .../translation/Tests/fixtures/valid.csv | 4 + .../Tests/fixtures/with-attributes.xlf | 21 + .../Tests/fixtures/withdoctype.xlf | 12 + .../translation/Tests/fixtures/withnote.xlf | 22 + vendor/symfony/translation/Translator.php | 437 ++ .../translation/TranslatorBagInterface.php | 33 + .../translation/TranslatorInterface.php | 67 + .../translation/Util/ArrayConverter.php | 99 + .../translation/Writer/TranslationWriter.php | 90 + .../Writer/TranslationWriterInterface.php | 34 + vendor/symfony/translation/composer.json | 53 + vendor/symfony/translation/phpunit.xml.dist | 30 + 411 files changed, 34853 insertions(+), 397 deletions(-) create mode 100644 cf2/Jobs/DatabaseConnection.php create mode 100644 cf2/Jobs/DeleteTransientJob.php create mode 100644 cf2/Jobs/Job.php create mode 100644 cf2/Jobs/Scheduler.php create mode 100644 cf2/Jobs/XJob.php create mode 100644 cf2/RestApi/AuthorizesRestApiRequestWithCfProKeys.php create mode 100644 cf2/RestApi/Queue/RunQueue.php create mode 100644 cf2/Services/QueueSchedulerService.php create mode 100644 cf2/Services/QueueService.php create mode 100644 cf2/Services/Service.php create mode 100644 cf2/Services/ServiceContract.php create mode 100644 cf2/functions.php delete mode 100644 clients/render/build/index.min.js~feature_2771 create mode 100644 tests/Integration/Caldera_Forms_TransientTest.php create mode 100644 tests/Integration/Features/ContainerServicesAreLoadedTest.php create mode 100644 tests/Integration/FunctionsTest.php create mode 100644 tests/Integration/Jobs/DatabaseConnectionTest.php create mode 100644 tests/Integration/Jobs/DeleteTransientJobTest.php create mode 100644 tests/Integration/Jobs/SchedulerTest.php create mode 100644 tests/Integration/RestApi/RunQueueTest.php create mode 100644 tests/Integration/Service/QueueSchedulerServiceTest.php create mode 100644 tests/Integration/Service/QueueServiceTest.php create mode 100644 tests/Util/Mocks/MockJob.php create mode 100644 tests/Util/Mocks/MockQueueConnection.php create mode 100644 tests/Util/Mocks/MockService.php create mode 100644 vendor/a5hleyrich/wp-queue/.gitignore create mode 100644 vendor/a5hleyrich/wp-queue/.scrutinizer.yml create mode 100644 vendor/a5hleyrich/wp-queue/.travis.yml create mode 100644 vendor/a5hleyrich/wp-queue/LICENSE.md create mode 100644 vendor/a5hleyrich/wp-queue/README.md create mode 100644 vendor/a5hleyrich/wp-queue/composer.json create mode 100644 vendor/a5hleyrich/wp-queue/phpunit.xml create mode 100644 vendor/a5hleyrich/wp-queue/src/WP_Queue/Connections/ConnectionInterface.php create mode 100644 vendor/a5hleyrich/wp-queue/src/WP_Queue/Connections/DatabaseConnection.php create mode 100644 vendor/a5hleyrich/wp-queue/src/WP_Queue/Connections/RedisConnection.php create mode 100644 vendor/a5hleyrich/wp-queue/src/WP_Queue/Connections/SyncConnection.php create mode 100644 vendor/a5hleyrich/wp-queue/src/WP_Queue/Cron.php create mode 100644 vendor/a5hleyrich/wp-queue/src/WP_Queue/Exceptions/ConnectionNotFoundException.php create mode 100644 vendor/a5hleyrich/wp-queue/src/WP_Queue/Exceptions/WorkerAttemptsExceededException.php create mode 100644 vendor/a5hleyrich/wp-queue/src/WP_Queue/Job.php create mode 100644 vendor/a5hleyrich/wp-queue/src/WP_Queue/Queue.php create mode 100644 vendor/a5hleyrich/wp-queue/src/WP_Queue/QueueManager.php create mode 100644 vendor/a5hleyrich/wp-queue/src/WP_Queue/Worker.php create mode 100644 vendor/a5hleyrich/wp-queue/src/functions.php create mode 100644 vendor/a5hleyrich/wp-queue/tests/TestDatabaseConnection.php create mode 100644 vendor/a5hleyrich/wp-queue/tests/TestFunctions.php create mode 100644 vendor/a5hleyrich/wp-queue/tests/TestJob.php create mode 100644 vendor/a5hleyrich/wp-queue/tests/TestQueue.php create mode 100644 vendor/a5hleyrich/wp-queue/tests/TestQueueManager.php create mode 100644 vendor/a5hleyrich/wp-queue/tests/TestWorker.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/InsightOpsHandler.php mode change 100644 => 100755 vendor/monolog/monolog/src/Monolog/Handler/Slack/SlackRecord.php create mode 100644 vendor/monolog/monolog/src/Monolog/Processor/ProcessorInterface.php create mode 100644 vendor/monolog/monolog/src/Monolog/ResettableInterface.php create mode 100644 vendor/monolog/monolog/src/Monolog/SignalHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Utils.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/InsightOpsHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/SignalHandlerTest.php create mode 100644 vendor/nesbot/carbon/LICENSE create mode 100644 vendor/nesbot/carbon/composer.json create mode 100644 vendor/nesbot/carbon/readme.md create mode 100644 vendor/nesbot/carbon/src/Carbon/Carbon.php create mode 100644 vendor/nesbot/carbon/src/Carbon/CarbonInterval.php create mode 100644 vendor/nesbot/carbon/src/Carbon/CarbonPeriod.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidDateException.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/af.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ar.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ar_Shakl.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/az.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/bg.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/bn.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/bs_BA.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ca.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/cs.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/cy.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/da.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/de.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/dv_MV.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/el.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/en.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/eo.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/es.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/et.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/eu.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fa.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fi.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fo.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/fr.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/gl.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/gu.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/he.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/hi.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/hr.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/hu.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/hy.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/id.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/is.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/it.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ja.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ka.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/kk.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/km.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ko.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/lt.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/lv.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/mk.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/mn.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ms.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/my.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ne.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/nl.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/no.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/oc.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/pl.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ps.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/pt.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/pt_BR.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ro.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ru.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sh.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sk.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sl.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sq.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sr.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sr_Cyrl.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sr_Cyrl_ME.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sr_Latn_ME.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sr_ME.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sv.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/sw.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/th.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/tr.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/uk.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/ur.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/uz.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/vi.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/zh.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Lang/zh_TW.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Laravel/ServiceProvider.php create mode 100644 vendor/nesbot/carbon/src/Carbon/Translator.php create mode 100644 vendor/nesbot/carbon/src/JsonSerializable.php 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/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/composer.json create mode 100644 vendor/symfony/translation/.gitignore create mode 100644 vendor/symfony/translation/CHANGELOG.md create mode 100644 vendor/symfony/translation/Catalogue/AbstractOperation.php create mode 100644 vendor/symfony/translation/Catalogue/MergeOperation.php create mode 100644 vendor/symfony/translation/Catalogue/OperationInterface.php create mode 100644 vendor/symfony/translation/Catalogue/TargetOperation.php create mode 100644 vendor/symfony/translation/Command/XliffLintCommand.php create mode 100644 vendor/symfony/translation/DataCollector/TranslationDataCollector.php create mode 100644 vendor/symfony/translation/DataCollectorTranslator.php create mode 100644 vendor/symfony/translation/DependencyInjection/TranslationDumperPass.php create mode 100644 vendor/symfony/translation/DependencyInjection/TranslationExtractorPass.php create mode 100644 vendor/symfony/translation/DependencyInjection/TranslatorPass.php create mode 100644 vendor/symfony/translation/Dumper/CsvFileDumper.php create mode 100644 vendor/symfony/translation/Dumper/DumperInterface.php create mode 100644 vendor/symfony/translation/Dumper/FileDumper.php create mode 100644 vendor/symfony/translation/Dumper/IcuResFileDumper.php create mode 100644 vendor/symfony/translation/Dumper/IniFileDumper.php create mode 100644 vendor/symfony/translation/Dumper/JsonFileDumper.php create mode 100644 vendor/symfony/translation/Dumper/MoFileDumper.php create mode 100644 vendor/symfony/translation/Dumper/PhpFileDumper.php create mode 100644 vendor/symfony/translation/Dumper/PoFileDumper.php create mode 100644 vendor/symfony/translation/Dumper/QtFileDumper.php create mode 100644 vendor/symfony/translation/Dumper/XliffFileDumper.php create mode 100644 vendor/symfony/translation/Dumper/YamlFileDumper.php create mode 100644 vendor/symfony/translation/Exception/ExceptionInterface.php create mode 100644 vendor/symfony/translation/Exception/InvalidArgumentException.php create mode 100644 vendor/symfony/translation/Exception/InvalidResourceException.php create mode 100644 vendor/symfony/translation/Exception/LogicException.php create mode 100644 vendor/symfony/translation/Exception/NotFoundResourceException.php create mode 100644 vendor/symfony/translation/Exception/RuntimeException.php create mode 100644 vendor/symfony/translation/Extractor/AbstractFileExtractor.php create mode 100644 vendor/symfony/translation/Extractor/ChainExtractor.php create mode 100644 vendor/symfony/translation/Extractor/ExtractorInterface.php create mode 100644 vendor/symfony/translation/Extractor/PhpExtractor.php create mode 100644 vendor/symfony/translation/Extractor/PhpStringTokenParser.php create mode 100644 vendor/symfony/translation/Formatter/ChoiceMessageFormatterInterface.php create mode 100644 vendor/symfony/translation/Formatter/MessageFormatter.php create mode 100644 vendor/symfony/translation/Formatter/MessageFormatterInterface.php create mode 100644 vendor/symfony/translation/IdentityTranslator.php create mode 100644 vendor/symfony/translation/Interval.php create mode 100644 vendor/symfony/translation/LICENSE create mode 100644 vendor/symfony/translation/Loader/ArrayLoader.php create mode 100644 vendor/symfony/translation/Loader/CsvFileLoader.php create mode 100644 vendor/symfony/translation/Loader/FileLoader.php create mode 100644 vendor/symfony/translation/Loader/IcuDatFileLoader.php create mode 100644 vendor/symfony/translation/Loader/IcuResFileLoader.php create mode 100644 vendor/symfony/translation/Loader/IniFileLoader.php create mode 100644 vendor/symfony/translation/Loader/JsonFileLoader.php create mode 100644 vendor/symfony/translation/Loader/LoaderInterface.php create mode 100644 vendor/symfony/translation/Loader/MoFileLoader.php create mode 100644 vendor/symfony/translation/Loader/PhpFileLoader.php create mode 100644 vendor/symfony/translation/Loader/PoFileLoader.php create mode 100644 vendor/symfony/translation/Loader/QtFileLoader.php create mode 100644 vendor/symfony/translation/Loader/XliffFileLoader.php create mode 100644 vendor/symfony/translation/Loader/YamlFileLoader.php create mode 100644 vendor/symfony/translation/Loader/schema/dic/xliff-core/xliff-core-1.2-strict.xsd create mode 100644 vendor/symfony/translation/Loader/schema/dic/xliff-core/xliff-core-2.0.xsd create mode 100644 vendor/symfony/translation/Loader/schema/dic/xliff-core/xml.xsd create mode 100644 vendor/symfony/translation/LoggingTranslator.php create mode 100644 vendor/symfony/translation/MessageCatalogue.php create mode 100644 vendor/symfony/translation/MessageCatalogueInterface.php create mode 100644 vendor/symfony/translation/MessageSelector.php create mode 100644 vendor/symfony/translation/MetadataAwareInterface.php create mode 100644 vendor/symfony/translation/PluralizationRules.php create mode 100644 vendor/symfony/translation/README.md create mode 100644 vendor/symfony/translation/Reader/TranslationReader.php create mode 100644 vendor/symfony/translation/Reader/TranslationReaderInterface.php create mode 100644 vendor/symfony/translation/Resources/schemas/xliff-core-1.2-strict.xsd create mode 100644 vendor/symfony/translation/Tests/Catalogue/AbstractOperationTest.php create mode 100644 vendor/symfony/translation/Tests/Catalogue/MergeOperationTest.php create mode 100644 vendor/symfony/translation/Tests/Catalogue/TargetOperationTest.php create mode 100644 vendor/symfony/translation/Tests/Command/XliffLintCommandTest.php create mode 100644 vendor/symfony/translation/Tests/DataCollector/TranslationDataCollectorTest.php create mode 100644 vendor/symfony/translation/Tests/DataCollectorTranslatorTest.php create mode 100644 vendor/symfony/translation/Tests/DependencyInjection/TranslationDumperPassTest.php create mode 100644 vendor/symfony/translation/Tests/DependencyInjection/TranslationExtractorPassTest.php create mode 100644 vendor/symfony/translation/Tests/DependencyInjection/TranslationPassTest.php create mode 100644 vendor/symfony/translation/Tests/Dumper/CsvFileDumperTest.php create mode 100644 vendor/symfony/translation/Tests/Dumper/FileDumperTest.php create mode 100644 vendor/symfony/translation/Tests/Dumper/IcuResFileDumperTest.php create mode 100644 vendor/symfony/translation/Tests/Dumper/IniFileDumperTest.php create mode 100644 vendor/symfony/translation/Tests/Dumper/JsonFileDumperTest.php create mode 100644 vendor/symfony/translation/Tests/Dumper/MoFileDumperTest.php create mode 100644 vendor/symfony/translation/Tests/Dumper/PhpFileDumperTest.php create mode 100644 vendor/symfony/translation/Tests/Dumper/PoFileDumperTest.php create mode 100644 vendor/symfony/translation/Tests/Dumper/QtFileDumperTest.php create mode 100644 vendor/symfony/translation/Tests/Dumper/XliffFileDumperTest.php create mode 100644 vendor/symfony/translation/Tests/Dumper/YamlFileDumperTest.php create mode 100644 vendor/symfony/translation/Tests/Extractor/PhpExtractorTest.php create mode 100644 vendor/symfony/translation/Tests/Formatter/MessageFormatterTest.php create mode 100644 vendor/symfony/translation/Tests/IdentityTranslatorTest.php create mode 100644 vendor/symfony/translation/Tests/IntervalTest.php create mode 100644 vendor/symfony/translation/Tests/Loader/CsvFileLoaderTest.php create mode 100644 vendor/symfony/translation/Tests/Loader/IcuDatFileLoaderTest.php create mode 100644 vendor/symfony/translation/Tests/Loader/IcuResFileLoaderTest.php create mode 100644 vendor/symfony/translation/Tests/Loader/IniFileLoaderTest.php create mode 100644 vendor/symfony/translation/Tests/Loader/JsonFileLoaderTest.php create mode 100644 vendor/symfony/translation/Tests/Loader/LocalizedTestCase.php create mode 100644 vendor/symfony/translation/Tests/Loader/MoFileLoaderTest.php create mode 100644 vendor/symfony/translation/Tests/Loader/PhpFileLoaderTest.php create mode 100644 vendor/symfony/translation/Tests/Loader/PoFileLoaderTest.php create mode 100644 vendor/symfony/translation/Tests/Loader/QtFileLoaderTest.php create mode 100644 vendor/symfony/translation/Tests/Loader/XliffFileLoaderTest.php create mode 100644 vendor/symfony/translation/Tests/Loader/YamlFileLoaderTest.php create mode 100644 vendor/symfony/translation/Tests/LoggingTranslatorTest.php create mode 100644 vendor/symfony/translation/Tests/MessageCatalogueTest.php create mode 100644 vendor/symfony/translation/Tests/MessageSelectorTest.php create mode 100644 vendor/symfony/translation/Tests/PluralizationRulesTest.php create mode 100644 vendor/symfony/translation/Tests/TranslatorCacheTest.php create mode 100644 vendor/symfony/translation/Tests/TranslatorTest.php create mode 100644 vendor/symfony/translation/Tests/Util/ArrayConverterTest.php create mode 100644 vendor/symfony/translation/Tests/Writer/TranslationWriterTest.php create mode 100644 vendor/symfony/translation/Tests/fixtures/empty-translation.mo create mode 100644 vendor/symfony/translation/Tests/fixtures/empty-translation.po create mode 100644 vendor/symfony/translation/Tests/fixtures/empty.csv create mode 100644 vendor/symfony/translation/Tests/fixtures/empty.ini create mode 100644 vendor/symfony/translation/Tests/fixtures/empty.json create mode 100644 vendor/symfony/translation/Tests/fixtures/empty.mo create mode 100644 vendor/symfony/translation/Tests/fixtures/empty.po create mode 100644 vendor/symfony/translation/Tests/fixtures/empty.xlf create mode 100644 vendor/symfony/translation/Tests/fixtures/empty.yml create mode 100644 vendor/symfony/translation/Tests/fixtures/encoding.xlf create mode 100644 vendor/symfony/translation/Tests/fixtures/escaped-id-plurals.po create mode 100644 vendor/symfony/translation/Tests/fixtures/escaped-id.po create mode 100644 vendor/symfony/translation/Tests/fixtures/extractor/resource.format.engine create mode 100644 vendor/symfony/translation/Tests/fixtures/extractor/this.is.a.template.format.engine create mode 100644 vendor/symfony/translation/Tests/fixtures/extractor/translation.html.php create mode 100644 vendor/symfony/translation/Tests/fixtures/fuzzy-translations.po create mode 100644 vendor/symfony/translation/Tests/fixtures/invalid-xml-resources.xlf create mode 100644 vendor/symfony/translation/Tests/fixtures/malformed.json create mode 100644 vendor/symfony/translation/Tests/fixtures/messages.yml create mode 100644 vendor/symfony/translation/Tests/fixtures/messages_linear.yml create mode 100644 vendor/symfony/translation/Tests/fixtures/non-valid.xlf create mode 100644 vendor/symfony/translation/Tests/fixtures/non-valid.yml create mode 100644 vendor/symfony/translation/Tests/fixtures/plurals.mo create mode 100644 vendor/symfony/translation/Tests/fixtures/plurals.po create mode 100644 vendor/symfony/translation/Tests/fixtures/resname.xlf create mode 100644 vendor/symfony/translation/Tests/fixtures/resourcebundle/corrupted/resources.dat create mode 100644 vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/en.res create mode 100644 vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/en.txt create mode 100644 vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/fr.res create mode 100644 vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/fr.txt create mode 100644 vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/packagelist.txt create mode 100644 vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/resources.dat create mode 100644 vendor/symfony/translation/Tests/fixtures/resourcebundle/res/en.res create mode 100644 vendor/symfony/translation/Tests/fixtures/resources-2.0-clean.xlf create mode 100644 vendor/symfony/translation/Tests/fixtures/resources-2.0-multi-segment-unit.xlf create mode 100644 vendor/symfony/translation/Tests/fixtures/resources-2.0.xlf create mode 100644 vendor/symfony/translation/Tests/fixtures/resources-clean.xlf create mode 100644 vendor/symfony/translation/Tests/fixtures/resources-notes-meta.xlf create mode 100644 vendor/symfony/translation/Tests/fixtures/resources-target-attributes.xlf create mode 100644 vendor/symfony/translation/Tests/fixtures/resources-tool-info.xlf create mode 100644 vendor/symfony/translation/Tests/fixtures/resources.csv create mode 100644 vendor/symfony/translation/Tests/fixtures/resources.dump.json create mode 100644 vendor/symfony/translation/Tests/fixtures/resources.ini create mode 100644 vendor/symfony/translation/Tests/fixtures/resources.json create mode 100644 vendor/symfony/translation/Tests/fixtures/resources.mo create mode 100644 vendor/symfony/translation/Tests/fixtures/resources.php create mode 100644 vendor/symfony/translation/Tests/fixtures/resources.po create mode 100644 vendor/symfony/translation/Tests/fixtures/resources.ts create mode 100644 vendor/symfony/translation/Tests/fixtures/resources.xlf create mode 100644 vendor/symfony/translation/Tests/fixtures/resources.yml create mode 100644 vendor/symfony/translation/Tests/fixtures/valid.csv create mode 100644 vendor/symfony/translation/Tests/fixtures/with-attributes.xlf create mode 100644 vendor/symfony/translation/Tests/fixtures/withdoctype.xlf create mode 100644 vendor/symfony/translation/Tests/fixtures/withnote.xlf create mode 100644 vendor/symfony/translation/Translator.php create mode 100644 vendor/symfony/translation/TranslatorBagInterface.php create mode 100644 vendor/symfony/translation/TranslatorInterface.php create mode 100644 vendor/symfony/translation/Util/ArrayConverter.php create mode 100644 vendor/symfony/translation/Writer/TranslationWriter.php create mode 100644 vendor/symfony/translation/Writer/TranslationWriterInterface.php create mode 100644 vendor/symfony/translation/composer.json create mode 100644 vendor/symfony/translation/phpunit.xml.dist diff --git a/caldera-core.php b/caldera-core.php index 13e9c9dfe..59f9b2714 100755 --- a/caldera-core.php +++ b/caldera-core.php @@ -121,7 +121,7 @@ function caldera_forms_load() */ do_action('caldera_forms_includes_complete'); - + caldera_forms_get_v2_container(); } add_action('plugins_loaded', array('Caldera_Forms', 'get_instance')); @@ -170,23 +170,37 @@ function caldera_forms_fallback_shortcode() return $field; },1,2); + + /** - * Init Cf2 system + * Setup Cf2 con * * @since 1.8.0 * * @TODO move this somewhere smarter */ -add_action( 'caldera_forms_core_init', function(){ - $container = new \calderawp\calderaforms\cf2\CalderaFormsV2(); +add_action( 'caldera_forms_v2_init', function(\calderawp\calderaforms\cf2\CalderaFormsV2Contract $container){ $container->setCoreDir(CFCORE_PATH); + + //Setup field types $container->getFieldTypeFactory() ->add( new \calderawp\calderaforms\cf2\Fields\FieldTypes\FileFieldType() ); + //Add hooks $container->getHooks()->subscribe(); + //Register other services + $container->registerService(new \calderawp\calderaforms\cf2\Services\QueueService(),true ); + $container->registerService(new \calderawp\calderaforms\cf2\Services\QueueSchedulerService(),true ); + + //Run the scheduler with CRON + /** @var \calderawp\calderaforms\cf2\Jobs\Scheduler $scheduler */ + $scheduler = $container->getService(\calderawp\calderaforms\cf2\Services\QueueSchedulerService::class); + $running = $scheduler->runWithCron(); + }); + diff --git a/cf2/CalderaFormsV2.php b/cf2/CalderaFormsV2.php index ab3894ccf..5731b947c 100644 --- a/cf2/CalderaFormsV2.php +++ b/cf2/CalderaFormsV2.php @@ -6,7 +6,7 @@ use calderawp\calderaforms\cf2\Fields\FieldTypeFactory; use calderawp\calderaforms\cf2\Transients\Cf1TransientsApi; - +use calderawp\calderaforms\cf2\Services\ServiceContract; class CalderaFormsV2 extends \calderawp\CalderaContainers\Service\Container implements CalderaFormsV2Contract { @@ -38,6 +38,40 @@ public function __construct() }); } + /** + * Register a service with container + * + * @since 1.8.0 + * + * @param ServiceContract $service The service to register + * + * @param boolean $isSingleton Is service a singleton? + * + * @return $this + */ + public function registerService( ServiceContract $service, $isSingleton ){ + if (! $service->isSingleton()) { + $this->bind($service->getIdentifier(), $service->register($this) ); + }else{ + $this->singleton($service->getIdentifier(), $service->register($this) ); + } + return $this; + } + + + /** + * Get service from container + * + * @since 1.8.0 + * + * @param string $identifier + * + * @return mixed + */ + public function getService($identifier){ + return $this->make($identifier); + } + /** * Set path to main plugin file * @@ -105,4 +139,11 @@ public function getFieldTypeFactory() { return $this->make(FieldTypeFactory::class ); } + + /** @inheritdoc */ + public function getWpdb() + { + global $wpdb; + return $wpdb; + } } diff --git a/cf2/CalderaFormsV2Contract.php b/cf2/CalderaFormsV2Contract.php index fdf156e9e..d69393aba 100644 --- a/cf2/CalderaFormsV2Contract.php +++ b/cf2/CalderaFormsV2Contract.php @@ -6,27 +6,28 @@ use calderawp\calderaforms\cf2\Fields\FieldTypeFactory; use calderawp\calderaforms\cf2\Transients\Cf1TransientsApi; +use calderawp\calderaforms\cf2\Services\ServiceContract; interface CalderaFormsV2Contract { - /** - * Get WordPress Transients API wrapper - * - * @since 1.8.0 - * - * @return Cf1TransientsApi - */ - public function getTransientsApi(); + /** + * Get WordPress Transients API wrapper + * + * @since 1.8.0 + * + * @return Cf1TransientsApi + */ + public function getTransientsApi(); - /** - * Get WordPress Plugins API manager - * - * @since 1.8.0 - * - * @return Hooks - */ - public function getHooks(); + /** + * Get WordPress Plugins API manager + * + * @since 1.8.0 + * + * @return Hooks + */ + public function getHooks(); /** @@ -57,4 +58,40 @@ public function getCoreDir(); * @return FieldTypeFactory */ public function getFieldTypeFactory(); -} + + /** + * Get global $wpdb + * + * @since 1.8.0 + * + * @return \wpdb + */ + public function getWpdb(); + + + + /** + * Register a service with container + * + * @since 1.8.0 + * + * @param ServiceContract $service The service to register + * + * @param boolean $isSingleton Is service a singleton? + * + * @return $this + */ + public function registerService( ServiceContract $service, $isSingleton ); + + + /** + * Get service from container + * + * @since 1.8.0 + * + * @param string $identifier + * + * @return mixed + */ + public function getService($identifier); +} \ No newline at end of file diff --git a/cf2/Jobs/DatabaseConnection.php b/cf2/Jobs/DatabaseConnection.php new file mode 100644 index 000000000..53422381a --- /dev/null +++ b/cf2/Jobs/DatabaseConnection.php @@ -0,0 +1,27 @@ +database = $wpdb; + $this->jobs_table = $this->database->prefix . static::QUEUED_JOBS_TABLE; + $this->failures_table = $this->database->prefix . static::FAILED_JOBS_TABLE; + } +} \ No newline at end of file diff --git a/cf2/Jobs/DeleteTransientJob.php b/cf2/Jobs/DeleteTransientJob.php new file mode 100644 index 000000000..a4e4a50fc --- /dev/null +++ b/cf2/Jobs/DeleteTransientJob.php @@ -0,0 +1,21 @@ +transientId = $transientId; + } + + + public function handle() + { + \Caldera_Forms_Transient::delete_transient($this->transientId); + } +} \ No newline at end of file diff --git a/cf2/Jobs/Job.php b/cf2/Jobs/Job.php new file mode 100644 index 000000000..8ef294796 --- /dev/null +++ b/cf2/Jobs/Job.php @@ -0,0 +1,10 @@ +queue = $queue; + } + + /** + * Get the queue worker + * + * @since 1.8.0 + * + * @return \WP_Queue\Worker + */ + public function getWorker(){ + return $this->queue->worker(2); + } + + /** + * Schedule a job to run + * + * @since 1.8.0 + * + * @param Job $job Job to schedule. + * @param int $delay Optional. Delay in seconds until job runs. Default is 0. + */ + public function schedule(Job $job, $delay = 0){ + $this->queue->push($job, $delay ); + } + + /** + * Run the queue with WP_Cron + * + * @since 1.8.0 + * + * @return bool + */ + public function runWithCron() + { + return $this->queue->cron(2)->init(); + } + + /** + * Run jobs independently of WP_Cron + * + * @since 1.8.0 + * + * @param int $numberOfJobs + * + * @return int + */ + public function runJobs($numberOfJobs = 5){ + $worker = $this->getWorker(); + $jobsRan = 0; + for( $totalJobs = 0; $totalJobs < $numberOfJobs; $totalJobs++ ){ + if (!$worker->process()) { + return $jobsRan; + } + $jobsRan++; + + } + + return $jobsRan; + + } + +} \ No newline at end of file diff --git a/cf2/Jobs/XJob.php b/cf2/Jobs/XJob.php new file mode 100644 index 000000000..e1ca5da22 --- /dev/null +++ b/cf2/Jobs/XJob.php @@ -0,0 +1,20 @@ +x = $x; + } + + public function handle() + { + wp_insert_post(['post_type' => 'post', 'post_status' => 'publish', 'post_content' => 'fasfsd', 'post_title' => 'job1' . $this->x ] ); + } +} \ No newline at end of file diff --git a/cf2/RestApi/AuthorizesRestApiRequestWithCfProKeys.php b/cf2/RestApi/AuthorizesRestApiRequestWithCfProKeys.php new file mode 100644 index 000000000..0da281fc1 --- /dev/null +++ b/cf2/RestApi/AuthorizesRestApiRequestWithCfProKeys.php @@ -0,0 +1,64 @@ +getPublic($request); + $token = $this->getToken($request); + + if( ! is_string( $public ) || ! is_string( $token ) ){ + return false; + } + return caldera_forms_pro_compare_to_saved_keys( $public, $token ); + } + + /** + * Get public key from request param or headers + * + * @since 1.8.0 + * + * @param \WP_REST_Request $request + * + * @return null|string + */ + public function getPublic(\WP_REST_Request $request ){ + if( ! empty( $request[ 'public']) && is_string( $request[ 'public' ] ) ){ + return $request[ 'public' ]; + } + if( ! is_null( $request->get_header( 'X-CS-PUBLIC' ) ) ){ + return $request->get_header( 'X-CS-PUBLIC' ); + } + } + + /** + * Get token from request param or headers + * + * @since 1.8.0 + * + * @param \WP_REST_Request $request + * + * @return null|string + */ + public function getToken(\WP_REST_Request $request ){ + if( ! empty( $request[ 'token']) && is_string( $request[ 'token' ] ) ){ + return $request[ 'token' ]; + } + if( ! is_null( $request->get_header( 'X-CS-TOKEN' ) ) ){ + return $request->get_header( 'X-CS-TOKEN' ); + } + } +} \ No newline at end of file diff --git a/cf2/RestApi/Queue/RunQueue.php b/cf2/RestApi/Queue/RunQueue.php new file mode 100644 index 000000000..5f47294b0 --- /dev/null +++ b/cf2/RestApi/Queue/RunQueue.php @@ -0,0 +1,74 @@ + 'POST', + 'callback' => [$this, 'runQueue'], + 'permission_callback' => [$this, 'checkKeys' ], + 'args' => [ + 'jobs' => [ + 'description' => __('Total jobs to run per back', 'caldera-forms'), + 'required' => false, + 'default' => 10, + 'sanitize_callback' => 'absint' + + ], + 'public' => [ + 'type' => 'string', + 'required' => false, + 'default' => '' + ] + ] + ]; + } + + + /** + * Trigger queue manger from remote ping + * + * @since 1.8.0 + * + * @param \WP_REST_Request $request + * @return \WP_REST_Response + */ + + /** + * @param \WP_REST_Request $request + * @return mixed|null|\WP_REST_Response + */ + public function runQueue(\WP_REST_Request $request) + { + $totalJobs = caldera_forms_get_v2_container() + ->getService(QueueSchedulerService::class ) + ->runJobs($request['jobs']); + + $statusCode = $totalJobs > 0 ? 201 : 200; + $response = rest_ensure_response(['totalJobs' => $totalJobs]); + $response->set_status($statusCode); + return $response; + } + + + +} diff --git a/cf2/RestApi/Register.php b/cf2/RestApi/Register.php index ccfe87a83..1ea185185 100644 --- a/cf2/RestApi/Register.php +++ b/cf2/RestApi/Register.php @@ -5,6 +5,7 @@ use calderawp\calderaforms\cf2\RestApi\File\CreateFile; +use calderawp\calderaforms\cf2\RestApi\Queue\RunQueue; class Register implements CalderaRestApiContract { @@ -41,6 +42,7 @@ public function getNamespace() public function initEndpoints() { (new CreateFile() )->add_routes($this->getNamespace()); + (new RunQueue() )->add_routes($this->getNamespace()); return $this; } diff --git a/cf2/Services/QueueSchedulerService.php b/cf2/Services/QueueSchedulerService.php new file mode 100644 index 000000000..161a42106 --- /dev/null +++ b/cf2/Services/QueueSchedulerService.php @@ -0,0 +1,30 @@ +getWpdb())) ); + } + + +} \ No newline at end of file diff --git a/cf2/Services/QueueService.php b/cf2/Services/QueueService.php new file mode 100644 index 000000000..eae1ddfdd --- /dev/null +++ b/cf2/Services/QueueService.php @@ -0,0 +1,25 @@ +getWpdb())); + } + +} \ No newline at end of file diff --git a/cf2/Services/Service.php b/cf2/Services/Service.php new file mode 100644 index 000000000..04850b8e2 --- /dev/null +++ b/cf2/Services/Service.php @@ -0,0 +1,16 @@ +hide_errors(); + $charset_collate = $wpdb->get_charset_collate(); + $table = \calderawp\calderaforms\cf2\Jobs\DatabaseConnection::QUEUED_JOBS_TABLE; + $sql = "CREATE TABLE {$wpdb->prefix}{$table} ( + id bigint(20) NOT NULL AUTO_INCREMENT, + job longtext NOT NULL, + attempts tinyint(3) NOT NULL DEFAULT 0, + reserved_at datetime DEFAULT NULL, + available_at datetime NOT NULL, + created_at datetime NOT NULL, + PRIMARY KEY (id) + ) $charset_collate;"; + + dbDelta($sql); + + + } + + /** + * Install database table for job queue fails + * + * @since 1.8.0 + */ + public function queue_failures(){ + global $wpdb; + + require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); + + $wpdb->hide_errors(); + $charset_collate = $wpdb->get_charset_collate(); + $table = \calderawp\calderaforms\cf2\Jobs\DatabaseConnection::FAILED_JOBS_TABLE; + $sql = "CREATE TABLE {$wpdb->prefix}{$table} ( + id bigint(20) NOT NULL AUTO_INCREMENT, + job longtext NOT NULL, + error text DEFAULT NULL, + failed_at datetime NOT NULL, + PRIMARY KEY (id) + ) $charset_collate;"; + + dbDelta( $sql ); + } + + /** * Set the charset_collate property if not set * diff --git a/classes/transient.php b/classes/transient.php index f7349b56e..93fb9831a 100755 --- a/classes/transient.php +++ b/classes/transient.php @@ -60,7 +60,11 @@ public static function set_transient( $id, $data, $expires = null ){ $expires = absint( $expires ); } - wp_schedule_single_event( time() + $expires, self::CRON_ACTION, array( $id ) ); + //schedule delete with job manager + caldera_forms_get_v2_container() + ->getService(\calderawp\calderaforms\cf2\Services\QueueSchedulerService::class) + ->schedule( new \calderawp\calderaforms\cf2\Jobs\DeleteTransientJob($id), $expires ); + return update_option( self::prefix( $id ), $data, false ); } @@ -75,7 +79,6 @@ public static function set_transient( $id, $data, $expires = null ){ * @return bool */ public static function delete_transient( $id ){ - wp_clear_scheduled_hook(self::CRON_ACTION, array( $id ) ); return delete_option( self::prefix( $id ) ); } diff --git a/clients/render/build/index.min.js~feature_2771 b/clients/render/build/index.min.js~feature_2771 deleted file mode 100644 index 088417296..000000000 --- a/clients/render/build/index.min.js~feature_2771 +++ /dev/null @@ -1,77 +0,0 @@ -this["calderaForms"] = this["calderaForms"] || {}; this["calderaForms"]["render"] = -/******/ (function(modules) { // webpackBootstrap -/******/ // The module cache -/******/ var installedModules = {}; -/******/ -/******/ // The require function -/******/ function __webpack_require__(moduleId) { -/******/ -/******/ // Check if module is in cache -/******/ if(installedModules[moduleId]) { -/******/ return installedModules[moduleId].exports; -/******/ } -/******/ // Create a new module (and put it into the cache) -/******/ var module = installedModules[moduleId] = { -/******/ i: moduleId, -/******/ l: false, -/******/ exports: {} -/******/ }; -/******/ -/******/ // Execute the module function -/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); -/******/ -/******/ // Flag the module as loaded -/******/ module.l = true; -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } -/******/ -/******/ -/******/ // expose the modules object (__webpack_modules__) -/******/ __webpack_require__.m = modules; -/******/ -/******/ // expose the module cache -/******/ __webpack_require__.c = installedModules; -/******/ -/******/ // define getter function for harmony exports -/******/ __webpack_require__.d = function(exports, name, getter) { -/******/ if(!__webpack_require__.o(exports, name)) { -/******/ Object.defineProperty(exports, name, { -/******/ configurable: false, -/******/ enumerable: true, -/******/ get: getter -/******/ }); -/******/ } -/******/ }; -/******/ -/******/ // getDefaultExport function for compatibility with non-harmony modules -/******/ __webpack_require__.n = function(module) { -/******/ var getter = module && module.__esModule ? -/******/ function getDefault() { return module['default']; } : -/******/ function getModuleExports() { return module; }; -/******/ __webpack_require__.d(getter, 'a', getter); -/******/ return getter; -/******/ }; -/******/ -/******/ // Object.prototype.hasOwnProperty.call -/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; -/******/ -/******/ // __webpack_public_path__ -/******/ __webpack_require__.p = ""; -/******/ -/******/ // Load entry module and return exports -/******/ return __webpack_require__(__webpack_require__.s = 688); -/******/ }) -/************************************************************************/ -/******/ ({ - -/***/ 688: -/***/ (function(module, exports) { - -throw new Error("Module build failed: Error: ENOENT: no such file or directory, open '/Users/josh/caldera-dev/Caldera-Forms/clients/render/index.js'"); - -/***/ }) - -/******/ }); -//# sourceMappingURL=index.min.js.map \ No newline at end of file diff --git a/composer.json b/composer.json index b1924e1b0..4ff9e2b4c 100755 --- a/composer.json +++ b/composer.json @@ -41,13 +41,17 @@ "inpsyde/wonolog": "^1.0", "calderawp/caldera-forms-query" : "dev-master", "calderawp/caldera-containers": "^0.2.0", - "composer/installers": "^1.6" + "composer/installers": "^1.6", + "a5hleyrich/wp-queue": "^1.3" }, "autoload": { "psr-4": { "calderawp\\calderaforms\\pro\\": "includes/cf-pro-client/classes/", "calderawp\\calderaforms\\cf2\\": "cf2" - } + }, + "files" : [ + "./cf2/functions.php" + ] }, "autoload-dev": { "psr-4": { diff --git a/composer.lock b/composer.lock index 1f64fd768..c4901aff4 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,57 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "184582c7faa7beeeed318dd04f95fe29", + "content-hash": "30529054a22310edfcf0b20dc2b36206", "packages": [ + { + "name": "a5hleyrich/wp-queue", + "version": "1.3.0", + "source": { + "type": "git", + "url": "https://github.com/A5hleyRich/wp-queue.git", + "reference": "4cc44fa2ae1e493113e1f71e7e4efeecefb63f50" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/A5hleyRich/wp-queue/zipball/4cc44fa2ae1e493113e1f71e7e4efeecefb63f50", + "reference": "4cc44fa2ae1e493113e1f71e7e4efeecefb63f50", + "shasum": "" + }, + "require": { + "nesbot/carbon": "^1.22", + "php": ">=5.3.0" + }, + "require-dev": { + "10up/wp_mock": "0.2.0", + "phpunit/phpunit": "~5.7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "WP_Queue\\": "src\\WP_Queue" + }, + "files": [ + "src/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ashley Rich", + "email": "hello@ashleyrich.com" + } + ], + "description": "WordPress job queues", + "keywords": [ + "job", + "queue", + "wordpress" + ], + "time": "2018-04-20T10:24:04+00:00" + }, { "name": "calderawp/caldera-containers", "version": "0.2.0", @@ -293,16 +342,16 @@ }, { "name": "monolog/monolog", - "version": "1.23.0", + "version": "1.24.0", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "fd8c787753b3a2ad11bc60c063cff1358a32a3b4" + "reference": "bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/fd8c787753b3a2ad11bc60c063cff1358a32a3b4", - "reference": "fd8c787753b3a2ad11bc60c063cff1358a32a3b4", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266", + "reference": "bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266", "shasum": "" }, "require": { @@ -367,7 +416,62 @@ "logging", "psr-3" ], - "time": "2017-06-19T01:22:40+00:00" + "time": "2018-11-05T09:00:11+00:00" + }, + { + "name": "nesbot/carbon", + "version": "1.34.0", + "source": { + "type": "git", + "url": "https://github.com/briannesbitt/Carbon.git", + "reference": "1dbd3cb01c5645f3e7deda7aa46ef780d95fcc33" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/1dbd3cb01c5645f3e7deda7aa46ef780d95fcc33", + "reference": "1dbd3cb01c5645f3e7deda7aa46ef780d95fcc33", + "shasum": "" + }, + "require": { + "php": ">=5.3.9", + "symfony/translation": "~2.6 || ~3.0 || ~4.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "~2", + "phpunit/phpunit": "^4.8.35 || ^5.7" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Carbon\\Laravel\\ServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brian Nesbitt", + "email": "brian@nesbot.com", + "homepage": "http://nesbot.com" + } + ], + "description": "A simple API extension for DateTime.", + "homepage": "http://carbon.nesbot.com", + "keywords": [ + "date", + "datetime", + "time" + ], + "time": "2018-09-20T19:36:25+00:00" }, { "name": "nilportugues/sql-query-builder", @@ -633,6 +737,134 @@ "psr-3" ], "time": "2016-10-10T12:19:37+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.10.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "c79c051f5b3a46be09205c73b80b346e4153e494" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/c79c051f5b3a46be09205c73b80b346e4153e494", + "reference": "c79c051f5b3a46be09205c73b80b346e4153e494", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.9-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.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": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "time": "2018-09-21T13:07:52+00:00" + }, + { + "name": "symfony/translation", + "version": "v4.1.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation.git", + "reference": "aa04dc1c75b7d3da7bd7003104cd0cfc5dff635c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation/zipball/aa04dc1c75b7d3da7bd7003104cd0cfc5dff635c", + "reference": "aa04dc1c75b7d3da7bd7003104cd0cfc5dff635c", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/config": "<3.4", + "symfony/dependency-injection": "<3.4", + "symfony/yaml": "<3.4" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "~3.4|~4.0", + "symfony/console": "~3.4|~4.0", + "symfony/dependency-injection": "~3.4|~4.0", + "symfony/finder": "~2.8|~3.0|~4.0", + "symfony/intl": "~3.4|~4.0", + "symfony/yaml": "~3.4|~4.0" + }, + "suggest": { + "psr/log-implementation": "To use logging capability in translator", + "symfony/config": "", + "symfony/yaml": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Translation Component", + "homepage": "https://symfony.com", + "time": "2018-10-28T18:38:52+00:00" } ], "packages-dev": [ @@ -2205,7 +2437,7 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.9.0", + "version": "v1.10.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", @@ -2263,7 +2495,7 @@ }, { "name": "symfony/yaml", - "version": "v3.4.17", + "version": "v3.4.18", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", @@ -2372,15 +2604,15 @@ }, { "name": "wpackagist-plugin/gutenberg", - "version": "4.0.0", + "version": "4.1.1", "source": { "type": "svn", "url": "https://plugins.svn.wordpress.org/gutenberg/", - "reference": "tags/4.0.0" + "reference": "tags/4.1.1" }, "dist": { "type": "zip", - "url": "https://downloads.wordpress.org/plugin/gutenberg.4.0.0.zip", + "url": "https://downloads.wordpress.org/plugin/gutenberg.4.1.1.zip", "reference": null, "shasum": null }, diff --git a/includes/cf-pro-client/classes/api/local/files.php b/includes/cf-pro-client/classes/api/local/files.php index dab6aff6b..fdb60568d 100644 --- a/includes/cf-pro-client/classes/api/local/files.php +++ b/includes/cf-pro-client/classes/api/local/files.php @@ -10,6 +10,7 @@ * @package calderawp\calderaforms\pro\api\local */ class files implements \Caldera_Forms_API_Route { + /** * @inheritdoc */ diff --git a/tests/Integration/Caldera_Forms_TransientTest.php b/tests/Integration/Caldera_Forms_TransientTest.php new file mode 100644 index 000000000..df8edcf62 --- /dev/null +++ b/tests/Integration/Caldera_Forms_TransientTest.php @@ -0,0 +1,95 @@ +assertSame( get_option('cftransdata_' . $id), $value ); + + } + /** + * + * @since 1.8.0 + * + * @covers \Caldera_Forms_Transient::get_transient() + */ + public function testGet_transient() + { + $id = uniqid('cf' ); + $value = 'foo'; + \Caldera_Forms_Transient::set_transient($id,$value,500 ); + $this->assertSame( \Caldera_Forms_Transient::get_transient($id), $value ); + } + + /** + * + * @since 1.8.0 + * + * @covers \Caldera_Forms_Transient::delete_transient() + */ + public function testDelete_transient() + { + $id = uniqid('cf' ); + $value = 'foo'; + \Caldera_Forms_Transient::set_transient($id,$value,500 ); + \Caldera_Forms_Transient::delete_transient($id ); + $this->assertFalse( get_option('cftransdata_' . $id), $value ); + $this->assertFalse( Caldera_Forms_Transient::get_transient($id) ); + } + + + /** + * + * @since 1.8.0 + * + * @covers \Caldera_Forms_Transient::cron_callback() + */ + public function testCron_callback() + { + $id = uniqid('cf' ); + $value = 'foo'; + \Caldera_Forms_Transient::set_transient($id,$value,500 ); + \Caldera_Forms_Transient::cron_callback([$id]); + $this->assertFalse( Caldera_Forms_Transient::get_transient($id) ); + } + + /** + * + * @since 1.8.0 + * + * @covers \Caldera_Forms_Transient::delete_at_submission_complete() + * @covers \Caldera_Forms_Transient::submission_complete() + */ + public function testDelete_at_submission_complete() + { + + $id = uniqid('cf' ); + $value = 'foo'; + $id2 = uniqid('cf' ); + $value2 = 'bar'; + \Caldera_Forms_Transient::set_transient($id,$value,500 ); + \Caldera_Forms_Transient::set_transient($id2,$value2,500 ); + + \Caldera_Forms_Transient::delete_at_submission_complete($id); + \Caldera_Forms_Transient::delete_at_submission_complete($id2); + \Caldera_Forms_Transient::submission_complete($id); + $this->assertFalse( Caldera_Forms_Transient::get_transient($id) ); + $this->assertFalse( Caldera_Forms_Transient::get_transient($id2) ); + } + +} + + diff --git a/tests/Integration/Features/ContainerServicesAreLoadedTest.php b/tests/Integration/Features/ContainerServicesAreLoadedTest.php new file mode 100644 index 000000000..757ce271d --- /dev/null +++ b/tests/Integration/Features/ContainerServicesAreLoadedTest.php @@ -0,0 +1,37 @@ +container = false; + parent::setUp(); // TODO: Change the autogenerated stub + } + + /** @group now */ + public function testDidAction(){ + $this->assertEquals( 1, did_action( 'caldera_forms_core_init')); + $this->assertEquals( 1,did_action( 'caldera_forms_v2_init')); + } + /** @group now */ + public function testHasQueueService(){ + $this->assertInstanceOf( Queue::class, caldera_forms_get_v2_container()->getService(QueueService::class) ); + } + + /** @group now */ + public function testHasQueueSchedulerService(){ + $this->assertInstanceOf( Scheduler::class, caldera_forms_get_v2_container()->getService(QueueSchedulerService::class) ); + } +} \ No newline at end of file diff --git a/tests/Integration/FunctionsTest.php b/tests/Integration/FunctionsTest.php new file mode 100644 index 000000000..5f70e5e15 --- /dev/null +++ b/tests/Integration/FunctionsTest.php @@ -0,0 +1,31 @@ +assertSame(caldera_forms_get_v2_container(),caldera_forms_get_v2_container() ); + } + + /** + * @since 1.8.0 + * + * @covers \caldera_forms_get_v2_container() + */ + public function testContainerFunctionReturnsInstance(){ + + $this->assertInstanceOf(CalderaFormsV2::class,caldera_forms_get_v2_container() ); + } +} \ No newline at end of file diff --git a/tests/Integration/Jobs/DatabaseConnectionTest.php b/tests/Integration/Jobs/DatabaseConnectionTest.php new file mode 100644 index 000000000..142873a3e --- /dev/null +++ b/tests/Integration/Jobs/DatabaseConnectionTest.php @@ -0,0 +1,49 @@ +schedule(new MockJob('foo') ); + $r = $wpdb->get_results( sprintf('SELECT * FROM %s;', $wpdb->prefix. DatabaseConnection::QUEUED_JOBS_TABLE) ); + $this->assertNotEmpty( $r[0]); + $this->assertEquals( 0,$r[0]->attempts); + + $this->assertSame(1,$this->getJobCount($wpdb)); + } + + /** + * @since 1.8.0 + * + * @param \wpdb $wpdb + * + * @return int + */ + protected function getJobCount(\wpdb $wpdb){ + + $r = $wpdb->get_results( sprintf('SELECT COUNT(*) FROM %s;', $wpdb->prefix. DatabaseConnection::QUEUED_JOBS_TABLE),ARRAY_A ); + return (int) $r[0]['COUNT(*)']; + } +} diff --git a/tests/Integration/Jobs/DeleteTransientJobTest.php b/tests/Integration/Jobs/DeleteTransientJobTest.php new file mode 100644 index 000000000..5bb860d21 --- /dev/null +++ b/tests/Integration/Jobs/DeleteTransientJobTest.php @@ -0,0 +1,25 @@ +handle(); + $this->assertFalse(\Caldera_Forms_Transient::get_transient($id)); + } +} diff --git a/tests/Integration/Jobs/SchedulerTest.php b/tests/Integration/Jobs/SchedulerTest.php new file mode 100644 index 000000000..a86a76bd7 --- /dev/null +++ b/tests/Integration/Jobs/SchedulerTest.php @@ -0,0 +1,195 @@ +callbackRan = FALSE; + + parent::setUp(); + } + + + /** + * Callback function for mock job + * + * @since 1.8.0 + */ + public function callbackFunction() + { + $this->callbackRan = TRUE; + } + + /** + * + * @since 1.8.0 + * + * @covers MockQueueConnection::getRemainingJobCount() + */ + public function testMockConnection() + { + $connection = new MockQueueConnection(); + $connection->push(new MockJob()); + $this->assertSame(1, $connection->getRemainingJobCount()); + } + + /** + * + * @since 1.8.0 + * + * @covers Scheduler::getWorker() + */ + public function testGetWorker() + { + $queue = new Queue(new MockQueueConnection()); + $scheduler = new Scheduler($queue); + $this->assertInstanceOf(Worker::class, $scheduler->getWorker()); + + + } + + /** + * + * @since 1.8.0 + * + * @covers Scheduler::runWithCron() + * @covers Scheduler::schedule() + */ + public function testSchedule() + { + $queue = new Queue(new MockQueueConnection()); + $scheduler = new Scheduler($queue); + $job = new MockJob(); + $job->setCallback($this->callbackFunction()); + $scheduler->schedule($job); + $scheduler->runWithCron(); + $this->assertTrue($this->callbackRan); + } + + /** + * + * @since 1.8.0 + * + * @covers Scheduler::schedule() + * @covers Scheduler::getWorker() + */ + public function testRunWithCron() + { + $queue = new Queue(new MockQueueConnection()); + $scheduler = new Scheduler($queue); + $job = new MockJob(); + $job->setCallback($this->callbackFunction()); + $scheduler->schedule($job); + $scheduler->runWithCron(); + $this->assertTrue($this->callbackRan); + } + + + /** + * @since 1.8.0 + * + * @covers \calderawp\calderaforms\cf2\Jobs\Scheduler::runJobs(); + */ + public function testWorkJobs() + { + $connection = new MockQueueConnection(); + $queue = new Queue($connection); + $scheduler = new Scheduler($queue); + $job = new MockJob(); + $job->setCallback($this->callbackFunction()); + $scheduler->schedule($job); + $scheduler->schedule($job); + $scheduler->schedule($job); + $numberOfJobs = 3; + $this->assertEquals($numberOfJobs, $scheduler->runJobs($numberOfJobs)); + $this->assertEquals(0, $connection->getRemainingJobCount()); + } + + + /** + * @since 1.8.0 + * + * @covers \calderawp\calderaforms\cf2\Jobs\Scheduler::runJobs(); + */ + public function testWorksRightNumberOfJobs() + { + $connection = new MockQueueConnection(); + $queue = new Queue($connection); + $scheduler = new Scheduler($queue); + $job = new MockJob(); + $job->setCallback($this->callbackFunction()); + $scheduler->schedule($job); + $scheduler->schedule($job); + $scheduler->schedule($job); + $numberOfJobs = 2; + $this->assertEquals($numberOfJobs, $scheduler->runJobs($numberOfJobs)); + $this->assertEquals(1, $connection->getRemainingJobCount()); + } + + + /** + * @since 1.8.0 + * + * @covers \calderawp\calderaforms\cf2\Jobs\Scheduler::runJobs(); + */ + public function testWorksJobsUntilOutOfPossibleJobs() + { + $connection = new MockQueueConnection(); + $queue = new Queue($connection); + $scheduler = new Scheduler($queue); + $job = new MockJob(); + $job->setCallback($this->callbackFunction()); + $scheduler->schedule($job); + $scheduler->schedule($job); + $scheduler->schedule($job); + $numberOfJobs = 5; + $this->assertEquals(3, $scheduler->runJobs($numberOfJobs)); + $this->assertEquals(0, $connection->getRemainingJobCount()); + } + + /** + * @since 1.8.0 + * + * @covers \calderawp\calderaforms\cf2\Jobs\Scheduler::runJobs(); + */ + public function testWorksLeftOverJobs() + { + $connection = new MockQueueConnection(); + $queue = new Queue($connection); + $scheduler = new Scheduler($queue); + $job = new MockJob(); + $job->setCallback($this->callbackFunction()); + $scheduler->schedule($job); + $scheduler->schedule($job); + $scheduler->schedule($job); + $numberOfJobs = 2; + $this->assertEquals($numberOfJobs, $scheduler->runJobs($numberOfJobs)); + $this->assertEquals(1, $connection->getRemainingJobCount()); + + $this->assertEquals(1, $scheduler->runJobs($numberOfJobs)); + $this->assertEquals(0, $connection->getRemainingJobCount()); + + } + +} diff --git a/tests/Integration/RestApi/RunQueueTest.php b/tests/Integration/RestApi/RunQueueTest.php new file mode 100644 index 000000000..18a2c930a --- /dev/null +++ b/tests/Integration/RestApi/RunQueueTest.php @@ -0,0 +1,379 @@ +getUri() ); + $request = new \WP_REST_Request('GET', '/cf-api/v3'); + $response = rest_get_server()->dispatch($request); + $this->assertTrue( + array_key_exists( $uri, $response->get_data()[ 'routes'] ) + ); + $this->assertTrue( + in_array( 'POST', $response->get_data()[ 'routes'][ $uri ]['methods'] ) + ); + + } + + /** + * @since 1.8.0 + * + * @covers \calderawp\calderaforms\cf2\RestApi\RunQueue::runQueue() + */ + public function testRunQueue() + { + $endpoint = new RunQueue(); + $uri = sprintf('/cf-api/v3/%s', $endpoint->getUri() ); + + $id1 = uniqid('a'); + $id2 = uniqid('b'); + \Caldera_Forms_Transient::set_transient($id1,rand()); + \Caldera_Forms_Transient::set_transient($id2,rand()); + + $request = new \WP_REST_Request('POST', $uri); + $request->set_param( 'jobs', 5 ); + $endpoint->runQueue($request); + } + + /** + * @since 1.8.0 + * + * @covers \calderawp\calderaforms\cf2\RestApi\AuthorizesRestApiRequestWithCfProKeys::checkKeys() + * @covers \calderawp\calderaforms\cf2\RestApi\AuthorizesRestApiRequestWithCfProKeys::getToken() + * @covers \calderawp\calderaforms\cf2\RestApi\AuthorizesRestApiRequestWithCfProKeys::getPublic() + */ + public function testCheckKeysWithHeaders() + { + + container::get_instance()->get_settings()->set_api_public('pub' ); + container::get_instance()->get_settings()->set_api_secret('secret' ); + + $endpoint = new RunQueue(); + $uri = sprintf('/cf-api/v3/%s', $endpoint->getUri() ); + + $token = container::get_instance()->get_settings()->get_api_keys()->get_token(); + + $request = new \WP_REST_Request('POST', $uri); + $request->set_header( 'X-CS-PUBLIC','pub' ); + $request->set_header( 'X-CS-TOKEN',$token ); + $request->set_param( 'jobs', 5 ); + $this->assertTrue( $endpoint->checkKeys($request ) ); + + } + + /** + * @since 1.8.0 + * + * @covers \calderawp\calderaforms\cf2\RestApi\AuthorizesRestApiRequestWithCfProKeys::getPublic() + */ + public function testGetPublicWithParam(){ + $endpoint = new RunQueue(); + $uri = sprintf('/cf-api/v3/%s', $endpoint->getUri() ); + $request = new \WP_REST_Request('POST', $uri); + $request->set_param( 'public','pub' ); + $this->assertEquals( 'pub', $endpoint->getPublic($request)); + } + + /** + * @since 1.8.0 + * + * @covers \calderawp\calderaforms\cf2\RestApi\AuthorizesRestApiRequestWithCfProKeys::getPublic() + */ + public function testGetPublicWithHeader(){ + $endpoint = new RunQueue(); + $uri = sprintf('/cf-api/v3/%s', $endpoint->getUri() ); + $request = new \WP_REST_Request('POST', $uri); + $request->set_header( 'X-CS-PUBLIC','pub' ); + $this->assertEquals( 'pub', $endpoint->getPublic($request)); + } + + /** + * @since 1.8.0 + * + * @covers \calderawp\calderaforms\cf2\RestApi\AuthorizesRestApiRequestWithCfProKeys::getToken() + */ + public function testGetTokenWithParam(){ + $endpoint = new RunQueue(); + $uri = sprintf('/cf-api/v3/%s', $endpoint->getUri() ); + $request = new \WP_REST_Request('POST', $uri); + $request->set_param( 'token','ttt' ); + $this->assertEquals( 'ttt', $endpoint->getToken($request)); + } + + /** + * @since 1.8.0 + * + * @covers \calderawp\calderaforms\cf2\RestApi\AuthorizesRestApiRequestWithCfProKeys::getToken() + */ + public function testGetTokenWithHeader(){ + $endpoint = new RunQueue(); + $uri = sprintf('/cf-api/v3/%s', $endpoint->getUri() ); + $request = new \WP_REST_Request('POST', $uri); + $request->set_header( 'X-CS-TOKEN','ttt' ); + $this->assertEquals( 'ttt', $endpoint->getToken($request)); + } + + /** + * @since 1.8.0 + * + * @covers \calderawp\calderaforms\cf2\RestApi\AuthorizesRestApiRequestWithCfProKeys::checkKeys() + * @covers \calderawp\calderaforms\cf2\RestApi\AuthorizesRestApiRequestWithCfProKeys::getToken() + * @covers \calderawp\calderaforms\cf2\RestApi\AuthorizesRestApiRequestWithCfProKeys::getPublic() + */ + public function testCheckKeysWithRequestParams() + { + + container::get_instance()->get_settings()->set_api_public('pub' ); + container::get_instance()->get_settings()->set_api_secret('secret' ); + + $endpoint = new RunQueue(); + $uri = sprintf('/cf-api/v3/%s', $endpoint->getUri() ); + + $token = container::get_instance()->get_settings()->get_api_keys()->get_token(); + + $request = new \WP_REST_Request('POST', $uri); + $request->set_param( 'public','pub' ); + $request->set_param( 'token',$token ); + $request->set_param( 'jobs', 5 ); + $this->assertTrue( $endpoint->checkKeys($request ) ); + + } + /** + * @since 1.8.0 + * + * @covers \calderawp\calderaforms\cf2\RestApi\AuthorizesRestApiRequestWithCfProKeys::checkKeys() + * @covers \calderawp\calderaforms\cf2\RestApi\AuthorizesRestApiRequestWithCfProKeys::getToken() + * @covers \calderawp\calderaforms\cf2\RestApi\AuthorizesRestApiRequestWithCfProKeys::getPublic() + */ + public function testCheckKeysWithInvalidHeaders() + { + + container::get_instance()->get_settings()->set_api_public('pub' ); + container::get_instance()->get_settings()->set_api_secret('secret' ); + + $endpoint = new RunQueue(); + $uri = sprintf('/cf-api/v3/%s', $endpoint->getUri() ); + + $request = new \WP_REST_Request('POST', $uri); + $request->set_header( 'X-CS-PUBLIC','lll' ); + $request->set_header( 'X-CS-TOKEN','l' ); + $request->set_param( 'jobs', 5 ); + $this->assertFalse( $endpoint->checkKeys($request ) ); + + } + /** + * @since 1.8.0 + * + * @covers \calderawp\calderaforms\cf2\RestApi\AuthorizesRestApiRequestWithCfProKeys::checkKeys() + * @covers \calderawp\calderaforms\cf2\RestApi\AuthorizesRestApiRequestWithCfProKeys::getToken() + * @covers \calderawp\calderaforms\cf2\RestApi\AuthorizesRestApiRequestWithCfProKeys::getPublic() + */ + public function testcheckKeysWithInvalidRequestParams() + { + + container::get_instance()->get_settings()->set_api_public('pub' ); + container::get_instance()->get_settings()->set_api_secret('secret' ); + + $endpoint = new RunQueue(); + $uri = sprintf('/cf-api/v3/%s', $endpoint->getUri() ); + + $token = container::get_instance()->get_settings()->get_api_keys()->get_token(); + + $request = new \WP_REST_Request('POST', $uri); + $request->set_param( 'public','pubzz' ); + $request->set_param( 'token',uniqid('xc') ); + $request->set_param( 'jobs', 5 ); + $this->assertFalse( $endpoint->checkKeys($request ) ); + + } + /** + * @since 1.8.0 + * + * @covers \calderawp\calderaforms\cf2\RestApi\AuthorizesRestApiRequestWithCfProKeys::checkKeys() + * @covers \calderawp\calderaforms\cf2\RestApi\AuthorizesRestApiRequestWithCfProKeys::getToken() + * @covers \calderawp\calderaforms\cf2\RestApi\AuthorizesRestApiRequestWithCfProKeys::getPublic() + */ + public function testcheckKeysWithValidTokenNoPublicHeaders() + { + + container::get_instance()->get_settings()->set_api_public('pub' ); + container::get_instance()->get_settings()->set_api_secret('secret' ); + + $endpoint = new RunQueue(); + $uri = sprintf('/cf-api/v3/%s', $endpoint->getUri() ); + + $token = container::get_instance()->get_settings()->get_api_keys()->get_token(); + + $request = new \WP_REST_Request('POST', $uri); + $request->set_header( 'X-CS-TOKEN',$token ); + $request->set_param( 'jobs', 5 ); + $this->assertFalse( $endpoint->checkKeys($request ) ); + + } + /** + * @since 1.8.0 + * + * @covers \calderawp\calderaforms\cf2\RestApi\AuthorizesRestApiRequestWithCfProKeys::checkKeys() + * @covers \calderawp\calderaforms\cf2\RestApi\AuthorizesRestApiRequestWithCfProKeys::getToken() + * @covers \calderawp\calderaforms\cf2\RestApi\AuthorizesRestApiRequestWithCfProKeys::getPublic() + */ + public function testCheckKeysWithValidTokenNoPublic() + { + + container::get_instance()->get_settings()->set_api_public('pub' ); + container::get_instance()->get_settings()->set_api_secret('secret' ); + + $endpoint = new RunQueue(); + $uri = sprintf('/cf-api/v3/%s', $endpoint->getUri() ); + + $token = container::get_instance()->get_settings()->get_api_keys()->get_token(); + + $request = new \WP_REST_Request('POST', $uri); + $request->set_param( 'token',$token ); + $request->set_param( 'jobs', 5 ); + $this->assertFalse( $endpoint->checkKeys($request ) ); + + } + + /** + * @since 1.8.0 + * + * @covers \calderawp\calderaforms\cf2\RestApi\RunQueue::runQueue() + * @covers \calderawp\calderaforms\cf2\RestApi\AuthorizesRestApiRequestWithCfProKeys::checkKeys() + * @covers \calderawp\calderaforms\cf2\RestApi\AuthorizesRestApiRequestWithCfProKeys::getToken() + * @covers \calderawp\calderaforms\cf2\RestApi\AuthorizesRestApiRequestWithCfProKeys::getPublic() + */ + public function testFullDispatchRequestWithInvalidAuth(){ + container::get_instance()->get_settings()->set_api_public('pub' ); + container::get_instance()->get_settings()->set_api_secret('secret' ); + + $endpoint = new RunQueue(); + $uri = sprintf('/cf-api/v3/%s', $endpoint->getUri() ); + + $request = new \WP_REST_Request('POST', $uri); + $request->set_param( 'token','fff' ); + $request->set_param( 'jobs', 5 ); + + $response = rest_get_server()->dispatch($request); + $this->assertSame( 401, $response->get_status() ); + + } + + /** + * @since 1.8.0 + * + * @covers \calderawp\calderaforms\cf2\RestApi\RunQueue::runQueue() + * @covers \calderawp\calderaforms\cf2\RestApi\AuthorizesRestApiRequestWithCfProKeys::checkKeys() + * @covers \calderawp\calderaforms\cf2\RestApi\AuthorizesRestApiRequestWithCfProKeys::getToken() + * @covers \calderawp\calderaforms\cf2\RestApi\AuthorizesRestApiRequestWithCfProKeys::getPublic() + */ public function testDispatchWithNoJobs200StatusCode(){ + container::get_instance()->get_settings()->set_api_public('pub' ); + container::get_instance()->get_settings()->set_api_secret('secret' ); + + $endpoint = new RunQueue(); + $uri = sprintf('/cf-api/v3/%s', $endpoint->getUri() ); + + $token = container::get_instance()->get_settings()->get_api_keys()->get_token(); + + $request = new \WP_REST_Request('POST', $uri); + $request->set_header( 'X-CS-PUBLIC','pub' ); + $request->set_header( 'X-CS-TOKEN',$token ); + $request->set_param( 'jobs', 5 ); + + $response = rest_get_server()->dispatch($request); + $this->assertSame( 200, $response->get_status() ); + + } + + /** + * @since 1.8.0 + * + * @covers \calderawp\calderaforms\cf2\RestApi\RunQueue::runQueue() + * @covers \calderawp\calderaforms\cf2\RestApi\AuthorizesRestApiRequestWithCfProKeys::checkKeys() + * @covers \calderawp\calderaforms\cf2\RestApi\AuthorizesRestApiRequestWithCfProKeys::getToken() + * @covers \calderawp\calderaforms\cf2\RestApi\AuthorizesRestApiRequestWithCfProKeys::getPublic() + */ public function testDispatchWithJobs201StatusCode(){ + $id1 = uniqid('r2' ); + $id2 = uniqid('d2' ); + \Caldera_Forms_Transient::set_transient($id1,'1' ); + \Caldera_Forms_Transient::set_transient($id2,'1a' ); + /** @var \calderawp\calderaforms\cf2\Jobs\Scheduler $scheduler */ + $scheduler = caldera_forms_get_v2_container()->getService(\calderawp\calderaforms\cf2\Services\QueueSchedulerService::class); + $scheduler->schedule(new DeleteTransientJob($id1 )); + $scheduler->schedule(new DeleteTransientJob($id2 )); + + + container::get_instance()->get_settings()->set_api_public('pub' ); + container::get_instance()->get_settings()->set_api_secret('secret' ); + + $endpoint = new RunQueue(); + $uri = sprintf('/cf-api/v3/%s', $endpoint->getUri() ); + + $token = container::get_instance()->get_settings()->get_api_keys()->get_token(); + + $request = new \WP_REST_Request('POST', $uri); + $request->set_header( 'X-CS-PUBLIC','pub' ); + $request->set_header( 'X-CS-TOKEN',$token ); + + $request->set_param( 'jobs', 5 ); + + $response = rest_get_server()->dispatch($request); + $this->assertSame( 201, $response->get_status() ); + + } + + /** + * @since 1.8.0 + * + * @covers \calderawp\calderaforms\cf2\RestApi\RunQueue::runQueue() + * @covers \calderawp\calderaforms\cf2\RestApi\AuthorizesRestApiRequestWithCfProKeys::checkKeys() + * @covers \calderawp\calderaforms\cf2\RestApi\AuthorizesRestApiRequestWithCfProKeys::getToken() + * @covers \calderawp\calderaforms\cf2\RestApi\AuthorizesRestApiRequestWithCfProKeys::getPublic() + */ + public function testResponse(){ + $id1 = uniqid('r2' ); + $id2 = uniqid('d2' ); + \Caldera_Forms_Transient::set_transient($id1,'1' ); + \Caldera_Forms_Transient::set_transient($id2,'1a' ); + /** @var \calderawp\calderaforms\cf2\Jobs\Scheduler $scheduler */ + $scheduler = caldera_forms_get_v2_container()->getService(\calderawp\calderaforms\cf2\Services\QueueSchedulerService::class); + $scheduler->schedule(new DeleteTransientJob($id1 )); + $scheduler->schedule(new DeleteTransientJob($id2 )); + + + container::get_instance()->get_settings()->set_api_public('pub' ); + container::get_instance()->get_settings()->set_api_secret('secret' ); + + $endpoint = new RunQueue(); + $uri = sprintf('/cf-api/v3/%s', $endpoint->getUri() ); + + $token = container::get_instance()->get_settings()->get_api_keys()->get_token(); + + $request = new \WP_REST_Request('POST', $uri); + $request->set_header( 'X-CS-PUBLIC','pub' ); + $request->set_header( 'X-CS-TOKEN',$token ); + + $request->set_param( 'jobs', 5 ); + + $response = rest_get_server()->dispatch($request); + $data = $response->get_data(); + $this->assertArrayHasKey( 'totalJobs', $data ); + $this->assertSame( 2, $data['totalJobs'] ); + + } +} diff --git a/tests/Integration/Service/QueueSchedulerServiceTest.php b/tests/Integration/Service/QueueSchedulerServiceTest.php new file mode 100644 index 000000000..6b696671c --- /dev/null +++ b/tests/Integration/Service/QueueSchedulerServiceTest.php @@ -0,0 +1,53 @@ +assertSame(QueueSchedulerService::class, $service->getIdentifier()); + } + + /** + * + * @since 1.8.0 + * + * @covers \calderawp\calderaforms\cf2\Services\QueueSchedulerService::isSingleton() + */ + public function testIsSingleton() + { + $service = new QueueSchedulerService(); + $this->assertTrue($service->isSingleton()); + } + + + /** + * + * @since 1.8.0 + * + * @covers \calderawp\calderaforms\cf2\Services\QueueSchedulerService::register() + */ + public function testRegister() + { + $service = new QueueSchedulerService(); + $container = $this->getContainer(); + + $this->assertInstanceOf(Scheduler::class, $service->register($container)); + + } + +} diff --git a/tests/Integration/Service/QueueServiceTest.php b/tests/Integration/Service/QueueServiceTest.php new file mode 100644 index 000000000..304cec070 --- /dev/null +++ b/tests/Integration/Service/QueueServiceTest.php @@ -0,0 +1,53 @@ +getContainer(); + $service = new QueueService(); + + $container->registerService($service,true ); + $this->assertInstanceOf( Queue::class, $container->getService(QueueService::class) ); + + + } + + /** + * + * @since 1.8.0 + * + * @covers \calderawp\calderaforms\cf2\Services\QueueService::isSingleton() + */ + public function testIsSingleton() + { + $service = new QueueService(); + $this->assertTrue( $service->isSingleton() ); + } + + /** + * + * @since 1.8.0 + * + * @covers \calderawp\calderaforms\cf2\Services\QueueService::getIdentifier() + */ + public function testGetIdentifier() + { + $service = new QueueService(); + $this->assertTrue( is_string($service->getIdentifier()) ); + $this->assertSame(QueueService::class, $service->getIdentifier() ); + } +} diff --git a/tests/Unit/CalderaFormsV2Test.php b/tests/Unit/CalderaFormsV2Test.php index 83290bb55..4b4de685c 100644 --- a/tests/Unit/CalderaFormsV2Test.php +++ b/tests/Unit/CalderaFormsV2Test.php @@ -5,7 +5,9 @@ use calderawp\calderaforms\cf2\CalderaFormsV2; use calderawp\calderaforms\cf2\Fields\FieldTypeFactory; use calderawp\calderaforms\cf2\Hooks; +use calderawp\calderaforms\cf2\Services\QueueService; use calderawp\calderaforms\cf2\Transients\Cf1TransientsApi; +use calderawp\calderaforms\Tests\Util\Mocks\MockService; class CalderaFormsV2Test extends TestCase { @@ -79,4 +81,23 @@ public function testGetCorDir(){ $containerReal->setCoreDir($coreDir); $this->assertEquals( $coreDir, $containerReal->getCoreDir() ); } + + + /** + * Test service registration + * + * @since 1.8.0 + * + * @covers \calderawp\calderaforms\cf2\CalderaFormsV2::registerService(); + * @covers \calderawp\calderaforms\cf2\CalderaFormsV2::getService(); + */ + public function testRegisterService(){ + $coreDir = 'foo/bar'; + $containerMock = $this->getContainer(); + $containerMock->setCoreDir($coreDir); + + $service = new MockService(); + $containerMock->registerService($service,true ); + $this->assertInstanceOf( \stdClass::class, $containerMock->getService(MockService::class) ); + } } diff --git a/tests/Util/Mocks/MockJob.php b/tests/Util/Mocks/MockJob.php new file mode 100644 index 000000000..3390a7e9f --- /dev/null +++ b/tests/Util/Mocks/MockJob.php @@ -0,0 +1,27 @@ +id = $id; + } + + public function setCallback($callBackFunc){ + $this->callBackFunc = $callBackFunc; + } + public function handle() + { + call_user_func($this->callBackFunc); + } +} \ No newline at end of file diff --git a/tests/Util/Mocks/MockQueueConnection.php b/tests/Util/Mocks/MockQueueConnection.php new file mode 100644 index 000000000..d38859e3e --- /dev/null +++ b/tests/Util/Mocks/MockQueueConnection.php @@ -0,0 +1,33 @@ +jobs[] = $job; + return true; + } + + + public function getRemainingJobCount(){ + + return is_array( $this->jobs) ? count( $this->jobs ) : 0; + } + public function pop() + { + if( ! is_array( $this->jobs ) ){ + return false; + } + return array_pop($this->jobs ); + } +} \ No newline at end of file diff --git a/tests/Util/Mocks/MockService.php b/tests/Util/Mocks/MockService.php new file mode 100644 index 000000000..2e2e5d04e --- /dev/null +++ b/tests/Util/Mocks/MockService.php @@ -0,0 +1,25 @@ +roy = 'Sivan'; + return $obj; + } + + public function isSingleton() + { + return true; + } +} \ No newline at end of file diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 68dbe8843..603b51189 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -16,9 +16,6 @@ */ function _manually_load_plugin() { require dirname( dirname( __FILE__ ) ) . '/caldera-core.php'; - add_action( 'caldera_forms_includes_complete', function(){ - (new \calderawp\calderaforms\Tests\Util\ImportForms() )->import(); - }); } tests_add_filter( 'muplugins_loaded', '_manually_load_plugin' ); diff --git a/tests/test-forms-api.php b/tests/test-forms-api.php index b6ceb522d..6734fef4e 100644 --- a/tests/test-forms-api.php +++ b/tests/test-forms-api.php @@ -3,16 +3,16 @@ class Test_Caldera_Forms_API extends Caldera_Forms_Test_Case { - /** - * Compare the database abstraction to the froms API + /** + * Compare the database abstraction to the forms API * * @since 1.7.0 - * + ** * @covers Caldera_Forms_Forms::get_forms() * @covers Caldera_Forms_Forms::get_stored_forms() */ public function testGetFormVsDbForm(){ - $form_one_id = $this->import_autoresponder_form(); + $form_one_id = $this->import_autoresponder_form(); $form_two_id = $this->import_contact_form(); $db_results = Caldera_Forms_DB_Form::get_instance()->get_all( true ); $db_ids = wp_list_pluck( $db_results, 'form_id' ); diff --git a/vendor/a5hleyrich/wp-queue/.gitignore b/vendor/a5hleyrich/wp-queue/.gitignore new file mode 100644 index 000000000..2ac3addc7 --- /dev/null +++ b/vendor/a5hleyrich/wp-queue/.gitignore @@ -0,0 +1,3 @@ +/vendor +composer.lock +coverage.clover \ No newline at end of file diff --git a/vendor/a5hleyrich/wp-queue/.scrutinizer.yml b/vendor/a5hleyrich/wp-queue/.scrutinizer.yml new file mode 100644 index 000000000..595180748 --- /dev/null +++ b/vendor/a5hleyrich/wp-queue/.scrutinizer.yml @@ -0,0 +1,6 @@ +tools: + php_code_sniffer: + config: + standard: WordPress + external_code_coverage: + timeout: 3600 \ No newline at end of file diff --git a/vendor/a5hleyrich/wp-queue/.travis.yml b/vendor/a5hleyrich/wp-queue/.travis.yml new file mode 100644 index 000000000..f2a8faf12 --- /dev/null +++ b/vendor/a5hleyrich/wp-queue/.travis.yml @@ -0,0 +1,16 @@ +language: php + +php: + - 5.6 + - 7.0 + - 7.1 + +install: + - composer install --prefer-dist --no-interaction --no-suggest + +script: + - phpunit --coverage-text --coverage-clover=coverage.clover + +after_script: + - wget https://scrutinizer-ci.com/ocular.phar + - php ocular.phar code-coverage:upload --format=php-clover coverage.clover \ No newline at end of file diff --git a/vendor/a5hleyrich/wp-queue/LICENSE.md b/vendor/a5hleyrich/wp-queue/LICENSE.md new file mode 100644 index 000000000..3a48a9c14 --- /dev/null +++ b/vendor/a5hleyrich/wp-queue/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Ashley Rich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/vendor/a5hleyrich/wp-queue/README.md b/vendor/a5hleyrich/wp-queue/README.md new file mode 100644 index 000000000..f81947f1a --- /dev/null +++ b/vendor/a5hleyrich/wp-queue/README.md @@ -0,0 +1,112 @@ +[![Build Status](https://travis-ci.org/A5hleyRich/wp-queue.svg?branch=master)](https://travis-ci.org/A5hleyRich/wp-queue) +[![Code Coverage](https://scrutinizer-ci.com/g/A5hleyRich/wp-queue/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/A5hleyRich/wp-queue/?branch=master) +[![Total Downloads](https://poser.pugx.org/a5hleyrich/wp-queue/downloads)](https://packagist.org/packages/a5hleyrich/wp-queue) +[![Latest Stable Version](https://poser.pugx.org/a5hleyrich/wp-queue/v/stable)](https://packagist.org/packages/a5hleyrich/wp-queue) +[![License](https://poser.pugx.org/a5hleyrich/wp-queue/license)](https://packagist.org/packages/a5hleyrich/wp-queue) + +## Prerequisites + +WP_Queue requires PHP __5.3+__. + +The following database tables need to be created: + +``` +CREATE TABLE {$wpdb->prefix}queue_jobs ( +id bigint(20) NOT NULL AUTO_INCREMENT, +job longtext NOT NULL, +attempts tinyint(3) NOT NULL DEFAULT 0, +reserved_at datetime DEFAULT NULL, +available_at datetime NOT NULL, +created_at datetime NOT NULL, +PRIMARY KEY (id) +``` + +``` +CREATE TABLE {$wpdb->prefix}queue_failures ( +id bigint(20) NOT NULL AUTO_INCREMENT, +job longtext NOT NULL, +error text DEFAULT NULL, +failed_at datetime NOT NULL, +PRIMARY KEY (id) +``` + +Alternatively, you can call the `wp_queue_install_tables()` helper function to install the tables. If using WP_Queue in a plugin you may opt to call the helper from within your `register_activation_hook`. + +## Jobs + +Job classes should extend the `WP_Queue\Job` class and normally only contain a `handle` method which is called when the job is processed by the queue worker. Any data required by the job should be passed to the constructor and assigned to a public property. This data will remain available once the job is retrieved from the queue. Let's look at an example job class: + +``` +user_id = $user_id; + } + + /** + * Handle job logic. + */ + public function handle() { + $user = get_user_by( 'ID', $this->user_id ); + + // Process the user... + } + +} +``` + +## Dispatching Jobs + +Jobs can be pushed to the queue like so: + +``` +wp_queue()->push( new Subscribe_User_Job( 12345 ) ); +``` + +You can create delayed jobs by passing an optional second parameter to the `push` method. This job will be delayed by 60 minutes: + +``` +wp_queue()->push( new Subscribe_User_Job( 12345 ), 3600 ); +``` + +## Cron Worker + +Jobs need to be processed by a queue worker. You can start a cron worker like so, which piggy backs onto WP cron: + +``` +wp_queue()->cron(); +``` + +You can also specify the number of times a job should be attempted before being marked as a failure. + +``` +wp_queue()->cron( 3 ); +``` + +## Local Development + +When developing locally you may want jobs processed instantly, instead of them being pushed to the queue. This can be useful for debugging jobs via Xdebug. Add the following filter to use the `sync` connection: + +``` +add_filter( ‘wp_queue_default_connection’, function() { + return ‘sync’; +} ); +``` + +## License + +WP Queue is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT). \ No newline at end of file diff --git a/vendor/a5hleyrich/wp-queue/composer.json b/vendor/a5hleyrich/wp-queue/composer.json new file mode 100644 index 000000000..1faea78dc --- /dev/null +++ b/vendor/a5hleyrich/wp-queue/composer.json @@ -0,0 +1,28 @@ +{ + "name": "a5hleyrich/wp-queue", + "description": "WordPress job queues", + "keywords": ["wordpress","queue","job"], + "license": "MIT", + "authors": [ + { + "name": "Ashley Rich", + "email": "hello@ashleyrich.com" + } + ], + "require": { + "php": ">=5.3.0", + "nesbot/carbon": "^1.22" + }, + "autoload": { + "psr-4": { + "WP_Queue\\": "src\\WP_Queue" + }, + "files": [ + "src/functions.php" + ] + }, + "require-dev": { + "phpunit/phpunit": "~5.7.0", + "10up/wp_mock": "0.2.0" + } +} diff --git a/vendor/a5hleyrich/wp-queue/phpunit.xml b/vendor/a5hleyrich/wp-queue/phpunit.xml new file mode 100644 index 000000000..af6f6175e --- /dev/null +++ b/vendor/a5hleyrich/wp-queue/phpunit.xml @@ -0,0 +1,16 @@ + + + + + ./tests + + + + + ./src + + ./vendor + + + + \ No newline at end of file diff --git a/vendor/a5hleyrich/wp-queue/src/WP_Queue/Connections/ConnectionInterface.php b/vendor/a5hleyrich/wp-queue/src/WP_Queue/Connections/ConnectionInterface.php new file mode 100644 index 000000000..8ac0ac09f --- /dev/null +++ b/vendor/a5hleyrich/wp-queue/src/WP_Queue/Connections/ConnectionInterface.php @@ -0,0 +1,65 @@ +database = $wpdb; + $this->jobs_table = $this->database->prefix . 'queue_jobs'; + $this->failures_table = $this->database->prefix . 'queue_failures'; + } + + /** + * Push a job onto the queue. + * + * @param Job $job + * @param int $delay + * + * @return bool|int + */ + public function push( Job $job, $delay = 0 ) { + $result = $this->database->insert( $this->jobs_table, array( + 'job' => serialize( $job ), + 'available_at' => $this->datetime( $delay ), + 'created_at' => $this->datetime(), + ) ); + + if ( ! $result ) { + return false; + } + + return $this->database->insert_id; + } + + /** + * Retrieve a job from the queue. + * + * @return bool|Job + */ + public function pop() { + $this->release_reserved(); + + $sql = $this->database->prepare( " + SELECT * FROM {$this->jobs_table} + WHERE reserved_at IS NULL + AND available_at <= %s + ORDER BY available_at + LIMIT 1 + ", $this->datetime() ); + + $raw_job = $this->database->get_row( $sql ); + + if ( is_null( $raw_job ) ) { + return false; + } + + $job = $this->vitalize_job( $raw_job ); + + $this->reserve( $job ); + + return $job; + } + + /** + * Delete a job from the queue. + * + * @param Job $job + * + * @return bool + */ + public function delete( $job ) { + $where = array( + 'id' => $job->id(), + ); + + if ( $this->database->delete( $this->jobs_table, $where ) ) { + return true; + } + + return false; + } + + /** + * Release a job back onto the queue. + * + * @param Job $job + * + * @return bool + */ + public function release( $job ) { + $data = array( + 'job' => serialize( $job ), + 'attempts' => $job->attempts(), + 'reserved_at' => null, + ); + $where = array( + 'id' => $job->id(), + ); + + if ( $this->database->update( $this->jobs_table, $data, $where ) ) { + return true; + } + + return false; + } + + /** + * Push a job onto the failure queue. + * + * @param Job $job + * @param Exception $exception + * + * @return bool + */ + public function failure( $job, Exception $exception ) { + $insert = $this->database->insert( $this->failures_table, array( + 'job' => serialize( $job ), + 'error' => $this->format_exception( $exception ), + 'failed_at' => $this->datetime(), + ) ); + + if ( $insert ) { + $this->delete( $job ); + + return true; + } + + return false; + } + + /** + * Get total jobs in the queue. + * + * @return int + */ + public function jobs() { + $sql = "SELECT COUNT(*) FROM {$this->jobs_table}"; + + return (int) $this->database->get_var( $sql ); + } + + /** + * Get total jobs in the failures queue. + * + * @return int + */ + public function failed_jobs() { + $sql = "SELECT COUNT(*) FROM {$this->failures_table}"; + + return (int) $this->database->get_var( $sql ); + } + + /** + * Reserve a job in the queue. + * + * @param Job $job + */ + protected function reserve( $job ) { + $data = array( + 'reserved_at' => $this->datetime(), + ); + + $this->database->update( $this->jobs_table, $data, array( + 'id' => $job->id(), + ) ); + } + + /** + * Release reserved jobs back onto the queue. + */ + protected function release_reserved() { + $expired = $this->datetime( -300 ); + + $sql = $this->database->prepare( " + UPDATE {$this->jobs_table} + SET attempts = attempts + 1, reserved_at = NULL + WHERE reserved_at <= %s", $expired ); + + $this->database->query( $sql ); + } + + /** + * Vitalize Job with latest data. + * + * @param mixed $raw_job + * + * @return Job + */ + protected function vitalize_job( $raw_job ) { + $job = unserialize( $raw_job->job ); + + $job->set_id( $raw_job->id ); + $job->set_attempts( $raw_job->attempts ); + $job->set_reserved_at( empty( $raw_job->reserved_at ) ? null : new Carbon( $raw_job->reserved_at ) ); + $job->set_available_at( new Carbon( $raw_job->available_at ) ); + $job->set_created_at( new Carbon( $raw_job->created_at ) ); + + return $job; + } + + /** + * Get MySQL datetime. + * + * @param int $offset Seconds, can pass negative int. + * + * @return string + */ + protected function datetime( $offset = 0 ) { + $timestamp = time() + $offset; + + return gmdate( 'Y-m-d H:i:s', $timestamp ); + } + + /** + * Format an exception error string. + * + * @param Exception $exception + * + * @return string + */ + protected function format_exception( Exception $exception ) { + $string = get_class( $exception ); + + if ( ! empty( $exception->getMessage() ) ) { + $string .= " : {$exception->getMessage()}"; + } + + if ( ! empty( $exception->getCode() ) ) { + $string .= " (#{$exception->getCode()})"; + } + + return $string; + } + +} \ No newline at end of file diff --git a/vendor/a5hleyrich/wp-queue/src/WP_Queue/Connections/RedisConnection.php b/vendor/a5hleyrich/wp-queue/src/WP_Queue/Connections/RedisConnection.php new file mode 100644 index 000000000..f6cda6bfe --- /dev/null +++ b/vendor/a5hleyrich/wp-queue/src/WP_Queue/Connections/RedisConnection.php @@ -0,0 +1,77 @@ +handle(); + + return true; + } + + /** + * Retrieve a job from the queue. + * + * @return bool|Job + */ + public function pop() { + return false; + } + + /** + * Delete a job from the queue. + * + * @param Job $job + * + * @return bool + */ + public function delete($job) { + return false; + } + + /** + * Release a job back onto the queue. + * + * @param Job $job + * + * @return bool + */ + public function release($job) { + return false; + } + + /** + * Push a job onto the failure queue. + * + * @param Job $job + * @param Exception $exception + * + * @return bool + */ + public function failure($job, Exception $exception) { + return false; + } + + /** + * Get total jobs in the queue. + * + * @return int + */ + public function jobs() { + return 0; + } + + /** + * Get total jobs in the failures queue. + * + * @return int + */ + public function failed_jobs() { + return 0; + } +} diff --git a/vendor/a5hleyrich/wp-queue/src/WP_Queue/Cron.php b/vendor/a5hleyrich/wp-queue/src/WP_Queue/Cron.php new file mode 100644 index 000000000..5bb2b40f7 --- /dev/null +++ b/vendor/a5hleyrich/wp-queue/src/WP_Queue/Cron.php @@ -0,0 +1,194 @@ +id = strtolower( str_replace( '\\', '_', $id ) ); + $this->worker = $worker; + $this->interval = $interval; + } + + /** + * Is the cron queue worker enabled? + * + * @return bool + */ + protected function is_enabled() { + if ( defined( 'DISABLE_WP_QUEUE_CRON' ) && DISABLE_WP_QUEUE_CRON ) { + return false; + } + + return true; + } + + /** + * Init cron class. + * + * @return bool + */ + public function init() { + if ( ! $this->is_enabled() ) { + return false; + } + + add_filter( 'cron_schedules', array( $this, 'schedule_cron' ) ); + add_action( $this->id, array( $this, 'cron_worker' ) ); + + if ( ! wp_next_scheduled( $this->id ) ) { + // Schedule health check + wp_schedule_event( time(), $this->id, $this->id ); + } + + return true; + } + + /** + * Add interval to cron schedules. + * + * @param array $schedules + * + * @return array + */ + public function schedule_cron( $schedules ) { + $schedules[ $this->id ] = array( + 'interval' => MINUTE_IN_SECONDS * $this->interval, + 'display' => sprintf( __( 'Every %d Minutes' ), $this->interval ), + ); + + return $schedules; + } + + /** + * Process any jobs in the queue. + */ + public function cron_worker() { + if ( $this->is_worker_locked() ) { + return; + } + + $this->start_time = time(); + + $this->lock_worker(); + + while ( ! $this->time_exceeded() && ! $this->memory_exceeded() ) { + if ( ! $this->worker->process() ) { + break; + } + } + + $this->unlock_worker(); + } + + /** + * Is the cron worker locked? + * + * @return bool + */ + protected function is_worker_locked() { + return (bool) get_site_transient( $this->id ); + } + + /** + * Lock the cron worker. + */ + protected function lock_worker() { + set_site_transient( $this->id, time(), 300 ); + } + + /** + * Unlock the cron worker. + */ + protected function unlock_worker() { + delete_site_transient( $this->id ); + } + + /** + * Memory exceeded + * + * Ensures the worker process never exceeds 80% + * of the maximum allowed PHP memory. + * + * @return bool + */ + protected function memory_exceeded() { + $memory_limit = $this->get_memory_limit() * 0.8; // 80% of max memory + $current_memory = memory_get_usage( true ); + $return = false; + + if ( $current_memory >= $memory_limit ) { + $return = true; + } + + return apply_filters( 'wp_queue_cron_memory_exceeded', $return ); + } + + /** + * Get memory limit. + * + * @return int + */ + protected function get_memory_limit() { + if ( function_exists( 'ini_get' ) ) { + $memory_limit = ini_get( 'memory_limit' ); + } else { + $memory_limit = '256M'; + } + + if ( ! $memory_limit || - 1 == $memory_limit ) { + // Unlimited, set to 1GB + $memory_limit = '1000M'; + } + + return intval( $memory_limit ) * 1024 * 1024; + } + + /** + * Time exceeded + * + * Ensures the worker never exceeds a sensible time limit (20s by default). + * A timeout limit of 30s is common on shared hosting. + * + * @return bool + */ + protected function time_exceeded() { + $finish = $this->start_time + apply_filters( 'wp_queue_cron_time_limit', 20 ); // 20 seconds + $return = false; + + if ( time() >= $finish ) { + $return = true; + } + + return apply_filters( 'wp_queue_cron_time_exceeded', $return ); + } +} \ No newline at end of file diff --git a/vendor/a5hleyrich/wp-queue/src/WP_Queue/Exceptions/ConnectionNotFoundException.php b/vendor/a5hleyrich/wp-queue/src/WP_Queue/Exceptions/ConnectionNotFoundException.php new file mode 100644 index 000000000..e40063878 --- /dev/null +++ b/vendor/a5hleyrich/wp-queue/src/WP_Queue/Exceptions/ConnectionNotFoundException.php @@ -0,0 +1,9 @@ +id; + } + + /** + * Set job ID. + * + * @param int $id + */ + public function set_id( $id ) { + $this->id = $id; + } + + /** + * Get job attempts; + * + * @return int + */ + public function attempts() { + return $this->attempts; + } + + /** + * Set job attempts. + * + * @param int $attempts + */ + public function set_attempts( $attempts ) { + $this->attempts = $attempts; + } + + /** + * Get reserved at date. + * + * @return Carbon + */ + public function reserved_at() { + return $this->reserved_at; + } + + /** + * Set reserved at date. + * + * @param null|Carbon $reserved_at + */ + public function set_reserved_at( $reserved_at ) { + $this->reserved_at = $reserved_at; + } + + /** + * Get available at date. + * + * @return Carbon + */ + public function available_at() { + return $this->available_at; + } + + /** + * Set available at date. + * + * @param Carbon $available_at + */ + public function set_available_at( Carbon $available_at ) { + $this->available_at = $available_at; + } + + /** + * Get created at date. + * + * @return Carbon + */ + public function created_at() { + return $this->created_at; + } + + /** + * Set created at date. + * + * @param Carbon $created_at + */ + public function set_created_at( Carbon $created_at ) { + $this->created_at = $created_at; + } + + /** + * Flag job as released. + */ + public function release() { + $this->released = true; + $this->attempts += 1; + } + + /** + * Should the job be released back onto the queue? + * + * @return bool + */ + public function released() { + return $this->released; + } + + /** + * Flag job as failed. + */ + public function fail() { + $this->failed = true; + } + + /** + * Has the job failed? + * + * @return bool + */ + public function failed() { + return $this->failed; + } + + /** + * Determine which properties should be serialized. + * + * @return array + */ + public function __sleep() { + $object_props = get_object_vars( $this ); + $excluded_props = array( + 'id', + 'attempts', + 'reserved_at', + 'available_at', + 'created_at', + 'released', + 'failed', + ); + + foreach ( $excluded_props as $prop ) { + unset( $object_props[ $prop ] ); + } + + return array_keys( $object_props ); + } + +} \ No newline at end of file diff --git a/vendor/a5hleyrich/wp-queue/src/WP_Queue/Queue.php b/vendor/a5hleyrich/wp-queue/src/WP_Queue/Queue.php new file mode 100644 index 000000000..636df67e1 --- /dev/null +++ b/vendor/a5hleyrich/wp-queue/src/WP_Queue/Queue.php @@ -0,0 +1,67 @@ +connection = $connection; + } + + /** + * Push a job onto the queue; + * + * @param Job $job + * @param int $delay + * + * @return bool|int + */ + public function push( Job $job, $delay = 0 ) { + return $this->connection->push( $job, $delay ); + } + + /** + * Create a cron worker. + * + * @param int $attempts + * @param int $interval + * + * @return Cron + */ + public function cron( $attempts = 3, $interval = 5 ) { + if ( is_null( $this->cron ) ) { + $this->cron = new Cron( get_class( $this->connection ), $this->worker( $attempts ), $interval ); + $this->cron->init(); + } + + return $this->cron; + } + + /** + * Create a new worker. + * + * @param int $attempts + * + * @return Worker + */ + public function worker( $attempts ) { + return new Worker( $this->connection, $attempts ); + } +} \ No newline at end of file diff --git a/vendor/a5hleyrich/wp-queue/src/WP_Queue/QueueManager.php b/vendor/a5hleyrich/wp-queue/src/WP_Queue/QueueManager.php new file mode 100644 index 000000000..907e4c996 --- /dev/null +++ b/vendor/a5hleyrich/wp-queue/src/WP_Queue/QueueManager.php @@ -0,0 +1,66 @@ + new DatabaseConnection( $GLOBALS['wpdb'] ), + 'redis' => new RedisConnection(), + 'sync' => new SyncConnection(), + ); + + return apply_filters( 'wp_queue_connections', $connections ); + } +} \ No newline at end of file diff --git a/vendor/a5hleyrich/wp-queue/src/WP_Queue/Worker.php b/vendor/a5hleyrich/wp-queue/src/WP_Queue/Worker.php new file mode 100644 index 000000000..9b9728ce7 --- /dev/null +++ b/vendor/a5hleyrich/wp-queue/src/WP_Queue/Worker.php @@ -0,0 +1,71 @@ +connection = $connection; + $this->attempts = $attempts; + } + + /** + * Process a job on the queue. + * + * @return bool + */ + public function process() { + $job = $this->connection->pop(); + + if ( ! $job ) { + return false; + } + + $exception = null; + + try { + $job->handle(); + } catch ( Exception $exception ) { + $job->release(); + } + + if ( $job->attempts() >= $this->attempts ) { + if ( empty( $exception ) ) { + $exception = new WorkerAttemptsExceededException(); + } + + $job->fail(); + } + + if ( $job->failed() ) { + $this->connection->failure( $job, $exception ); + } else if ( $job->released() ) { + $this->connection->release( $job ); + } else { + $this->connection->delete( $job ); + } + + return true; + } + +} \ No newline at end of file diff --git a/vendor/a5hleyrich/wp-queue/src/functions.php b/vendor/a5hleyrich/wp-queue/src/functions.php new file mode 100644 index 000000000..37c8231d9 --- /dev/null +++ b/vendor/a5hleyrich/wp-queue/src/functions.php @@ -0,0 +1,57 @@ +hide_errors(); + $charset_collate = $wpdb->get_charset_collate(); + + $sql = "CREATE TABLE {$wpdb->prefix}queue_jobs ( + id bigint(20) NOT NULL AUTO_INCREMENT, + job longtext NOT NULL, + attempts tinyint(3) NOT NULL DEFAULT 0, + reserved_at datetime DEFAULT NULL, + available_at datetime NOT NULL, + created_at datetime NOT NULL, + PRIMARY KEY (id) + ) $charset_collate;"; + + dbDelta( $sql ); + + $sql = "CREATE TABLE {$wpdb->prefix}queue_failures ( + id bigint(20) NOT NULL AUTO_INCREMENT, + job longtext NOT NULL, + error text DEFAULT NULL, + failed_at datetime NOT NULL, + PRIMARY KEY (id) + ) $charset_collate;"; + + dbDelta( $sql ); + } +} \ No newline at end of file diff --git a/vendor/a5hleyrich/wp-queue/tests/TestDatabaseConnection.php b/vendor/a5hleyrich/wp-queue/tests/TestDatabaseConnection.php new file mode 100644 index 000000000..f2d960737 --- /dev/null +++ b/vendor/a5hleyrich/wp-queue/tests/TestDatabaseConnection.php @@ -0,0 +1,139 @@ +wpdb = Mockery::spy( 'WPDB' );; + $this->wpdb->prefix = "wp_"; + } + + public function tearDown() { + WP_Mock::tearDown(); + } + + public function test_push_success() { + $insert_id = 12345; + $this->wpdb->shouldReceive( 'insert' )->once()->andReturn( 1 ); + $this->wpdb->insert_id = $insert_id; + + $instance = new DatabaseConnection( $this->wpdb ); + + $this->assertEquals( $insert_id, $instance->push( new TestJob() ) ); + } + + public function test_push_fail() { + $this->wpdb->shouldReceive( 'insert' )->once()->andReturn( false ); + + $instance = new DatabaseConnection( $this->wpdb ); + + $this->assertFalse( $instance->push( new TestJob() ) ); + } + + public function test_pop_success() { + $this->wpdb->shouldReceive( 'get_row' )->once()->andReturn( (object) array( + 'id' => 12345, + 'job' => serialize( new TestJob() ), + 'attempts' => 0, + 'reserved_at' => null, + 'available_at' => '2017-10-09 00:00:00', + 'created_at' => '2017-10-09 00:00:00', + ) ); + $instance = new DatabaseConnection( $this->wpdb ); + $job = $instance->pop(); + + $this->assertInstanceOf( TestJob::class, $job ); + $this->assertEquals( 12345, $job->id() ); + $this->assertEquals( 0, $job->attempts() ); + $this->assertNull( $job->reserved_at() ); + $this->assertInstanceOf( Carbon::class, $job->available_at() ); + $this->assertInstanceOf( Carbon::class, $job->created_at() ); + } + + public function test_pop_fail() { + $this->wpdb->shouldReceive( 'get_row' )->once()->andReturn( null ); + $instance = new DatabaseConnection( $this->wpdb ); + + $this->assertFalse( $instance->pop() ); + } + + public function test_delete_success() { + $this->wpdb->shouldReceive( 'delete' )->once()->andReturn( 1 ); + + $instance = new DatabaseConnection( $this->wpdb ); + + $this->assertTrue( $instance->delete( new TestJob() ) ); + } + + public function test_delete_fail() { + $this->wpdb->shouldReceive( 'delete' )->once()->andReturn( false ); + + $instance = new DatabaseConnection( $this->wpdb ); + + $this->assertFalse( $instance->delete( new TestJob() ) ); + } + + public function test_release_success() { + $this->wpdb->shouldReceive( 'update' )->once()->andReturn( 1 ); + + $instance = new DatabaseConnection( $this->wpdb ); + + $this->assertTrue( $instance->release( new TestJob() ) ); + } + + public function test_release_fail() { + $this->wpdb->shouldReceive( 'update' )->once()->andReturn( false ); + + $instance = new DatabaseConnection( $this->wpdb ); + + $this->assertFalse( $instance->release( new TestJob() ) ); + } + + public function test_failure_success() { + $this->wpdb->shouldReceive( 'insert' )->once()->andReturn( 1 ); + + $instance = new DatabaseConnection( $this->wpdb ); + + $this->assertTrue( $instance->failure( new TestJob(), new Exception() ) ); + } + + public function test_failure_fail() { + $this->wpdb->shouldReceive( 'insert' )->once()->andReturn( false ); + + $instance = new DatabaseConnection( $this->wpdb ); + + $this->assertFalse( $instance->failure( new TestJob(), new Exception() ) ); + } + + public function test_jobs() { + $count = rand( 1, 100 ); + $this->wpdb->shouldReceive( 'get_var' )->once()->andReturn( $count ); + + $instance = new DatabaseConnection( $this->wpdb ); + + $this->assertEquals( $count, $instance->jobs() ); + } + + public function test_failed_jobs() { + $count = rand( 1, 100 ); + $this->wpdb->shouldReceive( 'get_var' )->once()->andReturn( $count ); + + $instance = new DatabaseConnection( $this->wpdb ); + + $this->assertEquals( $count, $instance->failed_jobs() ); + } +} + +if ( ! class_exists( 'TestJob' ) ) { + class TestJob extends Job { + public function handle() {} + } +} \ No newline at end of file diff --git a/vendor/a5hleyrich/wp-queue/tests/TestFunctions.php b/vendor/a5hleyrich/wp-queue/tests/TestFunctions.php new file mode 100644 index 000000000..89e46192a --- /dev/null +++ b/vendor/a5hleyrich/wp-queue/tests/TestFunctions.php @@ -0,0 +1,23 @@ +prefix = "wp_"; + } + + public function tearDown() { + WP_Mock::tearDown(); + } + + public function test_wp_queue() { + $this->assertInstanceOf( Queue::class, wp_queue() ); + } +} \ No newline at end of file diff --git a/vendor/a5hleyrich/wp-queue/tests/TestJob.php b/vendor/a5hleyrich/wp-queue/tests/TestJob.php new file mode 100644 index 000000000..16f0aa4d1 --- /dev/null +++ b/vendor/a5hleyrich/wp-queue/tests/TestJob.php @@ -0,0 +1,30 @@ +instance = $this->getMockForAbstractClass( Job::class ); + } + + public function tearDown() { + WP_Mock::tearDown(); + } + + public function test_release() { + $this->assertFalse( $this->instance->released() ); + $this->instance->release(); + $this->assertTrue( $this->instance->released() ); + } + + public function test_fail() { + $this->assertFalse( $this->instance->failed() ); + $this->instance->fail(); + $this->assertTrue( $this->instance->failed() ); + } +} \ No newline at end of file diff --git a/vendor/a5hleyrich/wp-queue/tests/TestQueue.php b/vendor/a5hleyrich/wp-queue/tests/TestQueue.php new file mode 100644 index 000000000..c36134c53 --- /dev/null +++ b/vendor/a5hleyrich/wp-queue/tests/TestQueue.php @@ -0,0 +1,62 @@ +shouldReceive( 'push' )->once()->andReturn( $insert_id ); + + $queue = new Queue( $connection ); + + $this->assertEquals( $insert_id, $queue->push( new TestJob() ) ); + } + + public function test_push_fail() { + $connection = Mockery::mock( ConnectionInterface::class ); + $connection->shouldReceive( 'push' )->once()->andReturn( false ); + + $queue = new Queue( $connection ); + + $this->assertFalse( $queue->push( new TestJob() ) ); + } + + public function test_cron() { + $connection = Mockery::mock( ConnectionInterface::class ); + $queue = new Queue( $connection ); + + WP_Mock::userFunction( 'wp_next_scheduled', array( + 'return' => time(), + ) ); + + $this->assertInstanceOf( Cron::class, $queue->cron() ); + } + + public function test_worker() { + $connection = Mockery::mock( ConnectionInterface::class ); + $queue = new Queue( $connection ); + + $this->assertInstanceOf( Worker::class, $queue->worker( 3 ) ); + } +} + +if ( ! class_exists( 'TestJob' ) ) { + class TestJob extends Job { + public function handle() {} + } +} \ No newline at end of file diff --git a/vendor/a5hleyrich/wp-queue/tests/TestQueueManager.php b/vendor/a5hleyrich/wp-queue/tests/TestQueueManager.php new file mode 100644 index 000000000..b5616861b --- /dev/null +++ b/vendor/a5hleyrich/wp-queue/tests/TestQueueManager.php @@ -0,0 +1,33 @@ +prefix = 'wp_'; + } + + public function tearDown() { + WP_Mock::tearDown(); + } + + public function test_resolve() { + $queue = QueueManager::resolve( 'database' ); + $this->assertInstanceOf( Queue::class, $queue ); + $queue = QueueManager::resolve( 'database' ); + $this->assertInstanceOf( Queue::class, $queue ); + } + + public function test_resolve_exception() { + $this->expectException( ConnectionNotFoundException::class ); + QueueManager::resolve( 'wibble' ); + } +} \ No newline at end of file diff --git a/vendor/a5hleyrich/wp-queue/tests/TestWorker.php b/vendor/a5hleyrich/wp-queue/tests/TestWorker.php new file mode 100644 index 000000000..d07a1baaa --- /dev/null +++ b/vendor/a5hleyrich/wp-queue/tests/TestWorker.php @@ -0,0 +1,35 @@ +shouldReceive( 'pop' )->once()->andReturn( $job ); + + $worker = new Worker( $connection ); + $this->assertTrue( $worker->process() ); + } + + public function test_process_fail() { + $connection = Mockery::spy( ConnectionInterface::class ); + $job = Mockery::spy( Job::class ); + $connection->shouldReceive( 'pop' )->once()->andReturn( false ); + + $worker = new Worker( $connection ); + $this->assertFalse( $worker->process() ); + } +} \ No newline at end of file diff --git a/vendor/composer/ClassLoader.php b/vendor/composer/ClassLoader.php index fce8549f0..dc02dfb11 100644 --- a/vendor/composer/ClassLoader.php +++ b/vendor/composer/ClassLoader.php @@ -279,7 +279,7 @@ public function isClassMapAuthoritative() */ public function setApcuPrefix($apcuPrefix) { - $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null; + $this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null; } /** @@ -377,7 +377,7 @@ private function findFileWithExtension($class, $ext) $subPath = $class; while (false !== $lastPos = strrpos($subPath, '\\')) { $subPath = substr($subPath, 0, $lastPos); - $search = $subPath . '\\'; + $search = $subPath.'\\'; if (isset($this->prefixDirsPsr4[$search])) { $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); foreach ($this->prefixDirsPsr4[$search] as $dir) { diff --git a/vendor/composer/autoload_files.php b/vendor/composer/autoload_files.php index bf19cde4b..7787bad78 100644 --- a/vendor/composer/autoload_files.php +++ b/vendor/composer/autoload_files.php @@ -7,9 +7,12 @@ return array( '320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php', + '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php', '051bafe20e2674435a162870efa2d2a7' => $vendorDir . '/brain/monkey/inc/api.php', '6124b4c8570aa390c21fafd04a26c69f' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php', + 'edf8ef411b308ea9e315d190a754d91b' => $vendorDir . '/a5hleyrich/wp-queue/src/functions.php', '653f15cae3415bbad33eff25628b45a8' => $vendorDir . '/calderawp/caldera-forms-query/src/CalderaFormsQueries.php', '5e73ffc188f5a63fbd263c4490731358' => $vendorDir . '/inpsyde/wonolog/inc/bootstrap.php', + '1788f000e5723cf54aa875ee1eec6c27' => $baseDir . '/cf2/functions.php', 'e3b369f785b64e46a24dc3ca6f0257a1' => $baseDir . '/tests/testing-cli.php', ); diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php index 96b659f21..d0ca39b34 100644 --- a/vendor/composer/autoload_psr4.php +++ b/vendor/composer/autoload_psr4.php @@ -16,8 +16,11 @@ 'calderawp\\CalderaFormsQuery\\' => array($vendorDir . '/calderawp/caldera-forms-query/src'), 'calderawp\\CalderaContainers\\' => array($vendorDir . '/calderawp/caldera-containers/src'), 'Webmozart\\Assert\\' => array($vendorDir . '/webmozart/assert/src'), + 'WP_Queue\\' => array($vendorDir . '/a5hleyrich/wp-queue/src/WP_Queue'), + 'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'), 'Symfony\\Polyfill\\Ctype\\' => array($vendorDir . '/symfony/polyfill-ctype'), 'Symfony\\Component\\Yaml\\' => array($vendorDir . '/symfony/yaml'), + 'Symfony\\Component\\Translation\\' => array($vendorDir . '/symfony/translation'), 'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'), 'Psr\\Container\\' => array($vendorDir . '/psr/container/src'), 'NilPortugues\\Sql\\QueryFormatter\\' => array($vendorDir . '/nilportugues/sql-query-formatter/src'), @@ -28,4 +31,5 @@ 'DeepCopy\\' => array($vendorDir . '/myclabs/deep-copy/src/DeepCopy'), 'Composer\\Installers\\' => array($vendorDir . '/composer/installers/src/Composer/Installers'), 'Brain\\Monkey\\' => array($vendorDir . '/brain/monkey/src'), + '' => array($vendorDir . '/nesbot/carbon/src'), ); diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index 316be3ec7..f33c67f07 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -8,10 +8,13 @@ class ComposerStaticInitb2d56b84f94cc35345b1871b7d51f461 { public static $files = array ( '320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php', + '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php', '051bafe20e2674435a162870efa2d2a7' => __DIR__ . '/..' . '/brain/monkey/inc/api.php', '6124b4c8570aa390c21fafd04a26c69f' => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php', + 'edf8ef411b308ea9e315d190a754d91b' => __DIR__ . '/..' . '/a5hleyrich/wp-queue/src/functions.php', '653f15cae3415bbad33eff25628b45a8' => __DIR__ . '/..' . '/calderawp/caldera-forms-query/src/CalderaFormsQueries.php', '5e73ffc188f5a63fbd263c4490731358' => __DIR__ . '/..' . '/inpsyde/wonolog/inc/bootstrap.php', + '1788f000e5723cf54aa875ee1eec6c27' => __DIR__ . '/../..' . '/cf2/functions.php', 'e3b369f785b64e46a24dc3ca6f0257a1' => __DIR__ . '/../..' . '/tests/testing-cli.php', ); @@ -34,11 +37,14 @@ class ComposerStaticInitb2d56b84f94cc35345b1871b7d51f461 'W' => array ( 'Webmozart\\Assert\\' => 17, + 'WP_Queue\\' => 9, ), 'S' => array ( + 'Symfony\\Polyfill\\Mbstring\\' => 26, 'Symfony\\Polyfill\\Ctype\\' => 23, 'Symfony\\Component\\Yaml\\' => 23, + 'Symfony\\Component\\Translation\\' => 30, ), 'P' => array ( @@ -116,6 +122,14 @@ class ComposerStaticInitb2d56b84f94cc35345b1871b7d51f461 array ( 0 => __DIR__ . '/..' . '/webmozart/assert/src', ), + 'WP_Queue\\' => + array ( + 0 => __DIR__ . '/..' . '/a5hleyrich/wp-queue/src/WP_Queue', + ), + 'Symfony\\Polyfill\\Mbstring\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring', + ), 'Symfony\\Polyfill\\Ctype\\' => array ( 0 => __DIR__ . '/..' . '/symfony/polyfill-ctype', @@ -124,6 +138,10 @@ class ComposerStaticInitb2d56b84f94cc35345b1871b7d51f461 array ( 0 => __DIR__ . '/..' . '/symfony/yaml', ), + 'Symfony\\Component\\Translation\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/translation', + ), 'Psr\\Log\\' => array ( 0 => __DIR__ . '/..' . '/psr/log/Psr/Log', @@ -166,6 +184,10 @@ class ComposerStaticInitb2d56b84f94cc35345b1871b7d51f461 ), ); + public static $fallbackDirsPsr4 = array ( + 0 => __DIR__ . '/..' . '/nesbot/carbon/src', + ); + public static $prefixesPsr0 = array ( 'j' => array ( @@ -726,6 +748,7 @@ public static function getInitializer(ClassLoader $loader) return \Closure::bind(function () use ($loader) { $loader->prefixLengthsPsr4 = ComposerStaticInitb2d56b84f94cc35345b1871b7d51f461::$prefixLengthsPsr4; $loader->prefixDirsPsr4 = ComposerStaticInitb2d56b84f94cc35345b1871b7d51f461::$prefixDirsPsr4; + $loader->fallbackDirsPsr4 = ComposerStaticInitb2d56b84f94cc35345b1871b7d51f461::$fallbackDirsPsr4; $loader->prefixesPsr0 = ComposerStaticInitb2d56b84f94cc35345b1871b7d51f461::$prefixesPsr0; $loader->classMap = ComposerStaticInitb2d56b84f94cc35345b1871b7d51f461::$classMap; diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index 6d47f9b9f..625dc769d 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -1,4 +1,55 @@ [ + { + "name": "a5hleyrich/wp-queue", + "version": "1.3.0", + "version_normalized": "1.3.0.0", + "source": { + "type": "git", + "url": "https://github.com/A5hleyRich/wp-queue.git", + "reference": "4cc44fa2ae1e493113e1f71e7e4efeecefb63f50" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/A5hleyRich/wp-queue/zipball/4cc44fa2ae1e493113e1f71e7e4efeecefb63f50", + "reference": "4cc44fa2ae1e493113e1f71e7e4efeecefb63f50", + "shasum": "" + }, + "require": { + "nesbot/carbon": "^1.22", + "php": ">=5.3.0" + }, + "require-dev": { + "10up/wp_mock": "0.2.0", + "phpunit/phpunit": "~5.7.0" + }, + "time": "2018-04-20T10:24:04+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "WP_Queue\\": "src\\WP_Queue" + }, + "files": [ + "src/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ashley Rich", + "email": "hello@ashleyrich.com" + } + ], + "description": "WordPress job queues", + "keywords": [ + "job", + "queue", + "wordpress" + ] + }, { "name": "antecedent/patchwork", "version": "2.1.8", @@ -710,17 +761,17 @@ }, { "name": "monolog/monolog", - "version": "1.23.0", - "version_normalized": "1.23.0.0", + "version": "1.24.0", + "version_normalized": "1.24.0.0", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "fd8c787753b3a2ad11bc60c063cff1358a32a3b4" + "reference": "bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/fd8c787753b3a2ad11bc60c063cff1358a32a3b4", - "reference": "fd8c787753b3a2ad11bc60c063cff1358a32a3b4", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266", + "reference": "bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266", "shasum": "" }, "require": { @@ -756,7 +807,7 @@ "ruflin/elastica": "Allow sending log messages to an Elastic Search server", "sentry/sentry": "Allow sending log messages to a Sentry server" }, - "time": "2017-06-19T01:22:40+00:00", + "time": "2018-11-05T09:00:11+00:00", "type": "library", "extra": { "branch-alias": { @@ -838,6 +889,63 @@ "object graph" ] }, + { + "name": "nesbot/carbon", + "version": "1.34.0", + "version_normalized": "1.34.0.0", + "source": { + "type": "git", + "url": "https://github.com/briannesbitt/Carbon.git", + "reference": "1dbd3cb01c5645f3e7deda7aa46ef780d95fcc33" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/1dbd3cb01c5645f3e7deda7aa46ef780d95fcc33", + "reference": "1dbd3cb01c5645f3e7deda7aa46ef780d95fcc33", + "shasum": "" + }, + "require": { + "php": ">=5.3.9", + "symfony/translation": "~2.6 || ~3.0 || ~4.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "~2", + "phpunit/phpunit": "^4.8.35 || ^5.7" + }, + "time": "2018-09-20T19:36:25+00:00", + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Carbon\\Laravel\\ServiceProvider" + ] + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brian Nesbitt", + "email": "brian@nesbot.com", + "homepage": "http://nesbot.com" + } + ], + "description": "A simple API extension for DateTime.", + "homepage": "http://carbon.nesbot.com", + "keywords": [ + "date", + "datetime", + "time" + ] + }, { "name": "nilportugues/sql-query-builder", "version": "1.6.0", @@ -2276,8 +2384,8 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.9.0", - "version_normalized": "1.9.0.0", + "version": "v1.10.0", + "version_normalized": "1.10.0.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", @@ -2334,10 +2442,142 @@ "portable" ] }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.10.0", + "version_normalized": "1.10.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "c79c051f5b3a46be09205c73b80b346e4153e494" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/c79c051f5b3a46be09205c73b80b346e4153e494", + "reference": "c79c051f5b3a46be09205c73b80b346e4153e494", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "time": "2018-09-21T13:07:52+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.9-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.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": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ] + }, + { + "name": "symfony/translation", + "version": "v4.1.7", + "version_normalized": "4.1.7.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation.git", + "reference": "aa04dc1c75b7d3da7bd7003104cd0cfc5dff635c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation/zipball/aa04dc1c75b7d3da7bd7003104cd0cfc5dff635c", + "reference": "aa04dc1c75b7d3da7bd7003104cd0cfc5dff635c", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/config": "<3.4", + "symfony/dependency-injection": "<3.4", + "symfony/yaml": "<3.4" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "~3.4|~4.0", + "symfony/console": "~3.4|~4.0", + "symfony/dependency-injection": "~3.4|~4.0", + "symfony/finder": "~2.8|~3.0|~4.0", + "symfony/intl": "~3.4|~4.0", + "symfony/yaml": "~3.4|~4.0" + }, + "suggest": { + "psr/log-implementation": "To use logging capability in translator", + "symfony/config": "", + "symfony/yaml": "" + }, + "time": "2018-10-28T18:38:52+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Translation Component", + "homepage": "https://symfony.com" + }, { "name": "symfony/yaml", - "version": "v3.4.17", - "version_normalized": "3.4.17.0", + "version": "v3.4.18", + "version_normalized": "3.4.18.0", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", @@ -2449,16 +2689,16 @@ }, { "name": "wpackagist-plugin/gutenberg", - "version": "4.0.0", - "version_normalized": "4.0.0.0", + "version": "4.1.1", + "version_normalized": "4.1.1.0", "source": { "type": "svn", "url": "https://plugins.svn.wordpress.org/gutenberg/", - "reference": "tags/4.0.0" + "reference": "tags/4.1.1" }, "dist": { "type": "zip", - "url": "https://downloads.wordpress.org/plugin/gutenberg.4.0.0.zip", + "url": "https://downloads.wordpress.org/plugin/gutenberg.4.1.1.zip", "reference": null, "shasum": null }, diff --git a/vendor/monolog/monolog/CHANGELOG.md b/vendor/monolog/monolog/CHANGELOG.md index cd1142d15..bcf679c4a 100644 --- a/vendor/monolog/monolog/CHANGELOG.md +++ b/vendor/monolog/monolog/CHANGELOG.md @@ -1,3 +1,31 @@ +### 1.24.0 (2018-11-05) + + * Added a `ResettableInterface` in order to reset/reset/clear/flush handlers and processors + * Added a `ProcessorInterface` as an optional way to label a class as being a processor (mostly useful for autowiring dependency containers) + * Added a way to log signals being received using Monolog\SignalHandler + * Added ability to customize error handling at the Logger level using Logger::setExceptionHandler + * Added InsightOpsHandler to migrate users of the LogEntriesHandler + * Added protection to NormalizerHandler against circular and very deep structures, it now stops normalizing at a depth of 9 + * Added capture of stack traces to ErrorHandler when logging PHP errors + * Added RavenHandler support for a `contexts` context or extra key to forward that to Sentry's contexts + * Added forwarding of context info to FluentdFormatter + * Added SocketHandler::setChunkSize to override the default chunk size in case you must send large log lines to rsyslog for example + * Added ability to extend/override BrowserConsoleHandler + * Added SlackWebhookHandler::getWebhookUrl and SlackHandler::getToken to enable class extensibility + * Added SwiftMailerHandler::getSubjectFormatter to enable class extensibility + * Dropped official support for HHVM in test builds + * Fixed normalization of exception traces when call_user_func is used to avoid serializing objects and the data they contain + * Fixed naming of fields in Slack handler, all field names are now capitalized in all cases + * Fixed HipChatHandler bug where slack dropped messages randomly + * Fixed normalization of objects in Slack handlers + * Fixed support for PHP7's Throwable in NewRelicHandler + * Fixed race bug when StreamHandler sometimes incorrectly reported it failed to create a directory + * Fixed table row styling issues in HtmlFormatter + * Fixed RavenHandler dropping the message when logging exception + * Fixed WhatFailureGroupHandler skipping processors when using handleBatch + and implement it where possible + * Fixed display of anonymous class names + ### 1.23.0 (2017-06-19) * Improved SyslogUdpHandler's support for RFC5424 and added optional `$ident` argument diff --git a/vendor/monolog/monolog/README.md b/vendor/monolog/monolog/README.md index 7d8ade526..d75694465 100644 --- a/vendor/monolog/monolog/README.md +++ b/vendor/monolog/monolog/README.md @@ -2,7 +2,6 @@ [![Total Downloads](https://img.shields.io/packagist/dt/monolog/monolog.svg)](https://packagist.org/packages/monolog/monolog) [![Latest Stable Version](https://img.shields.io/packagist/v/monolog/monolog.svg)](https://packagist.org/packages/monolog/monolog) -[![Reference Status](https://www.versioneye.com/php/monolog:monolog/reference_badge.svg)](https://www.versioneye.com/php/monolog:monolog/references) Monolog sends your logs to files, sockets, inboxes, databases and various diff --git a/vendor/monolog/monolog/doc/02-handlers-formatters-processors.md b/vendor/monolog/monolog/doc/02-handlers-formatters-processors.md index bea968ace..af45913af 100644 --- a/vendor/monolog/monolog/doc/02-handlers-formatters-processors.md +++ b/vendor/monolog/monolog/doc/02-handlers-formatters-processors.md @@ -55,6 +55,7 @@ - _RollbarHandler_: Logs records to a [Rollbar](https://rollbar.com/) account. - _SyslogUdpHandler_: Logs records to a remote [Syslogd](http://www.rsyslog.com/) server. - _LogEntriesHandler_: Logs records to a [LogEntries](http://logentries.com/) account. +- _InsightOpsHandler_: Logs records to a [InsightOps](https://www.rapid7.com/products/insightops/) account. ### Logging in development diff --git a/vendor/monolog/monolog/doc/03-utilities.md b/vendor/monolog/monolog/doc/03-utilities.md index c62aa4161..fd3fd0e7d 100644 --- a/vendor/monolog/monolog/doc/03-utilities.md +++ b/vendor/monolog/monolog/doc/03-utilities.md @@ -5,6 +5,8 @@ help in some older codebases or for ease of use. - _ErrorHandler_: The `Monolog\ErrorHandler` class allows you to easily register a Logger instance as an exception handler, error handler or fatal error handler. +- _SignalHandler_: The `Monolog\SignalHandler` class allows you to easily register + a Logger instance as a POSIX signal handler. - _ErrorLevelActivationStrategy_: Activates a FingersCrossedHandler when a certain log level is reached. - _ChannelLevelActivationStrategy_: Activates a FingersCrossedHandler when a certain diff --git a/vendor/monolog/monolog/src/Monolog/ErrorHandler.php b/vendor/monolog/monolog/src/Monolog/ErrorHandler.php index 7bfcd833a..adc55bdf6 100644 --- a/vendor/monolog/monolog/src/Monolog/ErrorHandler.php +++ b/vendor/monolog/monolog/src/Monolog/ErrorHandler.php @@ -14,6 +14,7 @@ use Psr\Log\LoggerInterface; use Psr\Log\LogLevel; use Monolog\Handler\AbstractHandler; +use Monolog\Registry; /** * Monolog error handler @@ -38,6 +39,7 @@ class ErrorHandler private $hasFatalErrorHandler; private $fatalLevel; private $reservedMemory; + private $lastFatalTrace; private static $fatalErrors = array(E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR); public function __construct(LoggerInterface $logger) @@ -132,7 +134,7 @@ public function handleException($e) { $this->logger->log( $this->uncaughtExceptionLevel === null ? LogLevel::ERROR : $this->uncaughtExceptionLevel, - sprintf('Uncaught Exception %s: "%s" at %s line %s', get_class($e), $e->getMessage(), $e->getFile(), $e->getLine()), + sprintf('Uncaught Exception %s: "%s" at %s line %s', Utils::getClass($e), $e->getMessage(), $e->getFile(), $e->getLine()), array('exception' => $e) ); @@ -156,6 +158,13 @@ public function handleError($code, $message, $file = '', $line = 0, $context = a if (!$this->hasFatalErrorHandler || !in_array($code, self::$fatalErrors, true)) { $level = isset($this->errorLevelMap[$code]) ? $this->errorLevelMap[$code] : LogLevel::CRITICAL; $this->logger->log($level, self::codeToString($code).': '.$message, array('code' => $code, 'message' => $message, 'file' => $file, 'line' => $line)); + } else { + // http://php.net/manual/en/function.debug-backtrace.php + // As of 5.3.6, DEBUG_BACKTRACE_IGNORE_ARGS option was added. + // Any version less than 5.3.6 must use the DEBUG_BACKTRACE_IGNORE_ARGS constant value '2'. + $trace = debug_backtrace((PHP_VERSION_ID < 50306) ? 2 : DEBUG_BACKTRACE_IGNORE_ARGS); + array_shift($trace); // Exclude handleError from trace + $this->lastFatalTrace = $trace; } if ($this->previousErrorHandler === true) { @@ -177,7 +186,7 @@ public function handleFatalError() $this->logger->log( $this->fatalLevel === null ? LogLevel::ALERT : $this->fatalLevel, 'Fatal Error ('.self::codeToString($lastError['type']).'): '.$lastError['message'], - array('code' => $lastError['type'], 'message' => $lastError['message'], 'file' => $lastError['file'], 'line' => $lastError['line']) + array('code' => $lastError['type'], 'message' => $lastError['message'], 'file' => $lastError['file'], 'line' => $lastError['line'], 'trace' => $this->lastFatalTrace) ); if ($this->logger instanceof Logger) { diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/FluentdFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/FluentdFormatter.php index 02632bb56..46a91ffe8 100644 --- a/vendor/monolog/monolog/src/Monolog/Formatter/FluentdFormatter.php +++ b/vendor/monolog/monolog/src/Monolog/Formatter/FluentdFormatter.php @@ -62,6 +62,7 @@ public function format(array $record) $message = array( 'message' => $record['message'], + 'context' => $record['context'], 'extra' => $record['extra'], ); diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/HtmlFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/HtmlFormatter.php index 3eec95f6c..dfc0b4a3e 100644 --- a/vendor/monolog/monolog/src/Monolog/Formatter/HtmlFormatter.php +++ b/vendor/monolog/monolog/src/Monolog/Formatter/HtmlFormatter.php @@ -58,7 +58,7 @@ protected function addRow($th, $td = ' ', $escapeTd = true) $td = '
'.htmlspecialchars($td, ENT_NOQUOTES, 'UTF-8').'
'; } - return "\n$th:\n".$td."\n"; + return "\n$th:\n".$td."\n"; } /** diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/JsonFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/JsonFormatter.php index 0782f1499..9bd305f23 100644 --- a/vendor/monolog/monolog/src/Monolog/Formatter/JsonFormatter.php +++ b/vendor/monolog/monolog/src/Monolog/Formatter/JsonFormatter.php @@ -12,6 +12,7 @@ namespace Monolog\Formatter; use Exception; +use Monolog\Utils; use Throwable; /** @@ -138,18 +139,23 @@ protected function formatBatchNewlines(array $records) * * @return mixed */ - protected function normalize($data) + protected function normalize($data, $depth = 0) { + if ($depth > 9) { + return 'Over 9 levels deep, aborting normalization'; + } + if (is_array($data) || $data instanceof \Traversable) { $normalized = array(); $count = 1; foreach ($data as $key => $value) { - if ($count++ >= 1000) { - $normalized['...'] = 'Over 1000 items, aborting normalization'; + if ($count++ > 1000) { + $normalized['...'] = 'Over 1000 items ('.count($data).' total), aborting normalization'; break; } - $normalized[$key] = $this->normalize($value); + + $normalized[$key] = $this->normalize($value, $depth+1); } return $normalized; @@ -174,11 +180,11 @@ protected function normalizeException($e) { // TODO 2.0 only check for Throwable if (!$e instanceof Exception && !$e instanceof Throwable) { - throw new \InvalidArgumentException('Exception/Throwable expected, got '.gettype($e).' / '.get_class($e)); + throw new \InvalidArgumentException('Exception/Throwable expected, got '.gettype($e).' / '.Utils::getClass($e)); } $data = array( - 'class' => get_class($e), + 'class' => Utils::getClass($e), 'message' => $e->getMessage(), 'code' => $e->getCode(), 'file' => $e->getFile().':'.$e->getLine(), diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/LineFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/LineFormatter.php index d3e209e6c..f98e1a6fc 100644 --- a/vendor/monolog/monolog/src/Monolog/Formatter/LineFormatter.php +++ b/vendor/monolog/monolog/src/Monolog/Formatter/LineFormatter.php @@ -11,6 +11,8 @@ namespace Monolog\Formatter; +use Monolog\Utils; + /** * Formats incoming records into a one-line string * @@ -129,17 +131,17 @@ protected function normalizeException($e) { // TODO 2.0 only check for Throwable if (!$e instanceof \Exception && !$e instanceof \Throwable) { - throw new \InvalidArgumentException('Exception/Throwable expected, got '.gettype($e).' / '.get_class($e)); + throw new \InvalidArgumentException('Exception/Throwable expected, got '.gettype($e).' / '.Utils::getClass($e)); } $previousText = ''; if ($previous = $e->getPrevious()) { do { - $previousText .= ', '.get_class($previous).'(code: '.$previous->getCode().'): '.$previous->getMessage().' at '.$previous->getFile().':'.$previous->getLine(); + $previousText .= ', '.Utils::getClass($previous).'(code: '.$previous->getCode().'): '.$previous->getMessage().' at '.$previous->getFile().':'.$previous->getLine(); } while ($previous = $previous->getPrevious()); } - $str = '[object] ('.get_class($e).'(code: '.$e->getCode().'): '.$e->getMessage().' at '.$e->getFile().':'.$e->getLine().$previousText.')'; + $str = '[object] ('.Utils::getClass($e).'(code: '.$e->getCode().'): '.$e->getMessage().' at '.$e->getFile().':'.$e->getLine().$previousText.')'; if ($this->includeStacktraces) { $str .= "\n[stacktrace]\n".$e->getTraceAsString()."\n"; } diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/MongoDBFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/MongoDBFormatter.php index eb067bb72..eb7be849f 100644 --- a/vendor/monolog/monolog/src/Monolog/Formatter/MongoDBFormatter.php +++ b/vendor/monolog/monolog/src/Monolog/Formatter/MongoDBFormatter.php @@ -11,6 +11,8 @@ namespace Monolog\Formatter; +use Monolog\Utils; + /** * Formats a record for use with the MongoDBHandler. * @@ -75,7 +77,7 @@ protected function formatArray(array $record, $nestingLevel = 0) protected function formatObject($value, $nestingLevel) { $objectVars = get_object_vars($value); - $objectVars['class'] = get_class($value); + $objectVars['class'] = Utils::getClass($value); return $this->formatArray($objectVars, $nestingLevel); } @@ -83,7 +85,7 @@ protected function formatObject($value, $nestingLevel) protected function formatException(\Exception $exception, $nestingLevel) { $formattedException = array( - 'class' => get_class($exception), + 'class' => Utils::getClass($exception), 'message' => $exception->getMessage(), 'code' => $exception->getCode(), 'file' => $exception->getFile() . ':' . $exception->getLine(), diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/NormalizerFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/NormalizerFormatter.php index d44148823..668665789 100644 --- a/vendor/monolog/monolog/src/Monolog/Formatter/NormalizerFormatter.php +++ b/vendor/monolog/monolog/src/Monolog/Formatter/NormalizerFormatter.php @@ -12,6 +12,7 @@ namespace Monolog\Formatter; use Exception; +use Monolog\Utils; /** * Normalizes incoming records to remove objects/resources so it's easier to dump to various targets @@ -55,8 +56,12 @@ public function formatBatch(array $records) return $records; } - protected function normalize($data) + protected function normalize($data, $depth = 0) { + if ($depth > 9) { + return 'Over 9 levels deep, aborting normalization'; + } + if (null === $data || is_scalar($data)) { if (is_float($data)) { if (is_infinite($data)) { @@ -75,11 +80,12 @@ protected function normalize($data) $count = 1; foreach ($data as $key => $value) { - if ($count++ >= 1000) { + if ($count++ > 1000) { $normalized['...'] = 'Over 1000 items ('.count($data).' total), aborting normalization'; break; } - $normalized[$key] = $this->normalize($value); + + $normalized[$key] = $this->normalize($value, $depth+1); } return $normalized; @@ -103,7 +109,7 @@ protected function normalize($data) $value = $this->toJson($data, true); } - return sprintf("[object] (%s: %s)", get_class($data), $value); + return sprintf("[object] (%s: %s)", Utils::getClass($data), $value); } if (is_resource($data)) { @@ -117,11 +123,11 @@ protected function normalizeException($e) { // TODO 2.0 only check for Throwable if (!$e instanceof Exception && !$e instanceof \Throwable) { - throw new \InvalidArgumentException('Exception/Throwable expected, got '.gettype($e).' / '.get_class($e)); + throw new \InvalidArgumentException('Exception/Throwable expected, got '.gettype($e).' / '.Utils::getClass($e)); } $data = array( - 'class' => get_class($e), + 'class' => Utils::getClass($e), 'message' => $e->getMessage(), 'code' => $e->getCode(), 'file' => $e->getFile().':'.$e->getLine(), @@ -146,9 +152,20 @@ protected function normalizeException($e) if (isset($frame['file'])) { $data['trace'][] = $frame['file'].':'.$frame['line']; } elseif (isset($frame['function']) && $frame['function'] === '{closure}') { - // We should again normalize the frames, because it might contain invalid items + // Simplify closures handling $data['trace'][] = $frame['function']; } else { + if (isset($frame['args'])) { + // Make sure that objects present as arguments are not serialized nicely but rather only + // as a class name to avoid any unexpected leak of sensitive information + $frame['args'] = array_map(function ($arg) { + if (is_object($arg) && !($arg instanceof \DateTime || $arg instanceof \DateTimeInterface)) { + return sprintf("[object] (%s)", Utils::getClass($arg)); + } + + return $arg; + }, $frame['args']); + } // We should again normalize the frames, because it might contain invalid items $data['trace'][] = $this->toJson($this->normalize($frame), true); } diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/WildfireFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/WildfireFormatter.php index 654710a8d..65dba99c9 100644 --- a/vendor/monolog/monolog/src/Monolog/Formatter/WildfireFormatter.php +++ b/vendor/monolog/monolog/src/Monolog/Formatter/WildfireFormatter.php @@ -102,12 +102,12 @@ public function formatBatch(array $records) throw new \BadMethodCallException('Batch formatting does not make sense for the WildfireFormatter'); } - protected function normalize($data) + protected function normalize($data, $depth = 0) { if (is_object($data) && !$data instanceof \DateTime) { return $data; } - return parent::normalize($data); + return parent::normalize($data, $depth); } } diff --git a/vendor/monolog/monolog/src/Monolog/Handler/AbstractHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/AbstractHandler.php index 758a425c3..92b9d4580 100644 --- a/vendor/monolog/monolog/src/Monolog/Handler/AbstractHandler.php +++ b/vendor/monolog/monolog/src/Monolog/Handler/AbstractHandler.php @@ -11,16 +11,17 @@ namespace Monolog\Handler; -use Monolog\Logger; use Monolog\Formatter\FormatterInterface; use Monolog\Formatter\LineFormatter; +use Monolog\Logger; +use Monolog\ResettableInterface; /** * Base Handler class providing the Handler structure * * @author Jordi Boggiano */ -abstract class AbstractHandler implements HandlerInterface +abstract class AbstractHandler implements HandlerInterface, ResettableInterface { protected $level = Logger::DEBUG; protected $bubble = true; @@ -32,8 +33,8 @@ abstract class AbstractHandler implements HandlerInterface protected $processors = array(); /** - * @param int $level The minimum logging level at which this handler will be triggered - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + * @param int $level The minimum logging level at which this handler will be triggered + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not */ public function __construct($level = Logger::DEBUG, $bubble = true) { @@ -141,8 +142,8 @@ public function getLevel() /** * Sets the bubbling behavior. * - * @param Boolean $bubble true means that this handler allows bubbling. - * false means that bubbling is not permitted. + * @param bool $bubble true means that this handler allows bubbling. + * false means that bubbling is not permitted. * @return self */ public function setBubble($bubble) @@ -155,8 +156,8 @@ public function setBubble($bubble) /** * Gets the bubbling behavior. * - * @return Boolean true means that this handler allows bubbling. - * false means that bubbling is not permitted. + * @return bool true means that this handler allows bubbling. + * false means that bubbling is not permitted. */ public function getBubble() { @@ -174,6 +175,15 @@ public function __destruct() } } + public function reset() + { + foreach ($this->processors as $processor) { + if ($processor instanceof ResettableInterface) { + $processor->reset(); + } + } + } + /** * Gets the default formatter. * diff --git a/vendor/monolog/monolog/src/Monolog/Handler/AbstractProcessingHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/AbstractProcessingHandler.php index 6f18f72e1..e1e89530a 100644 --- a/vendor/monolog/monolog/src/Monolog/Handler/AbstractProcessingHandler.php +++ b/vendor/monolog/monolog/src/Monolog/Handler/AbstractProcessingHandler.php @@ -11,6 +11,8 @@ namespace Monolog\Handler; +use Monolog\ResettableInterface; + /** * Base Handler class providing the Handler structure * diff --git a/vendor/monolog/monolog/src/Monolog/Handler/AbstractSyslogHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/AbstractSyslogHandler.php index e2b2832d0..8c76aca0b 100644 --- a/vendor/monolog/monolog/src/Monolog/Handler/AbstractSyslogHandler.php +++ b/vendor/monolog/monolog/src/Monolog/Handler/AbstractSyslogHandler.php @@ -53,9 +53,9 @@ abstract class AbstractSyslogHandler extends AbstractProcessingHandler ); /** - * @param mixed $facility - * @param int $level The minimum logging level at which this handler will be triggered - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + * @param mixed $facility + * @param int $level The minimum logging level at which this handler will be triggered + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not */ public function __construct($facility = LOG_USER, $level = Logger::DEBUG, $bubble = true) { diff --git a/vendor/monolog/monolog/src/Monolog/Handler/BrowserConsoleHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/BrowserConsoleHandler.php index b3a21bd40..23cf23ba1 100644 --- a/vendor/monolog/monolog/src/Monolog/Handler/BrowserConsoleHandler.php +++ b/vendor/monolog/monolog/src/Monolog/Handler/BrowserConsoleHandler.php @@ -43,11 +43,11 @@ protected function getDefaultFormatter() protected function write(array $record) { // Accumulate records - self::$records[] = $record; + static::$records[] = $record; // Register shutdown handler if not already done - if (!self::$initialized) { - self::$initialized = true; + if (!static::$initialized) { + static::$initialized = true; $this->registerShutdownFunction(); } } @@ -58,27 +58,37 @@ protected function write(array $record) */ public static function send() { - $format = self::getResponseFormat(); + $format = static::getResponseFormat(); if ($format === 'unknown') { return; } - if (count(self::$records)) { + if (count(static::$records)) { if ($format === 'html') { - self::writeOutput(''); + static::writeOutput(''); } elseif ($format === 'js') { - self::writeOutput(self::generateScript()); + static::writeOutput(static::generateScript()); } - self::reset(); + static::resetStatic(); } } + public function close() + { + self::resetStatic(); + } + + public function reset() + { + self::resetStatic(); + } + /** * Forget all logged records */ - public static function reset() + public static function resetStatic() { - self::$records = array(); + static::$records = array(); } /** @@ -133,18 +143,18 @@ protected static function getResponseFormat() private static function generateScript() { $script = array(); - foreach (self::$records as $record) { - $context = self::dump('Context', $record['context']); - $extra = self::dump('Extra', $record['extra']); + foreach (static::$records as $record) { + $context = static::dump('Context', $record['context']); + $extra = static::dump('Extra', $record['extra']); if (empty($context) && empty($extra)) { - $script[] = self::call_array('log', self::handleStyles($record['formatted'])); + $script[] = static::call_array('log', static::handleStyles($record['formatted'])); } else { $script = array_merge($script, - array(self::call_array('groupCollapsed', self::handleStyles($record['formatted']))), + array(static::call_array('groupCollapsed', static::handleStyles($record['formatted']))), $context, $extra, - array(self::call('groupEnd')) + array(static::call('groupEnd')) ); } } @@ -154,19 +164,19 @@ private static function generateScript() private static function handleStyles($formatted) { - $args = array(self::quote('font-weight: normal')); + $args = array(static::quote('font-weight: normal')); $format = '%c' . $formatted; preg_match_all('/\[\[(.*?)\]\]\{([^}]*)\}/s', $format, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER); foreach (array_reverse($matches) as $match) { - $args[] = self::quote(self::handleCustomStyles($match[2][0], $match[1][0])); + $args[] = static::quote(static::handleCustomStyles($match[2][0], $match[1][0])); $args[] = '"font-weight: normal"'; $pos = $match[0][1]; $format = substr($format, 0, $pos) . '%c' . $match[1][0] . '%c' . substr($format, $pos + strlen($match[0][0])); } - array_unshift($args, self::quote($format)); + array_unshift($args, static::quote($format)); return $args; } @@ -198,13 +208,13 @@ private static function dump($title, array $dict) if (empty($dict)) { return $script; } - $script[] = self::call('log', self::quote('%c%s'), self::quote('font-weight: bold'), self::quote($title)); + $script[] = static::call('log', static::quote('%c%s'), static::quote('font-weight: bold'), static::quote($title)); foreach ($dict as $key => $value) { $value = json_encode($value); if (empty($value)) { - $value = self::quote(''); + $value = static::quote(''); } - $script[] = self::call('log', self::quote('%s: %o'), self::quote($key), $value); + $script[] = static::call('log', static::quote('%s: %o'), static::quote($key), $value); } return $script; @@ -220,7 +230,7 @@ private static function call() $args = func_get_args(); $method = array_shift($args); - return self::call_array($method, $args); + return static::call_array($method, $args); } private static function call_array($method, array $args) diff --git a/vendor/monolog/monolog/src/Monolog/Handler/BufferHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/BufferHandler.php index 72f895357..61d1b50c1 100644 --- a/vendor/monolog/monolog/src/Monolog/Handler/BufferHandler.php +++ b/vendor/monolog/monolog/src/Monolog/Handler/BufferHandler.php @@ -12,6 +12,7 @@ namespace Monolog\Handler; use Monolog\Logger; +use Monolog\ResettableInterface; /** * Buffers all records until closing the handler and then pass them as batch. @@ -34,8 +35,8 @@ class BufferHandler extends AbstractHandler * @param HandlerInterface $handler Handler. * @param int $bufferLimit How many entries should be buffered at most, beyond that the oldest items are removed from the buffer. * @param int $level The minimum logging level at which this handler will be triggered - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not - * @param Boolean $flushOnOverflow If true, the buffer is flushed when the max size has been reached, by default oldest entries are discarded + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + * @param bool $flushOnOverflow If true, the buffer is flushed when the max size has been reached, by default oldest entries are discarded */ public function __construct(HandlerInterface $handler, $bufferLimit = 0, $level = Logger::DEBUG, $bubble = true, $flushOnOverflow = false) { @@ -114,4 +115,15 @@ public function clear() $this->bufferSize = 0; $this->buffer = array(); } + + public function reset() + { + $this->flush(); + + parent::reset(); + + if ($this->handler instanceof ResettableInterface) { + $this->handler->reset(); + } + } } diff --git a/vendor/monolog/monolog/src/Monolog/Handler/ChromePHPHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/ChromePHPHandler.php index 785cb0c96..37419a061 100644 --- a/vendor/monolog/monolog/src/Monolog/Handler/ChromePHPHandler.php +++ b/vendor/monolog/monolog/src/Monolog/Handler/ChromePHPHandler.php @@ -32,7 +32,7 @@ class ChromePHPHandler extends AbstractProcessingHandler * Header name */ const HEADER_NAME = 'X-ChromeLogger-Data'; - + /** * Regular expression to detect supported browsers (matches any Chrome, or Firefox 43+) */ @@ -45,7 +45,7 @@ class ChromePHPHandler extends AbstractProcessingHandler * * Chrome limits the headers to 256KB, so when we sent 240KB we stop sending * - * @var Boolean + * @var bool */ protected static $overflowed = false; @@ -58,8 +58,8 @@ class ChromePHPHandler extends AbstractProcessingHandler protected static $sendHeaders = true; /** - * @param int $level The minimum logging level at which this handler will be triggered - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + * @param int $level The minimum logging level at which this handler will be triggered + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not */ public function __construct($level = Logger::DEBUG, $bubble = true) { @@ -174,7 +174,7 @@ protected function sendHeader($header, $content) /** * Verifies if the headers are accepted by the current user agent * - * @return Boolean + * @return bool */ protected function headersAccepted() { diff --git a/vendor/monolog/monolog/src/Monolog/Handler/DeduplicationHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/DeduplicationHandler.php index 7778c22a6..35b55cb4f 100644 --- a/vendor/monolog/monolog/src/Monolog/Handler/DeduplicationHandler.php +++ b/vendor/monolog/monolog/src/Monolog/Handler/DeduplicationHandler.php @@ -60,7 +60,7 @@ class DeduplicationHandler extends BufferHandler * @param string $deduplicationStore The file/path where the deduplication log should be kept * @param int $deduplicationLevel The minimum logging level for log records to be looked at for deduplication purposes * @param int $time The period (in seconds) during which duplicate entries should be suppressed after a given log is sent through - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not */ public function __construct(HandlerInterface $handler, $deduplicationStore = null, $deduplicationLevel = Logger::ERROR, $time = 60, $bubble = true) { diff --git a/vendor/monolog/monolog/src/Monolog/Handler/ElasticSearchHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/ElasticSearchHandler.php index 819674069..bb0f83ebc 100644 --- a/vendor/monolog/monolog/src/Monolog/Handler/ElasticSearchHandler.php +++ b/vendor/monolog/monolog/src/Monolog/Handler/ElasticSearchHandler.php @@ -46,10 +46,10 @@ class ElasticSearchHandler extends AbstractProcessingHandler protected $options = array(); /** - * @param Client $client Elastica Client object - * @param array $options Handler configuration - * @param int $level The minimum logging level at which this handler will be triggered - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + * @param Client $client Elastica Client object + * @param array $options Handler configuration + * @param int $level The minimum logging level at which this handler will be triggered + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not */ public function __construct(Client $client, array $options = array(), $level = Logger::DEBUG, $bubble = true) { diff --git a/vendor/monolog/monolog/src/Monolog/Handler/ErrorLogHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/ErrorLogHandler.php index 1447a584b..b2986b0fe 100644 --- a/vendor/monolog/monolog/src/Monolog/Handler/ErrorLogHandler.php +++ b/vendor/monolog/monolog/src/Monolog/Handler/ErrorLogHandler.php @@ -28,10 +28,10 @@ class ErrorLogHandler extends AbstractProcessingHandler protected $expandNewlines; /** - * @param int $messageType Says where the error should go. - * @param int $level The minimum logging level at which this handler will be triggered - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not - * @param Boolean $expandNewlines If set to true, newlines in the message will be expanded to be take multiple log entries + * @param int $messageType Says where the error should go. + * @param int $level The minimum logging level at which this handler will be triggered + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + * @param bool $expandNewlines If set to true, newlines in the message will be expanded to be take multiple log entries */ public function __construct($messageType = self::OPERATING_SYSTEM, $level = Logger::DEBUG, $bubble = true, $expandNewlines = false) { diff --git a/vendor/monolog/monolog/src/Monolog/Handler/FilterHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/FilterHandler.php index 2a0f7fd15..938c1a7e9 100644 --- a/vendor/monolog/monolog/src/Monolog/Handler/FilterHandler.php +++ b/vendor/monolog/monolog/src/Monolog/Handler/FilterHandler.php @@ -40,7 +40,7 @@ class FilterHandler extends AbstractHandler /** * Whether the messages that are handled can bubble up the stack or not * - * @var Boolean + * @var bool */ protected $bubble; @@ -48,7 +48,7 @@ class FilterHandler extends AbstractHandler * @param callable|HandlerInterface $handler Handler or factory callable($record, $this). * @param int|array $minLevelOrList A list of levels to accept or a minimum level if maxLevel is provided * @param int $maxLevel Maximum level to accept, only used if $minLevelOrList is not an array - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not */ public function __construct($handler, $minLevelOrList = Logger::DEBUG, $maxLevel = Logger::EMERGENCY, $bubble = true) { diff --git a/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ActivationStrategyInterface.php b/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ActivationStrategyInterface.php index c3e42efef..aaca12ccd 100644 --- a/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ActivationStrategyInterface.php +++ b/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ActivationStrategyInterface.php @@ -22,7 +22,7 @@ interface ActivationStrategyInterface * Returns whether the given record activates the handler. * * @param array $record - * @return Boolean + * @return bool */ public function isHandlerActivated(array $record); } diff --git a/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossedHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossedHandler.php index d1dcaacf0..275fd5136 100644 --- a/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossedHandler.php +++ b/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossedHandler.php @@ -14,6 +14,7 @@ use Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy; use Monolog\Handler\FingersCrossed\ActivationStrategyInterface; use Monolog\Logger; +use Monolog\ResettableInterface; /** * Buffers all records until a certain level is reached @@ -41,8 +42,8 @@ class FingersCrossedHandler extends AbstractHandler * @param callable|HandlerInterface $handler Handler or factory callable($record, $fingersCrossedHandler). * @param int|ActivationStrategyInterface $activationStrategy Strategy which determines when this handler takes action * @param int $bufferSize How many entries should be buffered at most, beyond that the oldest items are removed from the buffer. - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not - * @param Boolean $stopBuffering Whether the handler should stop buffering after being triggered (default true) + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + * @param bool $stopBuffering Whether the handler should stop buffering after being triggered (default true) * @param int $passthruLevel Minimum level to always flush to handler on close, even if strategy not triggered */ public function __construct($handler, $activationStrategy = null, $bufferSize = 0, $bubble = true, $stopBuffering = true, $passthruLevel = null) @@ -130,24 +131,18 @@ public function handle(array $record) */ public function close() { - if (null !== $this->passthruLevel) { - $level = $this->passthruLevel; - $this->buffer = array_filter($this->buffer, function ($record) use ($level) { - return $record['level'] >= $level; - }); - if (count($this->buffer) > 0) { - $this->handler->handleBatch($this->buffer); - $this->buffer = array(); - } - } + $this->flushBuffer(); } - /** - * Resets the state of the handler. Stops forwarding records to the wrapped handler. - */ public function reset() { - $this->buffering = true; + $this->flushBuffer(); + + parent::reset(); + + if ($this->handler instanceof ResettableInterface) { + $this->handler->reset(); + } } /** @@ -160,4 +155,23 @@ public function clear() $this->buffer = array(); $this->reset(); } + + /** + * Resets the state of the handler. Stops forwarding records to the wrapped handler. + */ + private function flushBuffer() + { + if (null !== $this->passthruLevel) { + $level = $this->passthruLevel; + $this->buffer = array_filter($this->buffer, function ($record) use ($level) { + return $record['level'] >= $level; + }); + if (count($this->buffer) > 0) { + $this->handler->handleBatch($this->buffer); + } + } + + $this->buffer = array(); + $this->buffering = true; + } } diff --git a/vendor/monolog/monolog/src/Monolog/Handler/FirePHPHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/FirePHPHandler.php index fee479508..c30b1843c 100644 --- a/vendor/monolog/monolog/src/Monolog/Handler/FirePHPHandler.php +++ b/vendor/monolog/monolog/src/Monolog/Handler/FirePHPHandler.php @@ -158,7 +158,7 @@ protected function write(array $record) /** * Verifies if the headers are accepted by the current user agent * - * @return Boolean + * @return bool */ protected function headersAccepted() { diff --git a/vendor/monolog/monolog/src/Monolog/Handler/GelfHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/GelfHandler.php index d3847d828..71e466934 100644 --- a/vendor/monolog/monolog/src/Monolog/Handler/GelfHandler.php +++ b/vendor/monolog/monolog/src/Monolog/Handler/GelfHandler.php @@ -47,14 +47,6 @@ public function __construct($publisher, $level = Logger::DEBUG, $bubble = true) $this->publisher = $publisher; } - /** - * {@inheritdoc} - */ - public function close() - { - $this->publisher = null; - } - /** * {@inheritdoc} */ diff --git a/vendor/monolog/monolog/src/Monolog/Handler/GroupHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/GroupHandler.php index 663f5a923..28e5c5648 100644 --- a/vendor/monolog/monolog/src/Monolog/Handler/GroupHandler.php +++ b/vendor/monolog/monolog/src/Monolog/Handler/GroupHandler.php @@ -12,6 +12,7 @@ namespace Monolog\Handler; use Monolog\Formatter\FormatterInterface; +use Monolog\ResettableInterface; /** * Forwards records to multiple handlers @@ -23,8 +24,8 @@ class GroupHandler extends AbstractHandler protected $handlers; /** - * @param array $handlers Array of Handlers. - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + * @param array $handlers Array of Handlers. + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not */ public function __construct(array $handlers, $bubble = true) { @@ -90,6 +91,17 @@ public function handleBatch(array $records) } } + public function reset() + { + parent::reset(); + + foreach ($this->handlers as $handler) { + if ($handler instanceof ResettableInterface) { + $handler->reset(); + } + } + } + /** * {@inheritdoc} */ diff --git a/vendor/monolog/monolog/src/Monolog/Handler/HandlerInterface.php b/vendor/monolog/monolog/src/Monolog/Handler/HandlerInterface.php index d920c4ba0..8d5a4a095 100644 --- a/vendor/monolog/monolog/src/Monolog/Handler/HandlerInterface.php +++ b/vendor/monolog/monolog/src/Monolog/Handler/HandlerInterface.php @@ -31,7 +31,7 @@ interface HandlerInterface * * @param array $record Partial log record containing only a level key * - * @return Boolean + * @return bool */ public function isHandling(array $record); @@ -46,7 +46,7 @@ public function isHandling(array $record); * calling further handlers in the stack with a given log record. * * @param array $record The record to handle - * @return Boolean true means that this handler handled the record, and that bubbling is not permitted. + * @return bool true means that this handler handled the record, and that bubbling is not permitted. * false means the record was either not processed or that this handler allows bubbling. */ public function handle(array $record); diff --git a/vendor/monolog/monolog/src/Monolog/Handler/HandlerWrapper.php b/vendor/monolog/monolog/src/Monolog/Handler/HandlerWrapper.php index e540d80f4..55e649868 100644 --- a/vendor/monolog/monolog/src/Monolog/Handler/HandlerWrapper.php +++ b/vendor/monolog/monolog/src/Monolog/Handler/HandlerWrapper.php @@ -11,6 +11,7 @@ namespace Monolog\Handler; +use Monolog\ResettableInterface; use Monolog\Formatter\FormatterInterface; /** @@ -30,7 +31,7 @@ * * @author Alexey Karapetov */ -class HandlerWrapper implements HandlerInterface +class HandlerWrapper implements HandlerInterface, ResettableInterface { /** * @var HandlerInterface @@ -105,4 +106,11 @@ public function getFormatter() { return $this->handler->getFormatter(); } + + public function reset() + { + if ($this->handler instanceof ResettableInterface) { + return $this->handler->reset(); + } + } } diff --git a/vendor/monolog/monolog/src/Monolog/Handler/HipChatHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/HipChatHandler.php index 73049f369..73233c95e 100644 --- a/vendor/monolog/monolog/src/Monolog/Handler/HipChatHandler.php +++ b/vendor/monolog/monolog/src/Monolog/Handler/HipChatHandler.php @@ -219,6 +219,21 @@ protected function getAlertColor($level) protected function write(array $record) { parent::write($record); + $this->finalizeWrite(); + } + + /** + * Finalizes the request by reading some bytes and then closing the socket + * + * If we do not read some but close the socket too early, hipchat sometimes + * drops the request entirely. + */ + protected function finalizeWrite() + { + $res = $this->getResource(); + if (is_resource($res)) { + @fread($res, 2048); + } $this->closeSocket(); } diff --git a/vendor/monolog/monolog/src/Monolog/Handler/IFTTTHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/IFTTTHandler.php index d60a3c825..7f2262208 100644 --- a/vendor/monolog/monolog/src/Monolog/Handler/IFTTTHandler.php +++ b/vendor/monolog/monolog/src/Monolog/Handler/IFTTTHandler.php @@ -30,10 +30,10 @@ class IFTTTHandler extends AbstractProcessingHandler private $secretKey; /** - * @param string $eventName The name of the IFTTT Maker event that should be triggered - * @param string $secretKey A valid IFTTT secret key - * @param int $level The minimum logging level at which this handler will be triggered - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + * @param string $eventName The name of the IFTTT Maker event that should be triggered + * @param string $secretKey A valid IFTTT secret key + * @param int $level The minimum logging level at which this handler will be triggered + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not */ public function __construct($eventName, $secretKey, $level = Logger::ERROR, $bubble = true) { diff --git a/vendor/monolog/monolog/src/Monolog/Handler/InsightOpsHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/InsightOpsHandler.php new file mode 100644 index 000000000..a12e3de56 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/InsightOpsHandler.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + + namespace Monolog\Handler; + + use Monolog\Logger; + +/** + * Inspired on LogEntriesHandler. + * + * @author Robert Kaufmann III + * @author Gabriel Machado + */ +class InsightOpsHandler extends SocketHandler +{ + /** + * @var string + */ + protected $logToken; + + /** + * @param string $token Log token supplied by InsightOps + * @param string $region Region where InsightOps account is hosted. Could be 'us' or 'eu'. + * @param bool $useSSL Whether or not SSL encryption should be used + * @param int $level The minimum logging level to trigger this handler + * @param bool $bubble Whether or not messages that are handled should bubble up the stack. + * + * @throws MissingExtensionException If SSL encryption is set to true and OpenSSL is missing + */ + public function __construct($token, $region = 'us', $useSSL = true, $level = Logger::DEBUG, $bubble = true) + { + if ($useSSL && !extension_loaded('openssl')) { + throw new MissingExtensionException('The OpenSSL PHP plugin is required to use SSL encrypted connection for LogEntriesHandler'); + } + + $endpoint = $useSSL + ? 'ssl://' . $region . '.data.logs.insight.rapid7.com:443' + : $region . '.data.logs.insight.rapid7.com:80'; + + parent::__construct($endpoint, $level, $bubble); + $this->logToken = $token; + } + + /** + * {@inheritdoc} + * + * @param array $record + * @return string + */ + protected function generateDataStream($record) + { + return $this->logToken . ' ' . $record['formatted']; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/LogEntriesHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/LogEntriesHandler.php index 494c605bc..ea89fb3ed 100644 --- a/vendor/monolog/monolog/src/Monolog/Handler/LogEntriesHandler.php +++ b/vendor/monolog/monolog/src/Monolog/Handler/LogEntriesHandler.php @@ -31,13 +31,13 @@ class LogEntriesHandler extends SocketHandler * * @throws MissingExtensionException If SSL encryption is set to true and OpenSSL is missing */ - public function __construct($token, $useSSL = true, $level = Logger::DEBUG, $bubble = true) + public function __construct($token, $useSSL = true, $level = Logger::DEBUG, $bubble = true, $host = 'data.logentries.com') { if ($useSSL && !extension_loaded('openssl')) { throw new MissingExtensionException('The OpenSSL PHP plugin is required to use SSL encrypted connection for LogEntriesHandler'); } - $endpoint = $useSSL ? 'ssl://data.logentries.com:443' : 'data.logentries.com:80'; + $endpoint = $useSSL ? 'ssl://' . $host . ':443' : $host . ':80'; parent::__construct($endpoint, $level, $bubble); $this->logToken = $token; } diff --git a/vendor/monolog/monolog/src/Monolog/Handler/MandrillHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/MandrillHandler.php index ab95924f9..3f0956a9c 100644 --- a/vendor/monolog/monolog/src/Monolog/Handler/MandrillHandler.php +++ b/vendor/monolog/monolog/src/Monolog/Handler/MandrillHandler.php @@ -27,7 +27,7 @@ class MandrillHandler extends MailHandler * @param string $apiKey A valid Mandrill API key * @param callable|\Swift_Message $message An example message for real messages, only the body will be replaced * @param int $level The minimum logging level at which this handler will be triggered - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not */ public function __construct($apiKey, $message, $level = Logger::ERROR, $bubble = true) { diff --git a/vendor/monolog/monolog/src/Monolog/Handler/NewRelicHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/NewRelicHandler.php index 6718e9e09..f911997ad 100644 --- a/vendor/monolog/monolog/src/Monolog/Handler/NewRelicHandler.php +++ b/vendor/monolog/monolog/src/Monolog/Handler/NewRelicHandler.php @@ -18,6 +18,8 @@ * Class to record a log on a NewRelic application. * Enabling New Relic High Security mode may prevent capture of useful information. * + * This handler requires a NormalizerFormatter to function and expects an array in $record['formatted'] + * * @see https://docs.newrelic.com/docs/agents/php-agent * @see https://docs.newrelic.com/docs/accounts-partnerships/accounts/security/high-security */ @@ -84,7 +86,7 @@ protected function write(array $record) unset($record['formatted']['context']['transaction_name']); } - if (isset($record['context']['exception']) && $record['context']['exception'] instanceof \Exception) { + if (isset($record['context']['exception']) && ($record['context']['exception'] instanceof \Exception || (PHP_VERSION_ID >= 70000 && $record['context']['exception'] instanceof \Throwable))) { newrelic_notice_error($record['message'], $record['context']['exception']); unset($record['formatted']['context']['exception']); } else { diff --git a/vendor/monolog/monolog/src/Monolog/Handler/PsrHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/PsrHandler.php index 1ae85845d..a99e6ab71 100644 --- a/vendor/monolog/monolog/src/Monolog/Handler/PsrHandler.php +++ b/vendor/monolog/monolog/src/Monolog/Handler/PsrHandler.php @@ -31,7 +31,7 @@ class PsrHandler extends AbstractHandler /** * @param LoggerInterface $logger The underlying PSR-3 compliant logger to which messages will be proxied * @param int $level The minimum logging level at which this handler will be triggered - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not */ public function __construct(LoggerInterface $logger, $level = Logger::DEBUG, $bubble = true) { diff --git a/vendor/monolog/monolog/src/Monolog/Handler/PushoverHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/PushoverHandler.php index bba720059..f27bb3da0 100644 --- a/vendor/monolog/monolog/src/Monolog/Handler/PushoverHandler.php +++ b/vendor/monolog/monolog/src/Monolog/Handler/PushoverHandler.php @@ -69,8 +69,8 @@ class PushoverHandler extends SocketHandler * @param string|array $users Pushover user id or array of ids the message will be sent to * @param string $title Title sent to the Pushover API * @param int $level The minimum logging level at which this handler will be triggered - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not - * @param Boolean $useSSL Whether to connect via SSL. Required when pushing messages to users that are not + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + * @param bool $useSSL Whether to connect via SSL. Required when pushing messages to users that are not * the pushover.net app owner. OpenSSL is required for this option. * @param int $highPriorityLevel The minimum logging level at which this handler will start * sending "high priority" requests to the Pushover API @@ -180,6 +180,6 @@ public function setEmergencyLevel($value) */ public function useFormattedMessage($value) { - $this->useFormattedMessage = (boolean) $value; + $this->useFormattedMessage = (bool) $value; } } diff --git a/vendor/monolog/monolog/src/Monolog/Handler/RavenHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/RavenHandler.php index 53a8b391d..10d7f43bf 100644 --- a/vendor/monolog/monolog/src/Monolog/Handler/RavenHandler.php +++ b/vendor/monolog/monolog/src/Monolog/Handler/RavenHandler.php @@ -18,7 +18,7 @@ /** * Handler to send messages to a Sentry (https://github.com/getsentry/sentry) server - * using raven-php (https://github.com/getsentry/raven-php) + * using sentry-php (https://github.com/getsentry/sentry-php) * * @author Marc Abramowitz */ @@ -27,7 +27,7 @@ class RavenHandler extends AbstractProcessingHandler /** * Translates Monolog log levels to Raven log levels. */ - private $logLevels = array( + protected $logLevels = array( Logger::DEBUG => Raven_Client::DEBUG, Logger::INFO => Raven_Client::INFO, Logger::NOTICE => Raven_Client::INFO, @@ -42,7 +42,7 @@ class RavenHandler extends AbstractProcessingHandler * @var string should represent the current version of the calling * software. Can be any string (git commit, version number) */ - private $release; + protected $release; /** * @var Raven_Client the client object that sends the message to the server @@ -57,7 +57,7 @@ class RavenHandler extends AbstractProcessingHandler /** * @param Raven_Client $ravenClient * @param int $level The minimum logging level at which this handler will be triggered - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not */ public function __construct(Raven_Client $ravenClient, $level = Logger::DEBUG, $bubble = true) { @@ -180,7 +180,7 @@ protected function write(array $record) } if (isset($record['context']['exception']) && ($record['context']['exception'] instanceof \Exception || (PHP_VERSION_ID >= 70000 && $record['context']['exception'] instanceof \Throwable))) { - $options['extra']['message'] = $record['formatted']; + $options['message'] = $record['formatted']; $this->ravenClient->captureException($record['context']['exception'], $options); } else { $this->ravenClient->captureMessage($record['formatted'], array(), $options); @@ -216,7 +216,7 @@ protected function getDefaultBatchFormatter() */ protected function getExtraParameters() { - return array('checksum', 'release', 'event_id'); + return array('contexts', 'checksum', 'release', 'event_id'); } /** diff --git a/vendor/monolog/monolog/src/Monolog/Handler/RollbarHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/RollbarHandler.php index 6c8a3e3ed..65073ffe3 100644 --- a/vendor/monolog/monolog/src/Monolog/Handler/RollbarHandler.php +++ b/vendor/monolog/monolog/src/Monolog/Handler/RollbarHandler.php @@ -129,4 +129,16 @@ public function close() { $this->flush(); } + + /** + * {@inheritdoc} + */ + public function reset() + { + $this->flush(); + + parent::reset(); + } + + } diff --git a/vendor/monolog/monolog/src/Monolog/Handler/RotatingFileHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/RotatingFileHandler.php index 3b60b3d15..ae2309f8a 100644 --- a/vendor/monolog/monolog/src/Monolog/Handler/RotatingFileHandler.php +++ b/vendor/monolog/monolog/src/Monolog/Handler/RotatingFileHandler.php @@ -39,9 +39,9 @@ class RotatingFileHandler extends StreamHandler * @param string $filename * @param int $maxFiles The maximal amount of files to keep (0 means unlimited) * @param int $level The minimum logging level at which this handler will be triggered - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not * @param int|null $filePermission Optional file permissions (default (0644) are only for owner read/write) - * @param Boolean $useLocking Try to lock log file before doing any writes + * @param bool $useLocking Try to lock log file before doing any writes */ public function __construct($filename, $maxFiles = 0, $level = Logger::DEBUG, $bubble = true, $filePermission = null, $useLocking = false) { @@ -66,6 +66,18 @@ public function close() } } + /** + * {@inheritdoc} + */ + public function reset() + { + parent::reset(); + + if (true === $this->mustRotate) { + $this->rotate(); + } + } + public function setFilenameFormat($filenameFormat, $dateFormat) { if (!preg_match('{^Y(([/_.-]?m)([/_.-]?d)?)?$}', $dateFormat)) { @@ -166,7 +178,7 @@ protected function getGlobPattern() $fileInfo = pathinfo($this->filename); $glob = str_replace( array('{filename}', '{date}'), - array($fileInfo['filename'], '*'), + array($fileInfo['filename'], '[0-9][0-9][0-9][0-9]*'), $fileInfo['dirname'] . '/' . $this->filenameFormat ); if (!empty($fileInfo['extension'])) { diff --git a/vendor/monolog/monolog/src/Monolog/Handler/Slack/SlackRecord.php b/vendor/monolog/monolog/src/Monolog/Handler/Slack/SlackRecord.php old mode 100644 new mode 100755 index 38bc838aa..e55e0e2e5 --- a/vendor/monolog/monolog/src/Monolog/Handler/Slack/SlackRecord.php +++ b/vendor/monolog/monolog/src/Monolog/Handler/Slack/SlackRecord.php @@ -146,7 +146,7 @@ public function getSlackData(array $record) if ($this->useShortAttachment) { $attachment['fields'][] = $this->generateAttachmentField( - ucfirst($key), + $key, $record[$key] ); } else { @@ -229,8 +229,8 @@ public function setFormatter(FormatterInterface $formatter) /** * Generates attachment field * - * @param string $title - * @param string|array $value\ + * @param string $title + * @param string|array $value * * @return array */ @@ -241,7 +241,7 @@ private function generateAttachmentField($title, $value) : $value; return array( - 'title' => $title, + 'title' => ucfirst($title), 'value' => $value, 'short' => false ); @@ -257,7 +257,7 @@ private function generateAttachmentField($title, $value) private function generateAttachmentFields(array $data) { $fields = array(); - foreach ($data as $key => $value) { + foreach ($this->normalizerFormatter->format($data) as $key => $value) { $fields[] = $this->generateAttachmentField($key, $value); } diff --git a/vendor/monolog/monolog/src/Monolog/Handler/SlackHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/SlackHandler.php index 3ac4d8368..45d634f46 100644 --- a/vendor/monolog/monolog/src/Monolog/Handler/SlackHandler.php +++ b/vendor/monolog/monolog/src/Monolog/Handler/SlackHandler.php @@ -75,6 +75,11 @@ public function getSlackRecord() return $this->slackRecord; } + public function getToken() + { + return $this->token; + } + /** * {@inheritdoc} * diff --git a/vendor/monolog/monolog/src/Monolog/Handler/SlackWebhookHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/SlackWebhookHandler.php index 9a1bbb440..1ef85faea 100644 --- a/vendor/monolog/monolog/src/Monolog/Handler/SlackWebhookHandler.php +++ b/vendor/monolog/monolog/src/Monolog/Handler/SlackWebhookHandler.php @@ -70,6 +70,11 @@ public function getSlackRecord() return $this->slackRecord; } + public function getWebhookUrl() + { + return $this->webhookUrl; + } + /** * {@inheritdoc} * diff --git a/vendor/monolog/monolog/src/Monolog/Handler/SocketHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/SocketHandler.php index 7a61bf4e0..db50d97fe 100644 --- a/vendor/monolog/monolog/src/Monolog/Handler/SocketHandler.php +++ b/vendor/monolog/monolog/src/Monolog/Handler/SocketHandler.php @@ -27,15 +27,16 @@ class SocketHandler extends AbstractProcessingHandler private $timeout = 0; private $writingTimeout = 10; private $lastSentBytes = null; + private $chunkSize = null; private $persistent = false; private $errno; private $errstr; private $lastWritingAt; /** - * @param string $connectionString Socket connection string - * @param int $level The minimum logging level at which this handler will be triggered - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + * @param string $connectionString Socket connection string + * @param int $level The minimum logging level at which this handler will be triggered + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not */ public function __construct($connectionString, $level = Logger::DEBUG, $bubble = true) { @@ -87,7 +88,7 @@ public function closeSocket() */ public function setPersistent($persistent) { - $this->persistent = (boolean) $persistent; + $this->persistent = (bool) $persistent; } /** @@ -127,6 +128,16 @@ public function setWritingTimeout($seconds) $this->writingTimeout = (float) $seconds; } + /** + * Set chunk size. Only has effect during connection in the writing cycle. + * + * @param float $bytes + */ + public function setChunkSize($bytes) + { + $this->chunkSize = $bytes; + } + /** * Get current connection string * @@ -177,6 +188,16 @@ public function getWritingTimeout() return $this->writingTimeout; } + /** + * Get current chunk size + * + * @return float + */ + public function getChunkSize() + { + return $this->chunkSize; + } + /** * Check to see if the socket is currently available. * @@ -219,6 +240,16 @@ protected function streamSetTimeout() return stream_set_timeout($this->resource, $seconds, $microseconds); } + /** + * Wrapper to allow mocking + * + * @see http://php.net/manual/en/function.stream-set-chunk-size.php + */ + protected function streamSetChunkSize() + { + return stream_set_chunk_size($this->resource, $this->chunkSize); + } + /** * Wrapper to allow mocking */ @@ -268,6 +299,7 @@ private function connect() { $this->createSocketResource(); $this->setSocketTimeout(); + $this->setStreamChunkSize(); } private function createSocketResource() @@ -290,6 +322,13 @@ private function setSocketTimeout() } } + private function setStreamChunkSize() + { + if ($this->chunkSize && !$this->streamSetChunkSize()) { + throw new \UnexpectedValueException("Failed setting chunk size with stream_set_chunk_size()"); + } + } + private function writeToSocket($data) { $length = strlen($data); diff --git a/vendor/monolog/monolog/src/Monolog/Handler/StreamHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/StreamHandler.php index 09a157387..a35b7e4c3 100644 --- a/vendor/monolog/monolog/src/Monolog/Handler/StreamHandler.php +++ b/vendor/monolog/monolog/src/Monolog/Handler/StreamHandler.php @@ -32,9 +32,9 @@ class StreamHandler extends AbstractProcessingHandler /** * @param resource|string $stream * @param int $level The minimum logging level at which this handler will be triggered - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not * @param int|null $filePermission Optional file permissions (default (0644) are only for owner read/write) - * @param Boolean $useLocking Try to lock log file before doing any writes + * @param bool $useLocking Try to lock log file before doing any writes * * @throws \Exception If a missing directory is not buildable * @throws \InvalidArgumentException If stream is not a resource or string @@ -167,7 +167,7 @@ private function createDir() set_error_handler(array($this, 'customErrorHandler')); $status = mkdir($dir, 0777, true); restore_error_handler(); - if (false === $status) { + if (false === $status && !is_dir($dir)) { throw new \UnexpectedValueException(sprintf('There is no existing directory at "%s" and its not buildable: '.$this->errorMessage, $dir)); } } diff --git a/vendor/monolog/monolog/src/Monolog/Handler/SwiftMailerHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/SwiftMailerHandler.php index 72f44a532..ac7b16ff8 100644 --- a/vendor/monolog/monolog/src/Monolog/Handler/SwiftMailerHandler.php +++ b/vendor/monolog/monolog/src/Monolog/Handler/SwiftMailerHandler.php @@ -12,6 +12,7 @@ namespace Monolog\Handler; use Monolog\Logger; +use Monolog\Formatter\FormatterInterface; use Monolog\Formatter\LineFormatter; use Swift; @@ -29,7 +30,7 @@ class SwiftMailerHandler extends MailHandler * @param \Swift_Mailer $mailer The mailer to use * @param callable|\Swift_Message $message An example message for real messages, only the body will be replaced * @param int $level The minimum logging level at which this handler will be triggered - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not */ public function __construct(\Swift_Mailer $mailer, $message, $level = Logger::ERROR, $bubble = true) { @@ -47,6 +48,17 @@ protected function send($content, array $records) $this->mailer->send($this->buildMessage($content, $records)); } + /** + * Gets the formatter for the Swift_Message subject. + * + * @param string $format The format of the subject + * @return FormatterInterface + */ + protected function getSubjectFormatter($format) + { + return new LineFormatter($format); + } + /** * Creates instance of Swift_Message to be sent * @@ -69,7 +81,7 @@ protected function buildMessage($content, array $records) } if ($records) { - $subjectFormatter = new LineFormatter($message->getSubject()); + $subjectFormatter = $this->getSubjectFormatter($message->getSubject()); $message->setSubject($subjectFormatter->format($this->getHighestRecord($records))); } diff --git a/vendor/monolog/monolog/src/Monolog/Handler/SyslogHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/SyslogHandler.php index 376bc3b24..f770c8028 100644 --- a/vendor/monolog/monolog/src/Monolog/Handler/SyslogHandler.php +++ b/vendor/monolog/monolog/src/Monolog/Handler/SyslogHandler.php @@ -32,11 +32,11 @@ class SyslogHandler extends AbstractSyslogHandler protected $logopts; /** - * @param string $ident - * @param mixed $facility - * @param int $level The minimum logging level at which this handler will be triggered - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not - * @param int $logopts Option flags for the openlog() call, defaults to LOG_PID + * @param string $ident + * @param mixed $facility + * @param int $level The minimum logging level at which this handler will be triggered + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + * @param int $logopts Option flags for the openlog() call, defaults to LOG_PID */ public function __construct($ident, $facility = LOG_USER, $level = Logger::DEBUG, $bubble = true, $logopts = LOG_PID) { diff --git a/vendor/monolog/monolog/src/Monolog/Handler/SyslogUdpHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/SyslogUdpHandler.php index 4718711b3..e14b378ce 100644 --- a/vendor/monolog/monolog/src/Monolog/Handler/SyslogUdpHandler.php +++ b/vendor/monolog/monolog/src/Monolog/Handler/SyslogUdpHandler.php @@ -25,12 +25,12 @@ class SyslogUdpHandler extends AbstractSyslogHandler protected $ident; /** - * @param string $host - * @param int $port - * @param mixed $facility - * @param int $level The minimum logging level at which this handler will be triggered - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not - * @param string $ident Program name or tag for each log message. + * @param string $host + * @param int $port + * @param mixed $facility + * @param int $level The minimum logging level at which this handler will be triggered + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + * @param string $ident Program name or tag for each log message. */ public function __construct($host, $port = 514, $facility = LOG_USER, $level = Logger::DEBUG, $bubble = true, $ident = 'php') { diff --git a/vendor/monolog/monolog/src/Monolog/Handler/TestHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/TestHandler.php index e39cfc667..b6b1343bd 100644 --- a/vendor/monolog/monolog/src/Monolog/Handler/TestHandler.php +++ b/vendor/monolog/monolog/src/Monolog/Handler/TestHandler.php @@ -84,14 +84,24 @@ public function hasRecords($level) return isset($this->recordsByLevel[$level]); } + /** + * @param string|array $record Either a message string or an array containing message and optionally context keys that will be checked against all records + * @param int $level Logger::LEVEL constant value + */ public function hasRecord($record, $level) { - if (is_array($record)) { - $record = $record['message']; + if (is_string($record)) { + $record = array('message' => $record); } return $this->hasRecordThatPasses(function ($rec) use ($record) { - return $rec['message'] === $record; + if ($rec['message'] !== $record['message']) { + return false; + } + if (isset($record['context']) && $rec['context'] !== $record['context']) { + return false; + } + return true; }, $level); } diff --git a/vendor/monolog/monolog/src/Monolog/Handler/WhatFailureGroupHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/WhatFailureGroupHandler.php index 2732ba3d6..6bc4671c7 100644 --- a/vendor/monolog/monolog/src/Monolog/Handler/WhatFailureGroupHandler.php +++ b/vendor/monolog/monolog/src/Monolog/Handler/WhatFailureGroupHandler.php @@ -48,6 +48,16 @@ public function handle(array $record) */ public function handleBatch(array $records) { + if ($this->processors) { + $processed = array(); + foreach ($records as $record) { + foreach ($this->processors as $processor) { + $processed[] = call_user_func($processor, $record); + } + } + $records = $processed; + } + foreach ($this->handlers as $handler) { try { $handler->handleBatch($records); diff --git a/vendor/monolog/monolog/src/Monolog/Logger.php b/vendor/monolog/monolog/src/Monolog/Logger.php index 49d00af1f..05dfc8179 100644 --- a/vendor/monolog/monolog/src/Monolog/Logger.php +++ b/vendor/monolog/monolog/src/Monolog/Logger.php @@ -15,6 +15,7 @@ use Monolog\Handler\StreamHandler; use Psr\Log\LoggerInterface; use Psr\Log\InvalidArgumentException; +use Exception; /** * Monolog log channel @@ -24,7 +25,7 @@ * * @author Jordi Boggiano */ -class Logger implements LoggerInterface +class Logger implements LoggerInterface, ResettableInterface { /** * Detailed debug information @@ -133,6 +134,11 @@ class Logger implements LoggerInterface */ protected $microsecondTimestamps = true; + /** + * @var callable + */ + protected $exceptionHandler; + /** * @param string $name The logging channel * @param HandlerInterface[] $handlers Optional stack of handlers, the first one in the array is called first, etc. @@ -141,7 +147,7 @@ class Logger implements LoggerInterface public function __construct($name, array $handlers = array(), array $processors = array()) { $this->name = $name; - $this->handlers = $handlers; + $this->setHandlers($handlers); $this->processors = $processors; } @@ -281,7 +287,7 @@ public function useMicrosecondTimestamps($micro) * @param int $level The logging level * @param string $message The log message * @param array $context The log context - * @return Boolean Whether the record has been processed + * @return bool Whether the record has been processed */ public function addRecord($level, $message, array $context = array()) { @@ -329,27 +335,75 @@ public function addRecord($level, $message, array $context = array()) 'extra' => array(), ); - foreach ($this->processors as $processor) { - $record = call_user_func($processor, $record); + try { + foreach ($this->processors as $processor) { + $record = call_user_func($processor, $record); + } + + while ($handler = current($this->handlers)) { + if (true === $handler->handle($record)) { + break; + } + + next($this->handlers); + } + } catch (Exception $e) { + $this->handleException($e, $record); } - while ($handler = current($this->handlers)) { - if (true === $handler->handle($record)) { - break; + return true; + } + + /** + * Ends a log cycle and frees all resources used by handlers. + * + * Closing a Handler means flushing all buffers and freeing any open resources/handles. + * Handlers that have been closed should be able to accept log records again and re-open + * themselves on demand, but this may not always be possible depending on implementation. + * + * This is useful at the end of a request and will be called automatically on every handler + * when they get destructed. + */ + public function close() + { + foreach ($this->handlers as $handler) { + if (method_exists($handler, 'close')) { + $handler->close(); } + } + } - next($this->handlers); + /** + * Ends a log cycle and resets all handlers and processors to their initial state. + * + * Resetting a Handler or a Processor means flushing/cleaning all buffers, resetting internal + * state, and getting it back to a state in which it can receive log records again. + * + * This is useful in case you want to avoid logs leaking between two requests or jobs when you + * have a long running process like a worker or an application server serving multiple requests + * in one process. + */ + public function reset() + { + foreach ($this->handlers as $handler) { + if ($handler instanceof ResettableInterface) { + $handler->reset(); + } } - return true; + foreach ($this->processors as $processor) { + if ($processor instanceof ResettableInterface) { + $processor->reset(); + } + } } /** * Adds a log record at the DEBUG level. * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed + * @param string $message The log message + * @param array $context The log context + * @return bool Whether the record has been processed */ public function addDebug($message, array $context = array()) { @@ -359,9 +413,9 @@ public function addDebug($message, array $context = array()) /** * Adds a log record at the INFO level. * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed + * @param string $message The log message + * @param array $context The log context + * @return bool Whether the record has been processed */ public function addInfo($message, array $context = array()) { @@ -371,9 +425,9 @@ public function addInfo($message, array $context = array()) /** * Adds a log record at the NOTICE level. * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed + * @param string $message The log message + * @param array $context The log context + * @return bool Whether the record has been processed */ public function addNotice($message, array $context = array()) { @@ -383,9 +437,9 @@ public function addNotice($message, array $context = array()) /** * Adds a log record at the WARNING level. * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed + * @param string $message The log message + * @param array $context The log context + * @return bool Whether the record has been processed */ public function addWarning($message, array $context = array()) { @@ -395,9 +449,9 @@ public function addWarning($message, array $context = array()) /** * Adds a log record at the ERROR level. * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed + * @param string $message The log message + * @param array $context The log context + * @return bool Whether the record has been processed */ public function addError($message, array $context = array()) { @@ -407,9 +461,9 @@ public function addError($message, array $context = array()) /** * Adds a log record at the CRITICAL level. * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed + * @param string $message The log message + * @param array $context The log context + * @return bool Whether the record has been processed */ public function addCritical($message, array $context = array()) { @@ -419,9 +473,9 @@ public function addCritical($message, array $context = array()) /** * Adds a log record at the ALERT level. * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed + * @param string $message The log message + * @param array $context The log context + * @return bool Whether the record has been processed */ public function addAlert($message, array $context = array()) { @@ -431,9 +485,9 @@ public function addAlert($message, array $context = array()) /** * Adds a log record at the EMERGENCY level. * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed + * @param string $message The log message + * @param array $context The log context + * @return bool Whether the record has been processed */ public function addEmergency($message, array $context = array()) { @@ -484,7 +538,7 @@ public static function toMonologLevel($level) * Checks whether the Logger has a handler that listens on the given level * * @param int $level - * @return Boolean + * @return bool */ public function isHandling($level) { @@ -501,15 +555,52 @@ public function isHandling($level) return false; } + /** + * Set a custom exception handler + * + * @param callable $callback + * @return $this + */ + public function setExceptionHandler($callback) + { + if (!is_callable($callback)) { + throw new \InvalidArgumentException('Exception handler must be valid callable (callback or object with an __invoke method), '.var_export($callback, true).' given'); + } + $this->exceptionHandler = $callback; + + return $this; + } + + /** + * @return callable + */ + public function getExceptionHandler() + { + return $this->exceptionHandler; + } + + /** + * Delegates exception management to the custom exception handler, + * or throws the exception if no custom handler is set. + */ + protected function handleException(Exception $e, array $record) + { + if (!$this->exceptionHandler) { + throw $e; + } + + call_user_func($this->exceptionHandler, $e, $record); + } + /** * Adds a log record at an arbitrary level. * * This method allows for compatibility with common interfaces. * * @param mixed $level The log level - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed + * @param string $message The log message + * @param array $context The log context + * @return bool Whether the record has been processed */ public function log($level, $message, array $context = array()) { @@ -523,9 +614,9 @@ public function log($level, $message, array $context = array()) * * This method allows for compatibility with common interfaces. * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed + * @param string $message The log message + * @param array $context The log context + * @return bool Whether the record has been processed */ public function debug($message, array $context = array()) { @@ -537,9 +628,9 @@ public function debug($message, array $context = array()) * * This method allows for compatibility with common interfaces. * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed + * @param string $message The log message + * @param array $context The log context + * @return bool Whether the record has been processed */ public function info($message, array $context = array()) { @@ -551,9 +642,9 @@ public function info($message, array $context = array()) * * This method allows for compatibility with common interfaces. * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed + * @param string $message The log message + * @param array $context The log context + * @return bool Whether the record has been processed */ public function notice($message, array $context = array()) { @@ -565,9 +656,9 @@ public function notice($message, array $context = array()) * * This method allows for compatibility with common interfaces. * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed + * @param string $message The log message + * @param array $context The log context + * @return bool Whether the record has been processed */ public function warn($message, array $context = array()) { @@ -579,9 +670,9 @@ public function warn($message, array $context = array()) * * This method allows for compatibility with common interfaces. * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed + * @param string $message The log message + * @param array $context The log context + * @return bool Whether the record has been processed */ public function warning($message, array $context = array()) { @@ -593,9 +684,9 @@ public function warning($message, array $context = array()) * * This method allows for compatibility with common interfaces. * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed + * @param string $message The log message + * @param array $context The log context + * @return bool Whether the record has been processed */ public function err($message, array $context = array()) { @@ -607,9 +698,9 @@ public function err($message, array $context = array()) * * This method allows for compatibility with common interfaces. * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed + * @param string $message The log message + * @param array $context The log context + * @return bool Whether the record has been processed */ public function error($message, array $context = array()) { @@ -621,9 +712,9 @@ public function error($message, array $context = array()) * * This method allows for compatibility with common interfaces. * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed + * @param string $message The log message + * @param array $context The log context + * @return bool Whether the record has been processed */ public function crit($message, array $context = array()) { @@ -635,9 +726,9 @@ public function crit($message, array $context = array()) * * This method allows for compatibility with common interfaces. * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed + * @param string $message The log message + * @param array $context The log context + * @return bool Whether the record has been processed */ public function critical($message, array $context = array()) { @@ -649,9 +740,9 @@ public function critical($message, array $context = array()) * * This method allows for compatibility with common interfaces. * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed + * @param string $message The log message + * @param array $context The log context + * @return bool Whether the record has been processed */ public function alert($message, array $context = array()) { @@ -663,9 +754,9 @@ public function alert($message, array $context = array()) * * This method allows for compatibility with common interfaces. * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed + * @param string $message The log message + * @param array $context The log context + * @return bool Whether the record has been processed */ public function emerg($message, array $context = array()) { @@ -677,9 +768,9 @@ public function emerg($message, array $context = array()) * * This method allows for compatibility with common interfaces. * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed + * @param string $message The log message + * @param array $context The log context + * @return bool Whether the record has been processed */ public function emergency($message, array $context = array()) { diff --git a/vendor/monolog/monolog/src/Monolog/Processor/GitProcessor.php b/vendor/monolog/monolog/src/Monolog/Processor/GitProcessor.php index 1899400dc..9fc3f50f0 100644 --- a/vendor/monolog/monolog/src/Monolog/Processor/GitProcessor.php +++ b/vendor/monolog/monolog/src/Monolog/Processor/GitProcessor.php @@ -19,7 +19,7 @@ * @author Nick Otter * @author Jordi Boggiano */ -class GitProcessor +class GitProcessor implements ProcessorInterface { private $level; private static $cache; diff --git a/vendor/monolog/monolog/src/Monolog/Processor/IntrospectionProcessor.php b/vendor/monolog/monolog/src/Monolog/Processor/IntrospectionProcessor.php index 2c07caede..6ae192a23 100644 --- a/vendor/monolog/monolog/src/Monolog/Processor/IntrospectionProcessor.php +++ b/vendor/monolog/monolog/src/Monolog/Processor/IntrospectionProcessor.php @@ -24,7 +24,7 @@ * * @author Jordi Boggiano */ -class IntrospectionProcessor +class IntrospectionProcessor implements ProcessorInterface { private $level; diff --git a/vendor/monolog/monolog/src/Monolog/Processor/MemoryProcessor.php b/vendor/monolog/monolog/src/Monolog/Processor/MemoryProcessor.php index 85f9dc5e7..2a379a302 100644 --- a/vendor/monolog/monolog/src/Monolog/Processor/MemoryProcessor.php +++ b/vendor/monolog/monolog/src/Monolog/Processor/MemoryProcessor.php @@ -16,7 +16,7 @@ * * @author Rob Jensen */ -abstract class MemoryProcessor +abstract class MemoryProcessor implements ProcessorInterface { /** * @var bool If true, get the real size of memory allocated from system. Else, only the memory used by emalloc() is reported. @@ -34,8 +34,8 @@ abstract class MemoryProcessor */ public function __construct($realUsage = true, $useFormatting = true) { - $this->realUsage = (boolean) $realUsage; - $this->useFormatting = (boolean) $useFormatting; + $this->realUsage = (bool) $realUsage; + $this->useFormatting = (bool) $useFormatting; } /** diff --git a/vendor/monolog/monolog/src/Monolog/Processor/MercurialProcessor.php b/vendor/monolog/monolog/src/Monolog/Processor/MercurialProcessor.php index 7c07a7e99..2f5b32659 100644 --- a/vendor/monolog/monolog/src/Monolog/Processor/MercurialProcessor.php +++ b/vendor/monolog/monolog/src/Monolog/Processor/MercurialProcessor.php @@ -18,7 +18,7 @@ * * @author Jonathan A. Schweder */ -class MercurialProcessor +class MercurialProcessor implements ProcessorInterface { private $level; private static $cache; diff --git a/vendor/monolog/monolog/src/Monolog/Processor/ProcessIdProcessor.php b/vendor/monolog/monolog/src/Monolog/Processor/ProcessIdProcessor.php index 9d3f5590f..66b80fbbd 100644 --- a/vendor/monolog/monolog/src/Monolog/Processor/ProcessIdProcessor.php +++ b/vendor/monolog/monolog/src/Monolog/Processor/ProcessIdProcessor.php @@ -16,7 +16,7 @@ * * @author Andreas Hörnicke */ -class ProcessIdProcessor +class ProcessIdProcessor implements ProcessorInterface { /** * @param array $record diff --git a/vendor/monolog/monolog/src/Monolog/Processor/ProcessorInterface.php b/vendor/monolog/monolog/src/Monolog/Processor/ProcessorInterface.php new file mode 100644 index 000000000..7e64d4dfa --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Processor/ProcessorInterface.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 Monolog\Processor; + +/** + * An optional interface to allow labelling Monolog processors. + * + * @author Nicolas Grekas + */ +interface ProcessorInterface +{ + /** + * @return array The processed records + */ + public function __invoke(array $records); +} diff --git a/vendor/monolog/monolog/src/Monolog/Processor/PsrLogMessageProcessor.php b/vendor/monolog/monolog/src/Monolog/Processor/PsrLogMessageProcessor.php index c2686ce5b..008850545 100644 --- a/vendor/monolog/monolog/src/Monolog/Processor/PsrLogMessageProcessor.php +++ b/vendor/monolog/monolog/src/Monolog/Processor/PsrLogMessageProcessor.php @@ -11,6 +11,8 @@ namespace Monolog\Processor; +use Monolog\Utils; + /** * Processes a record's message according to PSR-3 rules * @@ -18,7 +20,7 @@ * * @author Jordi Boggiano */ -class PsrLogMessageProcessor +class PsrLogMessageProcessor implements ProcessorInterface { /** * @param array $record @@ -35,7 +37,7 @@ public function __invoke(array $record) if (is_null($val) || is_scalar($val) || (is_object($val) && method_exists($val, "__toString"))) { $replacements['{'.$key.'}'] = $val; } elseif (is_object($val)) { - $replacements['{'.$key.'}'] = '[object '.get_class($val).']'; + $replacements['{'.$key.'}'] = '[object '.Utils::getClass($val).']'; } else { $replacements['{'.$key.'}'] = '['.gettype($val).']'; } diff --git a/vendor/monolog/monolog/src/Monolog/Processor/TagProcessor.php b/vendor/monolog/monolog/src/Monolog/Processor/TagProcessor.php index 7e2df2acb..615a4d991 100644 --- a/vendor/monolog/monolog/src/Monolog/Processor/TagProcessor.php +++ b/vendor/monolog/monolog/src/Monolog/Processor/TagProcessor.php @@ -16,7 +16,7 @@ * * @author Martijn Riemers */ -class TagProcessor +class TagProcessor implements ProcessorInterface { private $tags; diff --git a/vendor/monolog/monolog/src/Monolog/Processor/UidProcessor.php b/vendor/monolog/monolog/src/Monolog/Processor/UidProcessor.php index 812707cdb..d1f708cf2 100644 --- a/vendor/monolog/monolog/src/Monolog/Processor/UidProcessor.php +++ b/vendor/monolog/monolog/src/Monolog/Processor/UidProcessor.php @@ -11,12 +11,14 @@ namespace Monolog\Processor; +use Monolog\ResettableInterface; + /** * Adds a unique identifier into records * * @author Simon Mönch */ -class UidProcessor +class UidProcessor implements ProcessorInterface, ResettableInterface { private $uid; @@ -26,7 +28,8 @@ public function __construct($length = 7) throw new \InvalidArgumentException('The uid length must be an integer between 1 and 32'); } - $this->uid = substr(hash('md5', uniqid('', true)), 0, $length); + + $this->uid = $this->generateUid($length); } public function __invoke(array $record) @@ -43,4 +46,14 @@ public function getUid() { return $this->uid; } + + public function reset() + { + $this->uid = $this->generateUid(strlen($this->uid)); + } + + private function generateUid($length) + { + return substr(hash('md5', uniqid('', true)), 0, $length); + } } diff --git a/vendor/monolog/monolog/src/Monolog/Processor/WebProcessor.php b/vendor/monolog/monolog/src/Monolog/Processor/WebProcessor.php index ea1d89782..684188f66 100644 --- a/vendor/monolog/monolog/src/Monolog/Processor/WebProcessor.php +++ b/vendor/monolog/monolog/src/Monolog/Processor/WebProcessor.php @@ -16,7 +16,7 @@ * * @author Jordi Boggiano */ -class WebProcessor +class WebProcessor implements ProcessorInterface { /** * @var array|\ArrayAccess diff --git a/vendor/monolog/monolog/src/Monolog/ResettableInterface.php b/vendor/monolog/monolog/src/Monolog/ResettableInterface.php new file mode 100644 index 000000000..635bc77dc --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/ResettableInterface.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog; + +/** + * Handler or Processor implementing this interface will be reset when Logger::reset() is called. + * + * Resetting ends a log cycle gets them back to their initial state. + * + * Resetting a Handler or a Processor means flushing/cleaning all buffers, resetting internal + * state, and getting it back to a state in which it can receive log records again. + * + * This is useful in case you want to avoid logs leaking between two requests or jobs when you + * have a long running process like a worker or an application server serving multiple requests + * in one process. + * + * @author Grégoire Pineau + */ +interface ResettableInterface +{ + public function reset(); +} diff --git a/vendor/monolog/monolog/src/Monolog/SignalHandler.php b/vendor/monolog/monolog/src/Monolog/SignalHandler.php new file mode 100644 index 000000000..d5907805a --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/SignalHandler.php @@ -0,0 +1,115 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog; + +use Psr\Log\LoggerInterface; +use Psr\Log\LogLevel; +use ReflectionExtension; + +/** + * Monolog POSIX signal handler + * + * @author Robert Gust-Bardon + */ +class SignalHandler +{ + private $logger; + + private $previousSignalHandler = array(); + private $signalLevelMap = array(); + private $signalRestartSyscalls = array(); + + public function __construct(LoggerInterface $logger) + { + $this->logger = $logger; + } + + public function registerSignalHandler($signo, $level = LogLevel::CRITICAL, $callPrevious = true, $restartSyscalls = true, $async = true) + { + if (!extension_loaded('pcntl') || !function_exists('pcntl_signal')) { + return $this; + } + + if ($callPrevious) { + if (function_exists('pcntl_signal_get_handler')) { + $handler = pcntl_signal_get_handler($signo); + if ($handler === false) { + return $this; + } + $this->previousSignalHandler[$signo] = $handler; + } else { + $this->previousSignalHandler[$signo] = true; + } + } else { + unset($this->previousSignalHandler[$signo]); + } + $this->signalLevelMap[$signo] = $level; + $this->signalRestartSyscalls[$signo] = $restartSyscalls; + + if (function_exists('pcntl_async_signals') && $async !== null) { + pcntl_async_signals($async); + } + + pcntl_signal($signo, array($this, 'handleSignal'), $restartSyscalls); + + return $this; + } + + public function handleSignal($signo, array $siginfo = null) + { + static $signals = array(); + + if (!$signals && extension_loaded('pcntl')) { + $pcntl = new ReflectionExtension('pcntl'); + $constants = $pcntl->getConstants(); + if (!$constants) { + // HHVM 3.24.2 returns an empty array. + $constants = get_defined_constants(true); + $constants = $constants['Core']; + } + foreach ($constants as $name => $value) { + if (substr($name, 0, 3) === 'SIG' && $name[3] !== '_' && is_int($value)) { + $signals[$value] = $name; + } + } + unset($constants); + } + + $level = isset($this->signalLevelMap[$signo]) ? $this->signalLevelMap[$signo] : LogLevel::CRITICAL; + $signal = isset($signals[$signo]) ? $signals[$signo] : $signo; + $context = isset($siginfo) ? $siginfo : array(); + $this->logger->log($level, sprintf('Program received signal %s', $signal), $context); + + if (!isset($this->previousSignalHandler[$signo])) { + return; + } + + if ($this->previousSignalHandler[$signo] === true || $this->previousSignalHandler[$signo] === SIG_DFL) { + if (extension_loaded('pcntl') && function_exists('pcntl_signal') && function_exists('pcntl_sigprocmask') && function_exists('pcntl_signal_dispatch') + && extension_loaded('posix') && function_exists('posix_getpid') && function_exists('posix_kill')) { + $restartSyscalls = isset($this->restartSyscalls[$signo]) ? $this->restartSyscalls[$signo] : true; + pcntl_signal($signo, SIG_DFL, $restartSyscalls); + pcntl_sigprocmask(SIG_UNBLOCK, array($signo), $oldset); + posix_kill(posix_getpid(), $signo); + pcntl_signal_dispatch(); + pcntl_sigprocmask(SIG_SETMASK, $oldset); + pcntl_signal($signo, array($this, 'handleSignal'), $restartSyscalls); + } + } elseif (is_callable($this->previousSignalHandler[$signo])) { + if (PHP_VERSION_ID >= 70100) { + $this->previousSignalHandler[$signo]($signo, $siginfo); + } else { + $this->previousSignalHandler[$signo]($signo); + } + } + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Utils.php b/vendor/monolog/monolog/src/Monolog/Utils.php new file mode 100644 index 000000000..eb9be863b --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Utils.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 Monolog; + +class Utils +{ + /** + * @internal + */ + public static function getClass($object) + { + $class = \get_class($object); + + return 'c' === $class[0] && 0 === strpos($class, "class@anonymous\0") ? get_parent_class($class).'@anonymous' : $class; + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Formatter/FluentdFormatterTest.php b/vendor/monolog/monolog/tests/Monolog/Formatter/FluentdFormatterTest.php index 622b2bae2..fd36dbcf4 100644 --- a/vendor/monolog/monolog/tests/Monolog/Formatter/FluentdFormatterTest.php +++ b/vendor/monolog/monolog/tests/Monolog/Formatter/FluentdFormatterTest.php @@ -40,7 +40,7 @@ public function testFormat() $formatter = new FluentdFormatter(); $this->assertEquals( - '["test",0,{"message":"test","extra":[],"level":300,"level_name":"WARNING"}]', + '["test",0,{"message":"test","context":[],"extra":[],"level":300,"level_name":"WARNING"}]', $formatter->format($record) ); } @@ -55,7 +55,7 @@ public function testFormatWithTag() $formatter = new FluentdFormatter(true); $this->assertEquals( - '["test.error",0,{"message":"test","extra":[]}]', + '["test.error",0,{"message":"test","context":[],"extra":[]}]', $formatter->format($record) ); } diff --git a/vendor/monolog/monolog/tests/Monolog/Formatter/JsonFormatterTest.php b/vendor/monolog/monolog/tests/Monolog/Formatter/JsonFormatterTest.php index c9445f363..24b06cc94 100644 --- a/vendor/monolog/monolog/tests/Monolog/Formatter/JsonFormatterTest.php +++ b/vendor/monolog/monolog/tests/Monolog/Formatter/JsonFormatterTest.php @@ -180,4 +180,40 @@ private function formatException($exception, $previous = null) '}'; return $formattedException; } + + public function testNormalizeHandleLargeArraysWithExactly1000Items() + { + $formatter = new NormalizerFormatter(); + $largeArray = range(1, 1000); + + $res = $formatter->format(array( + 'level_name' => 'CRITICAL', + 'channel' => 'test', + 'message' => 'bar', + 'context' => array($largeArray), + 'datetime' => new \DateTime, + 'extra' => array(), + )); + + $this->assertCount(1000, $res['context'][0]); + $this->assertArrayNotHasKey('...', $res['context'][0]); + } + + public function testNormalizeHandleLargeArrays() + { + $formatter = new NormalizerFormatter(); + $largeArray = range(1, 2000); + + $res = $formatter->format(array( + 'level_name' => 'CRITICAL', + 'channel' => 'test', + 'message' => 'bar', + 'context' => array($largeArray), + 'datetime' => new \DateTime, + 'extra' => array(), + )); + + $this->assertCount(1001, $res['context'][0]); + $this->assertEquals('Over 1000 items (2000 total), aborting normalization', $res['context'][0]['...']); + } } diff --git a/vendor/monolog/monolog/tests/Monolog/Formatter/NormalizerFormatterTest.php b/vendor/monolog/monolog/tests/Monolog/Formatter/NormalizerFormatterTest.php index 57bcdf984..bafd1c74b 100644 --- a/vendor/monolog/monolog/tests/Monolog/Formatter/NormalizerFormatterTest.php +++ b/vendor/monolog/monolog/tests/Monolog/Formatter/NormalizerFormatterTest.php @@ -193,6 +193,15 @@ public function testIgnoresRecursiveObjectReferences() $this->assertEquals(@json_encode(array($foo, $bar)), $res); } + public function testCanNormalizeReferences() + { + $formatter = new NormalizerFormatter(); + $x = array('foo' => 'bar'); + $y = array('x' => &$x); + $x['y'] = &$y; + $formatter->format($y); + } + public function testIgnoresInvalidTypes() { // set up the recursion @@ -217,6 +226,24 @@ public function testIgnoresInvalidTypes() $this->assertEquals(@json_encode(array($resource)), $res); } + public function testNormalizeHandleLargeArraysWithExactly1000Items() + { + $formatter = new NormalizerFormatter(); + $largeArray = range(1, 1000); + + $res = $formatter->format(array( + 'level_name' => 'CRITICAL', + 'channel' => 'test', + 'message' => 'bar', + 'context' => array($largeArray), + 'datetime' => new \DateTime, + 'extra' => array(), + )); + + $this->assertCount(1000, $res['context'][0]); + $this->assertArrayNotHasKey('...', $res['context'][0]); + } + public function testNormalizeHandleLargeArrays() { $formatter = new NormalizerFormatter(); @@ -231,7 +258,7 @@ public function testNormalizeHandleLargeArrays() 'extra' => array(), )); - $this->assertCount(1000, $res['context'][0]); + $this->assertCount(1001, $res['context'][0]); $this->assertEquals('Over 1000 items (2000 total), aborting normalization', $res['context'][0]['...']); } @@ -380,6 +407,29 @@ public function testExceptionTraceWithArgs() $result['context']['exception']['trace'][0] ); } + + public function testExceptionTraceDoesNotLeakCallUserFuncArgs() + { + try { + $arg = new TestInfoLeak; + call_user_func(array($this, 'throwHelper'), $arg, $dt = new \DateTime()); + } catch (\Exception $e) { + } + + $formatter = new NormalizerFormatter(); + $record = array('context' => array('exception' => $e)); + $result = $formatter->format($record); + + $this->assertSame( + '{"function":"throwHelper","class":"Monolog\\\\Formatter\\\\NormalizerFormatterTest","type":"->","args":["[object] (Monolog\\\\Formatter\\\\TestInfoLeak)","'.$dt->format('Y-m-d H:i:s').'"]}', + $result['context']['exception']['trace'][0] + ); + } + + private function throwHelper($arg) + { + throw new \RuntimeException('Thrown'); + } } class TestFooNorm @@ -421,3 +471,11 @@ public function __toString() throw new \RuntimeException('Could not convert to string'); } } + +class TestInfoLeak +{ + public function __toString() + { + return 'Sensitive information'; + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/BrowserConsoleHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/BrowserConsoleHandlerTest.php index ffb1d746a..ffe45da2f 100644 --- a/vendor/monolog/monolog/tests/Monolog/Handler/BrowserConsoleHandlerTest.php +++ b/vendor/monolog/monolog/tests/Monolog/Handler/BrowserConsoleHandlerTest.php @@ -21,7 +21,7 @@ class BrowserConsoleHandlerTest extends TestCase { protected function setUp() { - BrowserConsoleHandler::reset(); + BrowserConsoleHandler::resetStatic(); } protected function generateScript() diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/ChromePHPHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/ChromePHPHandlerTest.php index 0449f8b1a..421cc4918 100644 --- a/vendor/monolog/monolog/tests/Monolog/Handler/ChromePHPHandlerTest.php +++ b/vendor/monolog/monolog/tests/Monolog/Handler/ChromePHPHandlerTest.php @@ -21,7 +21,7 @@ class ChromePHPHandlerTest extends TestCase { protected function setUp() { - TestChromePHPHandler::reset(); + TestChromePHPHandler::resetStatic(); $_SERVER['HTTP_USER_AGENT'] = 'Monolog Test; Chrome/1.0'; } @@ -136,7 +136,7 @@ class TestChromePHPHandler extends ChromePHPHandler { protected $headers = array(); - public static function reset() + public static function resetStatic() { self::$initialized = false; self::$overflowed = false; diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/FingersCrossedHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/FingersCrossedHandlerTest.php index b92bf437b..0ec36531a 100644 --- a/vendor/monolog/monolog/tests/Monolog/Handler/FingersCrossedHandlerTest.php +++ b/vendor/monolog/monolog/tests/Monolog/Handler/FingersCrossedHandlerTest.php @@ -58,7 +58,7 @@ public function testHandleStopsBufferingAfterTrigger() * @covers Monolog\Handler\FingersCrossedHandler::activate * @covers Monolog\Handler\FingersCrossedHandler::reset */ - public function testHandleRestartBufferingAfterReset() + public function testHandleResetBufferingAfterReset() { $test = new TestHandler(); $handler = new FingersCrossedHandler($test); @@ -76,7 +76,7 @@ public function testHandleRestartBufferingAfterReset() * @covers Monolog\Handler\FingersCrossedHandler::handle * @covers Monolog\Handler\FingersCrossedHandler::activate */ - public function testHandleRestartBufferingAfterBeingTriggeredWhenStopBufferingIsDisabled() + public function testHandleResetBufferingAfterBeingTriggeredWhenStopBufferingIsDisabled() { $test = new TestHandler(); $handler = new FingersCrossedHandler($test, Logger::WARNING, 0, false, false); diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/FirePHPHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/FirePHPHandlerTest.php index 0eb10a63f..7a404e660 100644 --- a/vendor/monolog/monolog/tests/Monolog/Handler/FirePHPHandlerTest.php +++ b/vendor/monolog/monolog/tests/Monolog/Handler/FirePHPHandlerTest.php @@ -21,7 +21,7 @@ class FirePHPHandlerTest extends TestCase { public function setUp() { - TestFirePHPHandler::reset(); + TestFirePHPHandler::resetStatic(); $_SERVER['HTTP_USER_AGENT'] = 'Monolog Test; FirePHP/1.0'; } @@ -77,7 +77,7 @@ class TestFirePHPHandler extends FirePHPHandler { protected $headers = array(); - public static function reset() + public static function resetStatic() { self::$initialized = false; self::$sendHeaders = true; diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/InsightOpsHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/InsightOpsHandlerTest.php new file mode 100644 index 000000000..97c18b592 --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/InsightOpsHandlerTest.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + + namespace Monolog\Handler; + + use Monolog\TestCase; + use Monolog\Logger; + +/** + * @author Robert Kaufmann III + * @author Gabriel Machado + */ +class InsightOpsHandlerTest extends TestCase +{ + /** + * @var resource + */ + private $resource; + + /** + * @var LogEntriesHandler + */ + private $handler; + + public function testWriteContent() + { + $this->createHandler(); + $this->handler->handle($this->getRecord(Logger::CRITICAL, 'Critical write test')); + + fseek($this->resource, 0); + $content = fread($this->resource, 1024); + + $this->assertRegexp('/testToken \[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\] test.CRITICAL: Critical write test/', $content); + } + + public function testWriteBatchContent() + { + $this->createHandler(); + $this->handler->handleBatch($this->getMultipleRecords()); + + fseek($this->resource, 0); + $content = fread($this->resource, 1024); + + $this->assertRegexp('/(testToken \[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\] .* \[\] \[\]\n){3}/', $content); + } + + private function createHandler() + { + $useSSL = extension_loaded('openssl'); + $args = array('testToken', 'us', $useSSL, Logger::DEBUG, true); + $this->resource = fopen('php://memory', 'a'); + $this->handler = $this->getMock( + '\Monolog\Handler\InsightOpsHandler', + array('fsockopen', 'streamSetTimeout', 'closeSocket'), + $args + ); + + $reflectionProperty = new \ReflectionProperty('\Monolog\Handler\SocketHandler', 'connectionString'); + $reflectionProperty->setAccessible(true); + $reflectionProperty->setValue($this->handler, 'localhost:1234'); + + $this->handler->expects($this->any()) + ->method('fsockopen') + ->will($this->returnValue($this->resource)); + $this->handler->expects($this->any()) + ->method('streamSetTimeout') + ->will($this->returnValue(true)); + $this->handler->expects($this->any()) + ->method('closeSocket') + ->will($this->returnValue(true)); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/RotatingFileHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/RotatingFileHandlerTest.php index f1feb2281..c6f5fac99 100644 --- a/vendor/monolog/monolog/tests/Monolog/Handler/RotatingFileHandlerTest.php +++ b/vendor/monolog/monolog/tests/Monolog/Handler/RotatingFileHandlerTest.php @@ -191,6 +191,40 @@ public function filenameFormatProvider() ); } + /** + * @dataProvider rotationWhenSimilarFilesExistTests + */ + public function testRotationWhenSimilarFileNamesExist($dateFormat) + { + touch($old1 = __DIR__.'/Fixtures/foo-foo-'.date($dateFormat).'.rot'); + touch($old2 = __DIR__.'/Fixtures/foo-bar-'.date($dateFormat).'.rot'); + + $log = __DIR__.'/Fixtures/foo-'.date($dateFormat).'.rot'; + + $handler = new RotatingFileHandler(__DIR__.'/Fixtures/foo.rot', 2); + $handler->setFormatter($this->getIdentityFormatter()); + $handler->setFilenameFormat('{filename}-{date}', $dateFormat); + $handler->handle($this->getRecord()); + $handler->close(); + + $this->assertTrue(file_exists($log)); + } + + public function rotationWhenSimilarFilesExistTests() + { + + return array( + 'Rotation is triggered when the file of the current day is not present but similar exists' + => array(RotatingFileHandler::FILE_PER_DAY), + + 'Rotation is triggered when the file of the current month is not present but similar exists' + => array(RotatingFileHandler::FILE_PER_MONTH), + + 'Rotation is triggered when the file of the current year is not present but similar exists' + => array(RotatingFileHandler::FILE_PER_YEAR), + ); + } + public function testReuseCurrentFile() { $log = __DIR__.'/Fixtures/foo-'.date('Y-m-d').'.rot'; diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/Slack/SlackRecordTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/Slack/SlackRecordTest.php index e1aa96d71..b9de73679 100644 --- a/vendor/monolog/monolog/tests/Monolog/Handler/Slack/SlackRecordTest.php +++ b/vendor/monolog/monolog/tests/Monolog/Handler/Slack/SlackRecordTest.php @@ -320,12 +320,12 @@ public function testAddsLongAttachmentWithContextAndExtra() 'short' => false, ), array( - 'title' => 'tags', + 'title' => 'Tags', 'value' => sprintf('```%s```', json_encode($extra['tags'])), 'short' => false ), array( - 'title' => 'test', + 'title' => 'Test', 'value' => $context['test'], 'short' => false ) @@ -353,6 +353,14 @@ public function testAddsTimestampToAttachment() $this->assertSame($record['datetime']->getTimestamp(), $attachment['ts']); } + public function testContextHasException() + { + $record = $this->getRecord(Logger::CRITICAL, 'This is a critical message.', array('exception' => new \Exception())); + $slackRecord = new SlackRecord(null, null, true, null, false, true); + $data = $slackRecord->getSlackData($record); + $this->assertInternalType('string', $data['attachments'][0]['fields'][1]['value']); + } + public function testExcludeExtraAndContextFields() { $record = $this->getRecord( @@ -368,12 +376,12 @@ public function testExcludeExtraAndContextFields() $expected = array( array( - 'title' => 'info', + 'title' => 'Info', 'value' => sprintf('```%s```', json_encode(array('author' => 'Jordi'), $this->jsonPrettyPrintFlag)), 'short' => false ), array( - 'title' => 'tags', + 'title' => 'Tags', 'value' => sprintf('```%s```', json_encode(array('web'))), 'short' => false ), diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/SocketHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/SocketHandlerTest.php index 1f9c1f287..1da987c9c 100644 --- a/vendor/monolog/monolog/tests/Monolog/Handler/SocketHandlerTest.php +++ b/vendor/monolog/monolog/tests/Monolog/Handler/SocketHandlerTest.php @@ -77,6 +77,13 @@ public function testSetWritingTimeout() $this->assertEquals(10.25, $this->handler->getWritingTimeout()); } + public function testSetChunkSize() + { + $this->createHandler('localhost:1234'); + $this->handler->setChunkSize(1025); + $this->assertEquals(1025, $this->handler->getChunkSize()); + } + public function testSetConnectionString() { $this->createHandler('tcp://localhost:9090'); @@ -120,6 +127,19 @@ public function testExceptionIsThrownIfCannotSetTimeout() $this->writeRecord('Hello world'); } + /** + * @expectedException UnexpectedValueException + */ + public function testExceptionIsThrownIfCannotSetChunkSize() + { + $this->setMockHandler(array('streamSetChunkSize')); + $this->handler->setChunkSize(8192); + $this->handler->expects($this->once()) + ->method('streamSetChunkSize') + ->will($this->returnValue(false)); + $this->writeRecord('Hello world'); + } + /** * @expectedException RuntimeException */ @@ -304,6 +324,12 @@ private function setMockHandler(array $methods = array()) ->will($this->returnValue(true)); } + if (!in_array('streamSetChunkSize', $methods)) { + $this->handler->expects($this->any()) + ->method('streamSetChunkSize') + ->will($this->returnValue(8192)); + } + $this->handler->setFormatter($this->getIdentityFormatter()); } } diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/TestHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/TestHandlerTest.php index bfb8d3df2..a7c4fc986 100644 --- a/vendor/monolog/monolog/tests/Monolog/Handler/TestHandlerTest.php +++ b/vendor/monolog/monolog/tests/Monolog/Handler/TestHandlerTest.php @@ -54,6 +54,52 @@ public function testHandler($method, $level) $this->assertEquals(array($record), $records); } + public function testHandlerAssertEmptyContext() { + $handler = new TestHandler; + $record = $this->getRecord(Logger::WARNING, 'test', array()); + $this->assertFalse($handler->hasWarning(array( + 'message' => 'test', + 'context' => array(), + ))); + + $handler->handle($record); + + $this->assertTrue($handler->hasWarning(array( + 'message' => 'test', + 'context' => array(), + ))); + $this->assertFalse($handler->hasWarning(array( + 'message' => 'test', + 'context' => array( + 'foo' => 'bar' + ), + ))); + } + + public function testHandlerAssertNonEmptyContext() { + $handler = new TestHandler; + $record = $this->getRecord(Logger::WARNING, 'test', array('foo' => 'bar')); + $this->assertFalse($handler->hasWarning(array( + 'message' => 'test', + 'context' => array( + 'foo' => 'bar' + ), + ))); + + $handler->handle($record); + + $this->assertTrue($handler->hasWarning(array( + 'message' => 'test', + 'context' => array( + 'foo' => 'bar' + ), + ))); + $this->assertFalse($handler->hasWarning(array( + 'message' => 'test', + 'context' => array(), + ))); + } + public function methodProvider() { return array( diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/WhatFailureGroupHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/WhatFailureGroupHandlerTest.php index 8d37a1fcc..0594a232b 100644 --- a/vendor/monolog/monolog/tests/Monolog/Handler/WhatFailureGroupHandlerTest.php +++ b/vendor/monolog/monolog/tests/Monolog/Handler/WhatFailureGroupHandlerTest.php @@ -87,6 +87,29 @@ public function testHandleUsesProcessors() $this->assertTrue($records[0]['extra']['foo']); } + /** + * @covers Monolog\Handler\WhatFailureGroupHandler::handleBatch + */ + public function testHandleBatchUsesProcessors() + { + $testHandlers = array(new TestHandler(), new TestHandler()); + $handler = new WhatFailureGroupHandler($testHandlers); + $handler->pushProcessor(function ($record) { + $record['extra']['foo'] = true; + + return $record; + }); + $handler->handleBatch(array($this->getRecord(Logger::DEBUG), $this->getRecord(Logger::INFO))); + foreach ($testHandlers as $test) { + $this->assertTrue($test->hasDebugRecords()); + $this->assertTrue($test->hasInfoRecords()); + $this->assertTrue(count($test->getRecords()) === 2); + $records = $test->getRecords(); + $this->assertTrue($records[0]['extra']['foo']); + $this->assertTrue($records[1]['extra']['foo']); + } + } + /** * @covers Monolog\Handler\WhatFailureGroupHandler::handle */ diff --git a/vendor/monolog/monolog/tests/Monolog/LoggerTest.php b/vendor/monolog/monolog/tests/Monolog/LoggerTest.php index 1ecc34a0a..442e87dea 100644 --- a/vendor/monolog/monolog/tests/Monolog/LoggerTest.php +++ b/vendor/monolog/monolog/tests/Monolog/LoggerTest.php @@ -545,4 +545,146 @@ public function useMicrosecondTimestampsProvider() 'without microseconds' => array(false, PHP_VERSION_ID >= 70100 ? 'assertNotSame' : 'assertSame'), ); } + + /** + * @covers Monolog\Logger::setExceptionHandler + */ + public function testSetExceptionHandler() + { + $logger = new Logger(__METHOD__); + $this->assertNull($logger->getExceptionHandler()); + $callback = function ($ex) { + }; + $logger->setExceptionHandler($callback); + $this->assertEquals($callback, $logger->getExceptionHandler()); + } + + /** + * @covers Monolog\Logger::setExceptionHandler + * @expectedException InvalidArgumentException + */ + public function testBadExceptionHandlerType() + { + $logger = new Logger(__METHOD__); + $logger->setExceptionHandler(false); + } + + /** + * @covers Monolog\Logger::handleException + * @expectedException Exception + */ + public function testDefaultHandleException() + { + $logger = new Logger(__METHOD__); + $handler = $this->getMock('Monolog\Handler\HandlerInterface'); + $handler->expects($this->any()) + ->method('isHandling') + ->will($this->returnValue(true)) + ; + $handler->expects($this->any()) + ->method('handle') + ->will($this->throwException(new \Exception('Some handler exception'))) + ; + $logger->pushHandler($handler); + $logger->info('test'); + } + + /** + * @covers Monolog\Logger::handleException + * @covers Monolog\Logger::addRecord + */ + public function testCustomHandleException() + { + $logger = new Logger(__METHOD__); + $that = $this; + $logger->setExceptionHandler(function ($e, $record) use ($that) { + $that->assertEquals($e->getMessage(), 'Some handler exception'); + $that->assertTrue(is_array($record)); + $that->assertEquals($record['message'], 'test'); + }); + $handler = $this->getMock('Monolog\Handler\HandlerInterface'); + $handler->expects($this->any()) + ->method('isHandling') + ->will($this->returnValue(true)) + ; + $handler->expects($this->any()) + ->method('handle') + ->will($this->throwException(new \Exception('Some handler exception'))) + ; + $logger->pushHandler($handler); + $logger->info('test'); + } + + public function testReset() + { + $logger = new Logger('app'); + + $testHandler = new Handler\TestHandler(); + $bufferHandler = new Handler\BufferHandler($testHandler); + $groupHandler = new Handler\GroupHandler(array($bufferHandler)); + $fingersCrossedHandler = new Handler\FingersCrossedHandler($groupHandler); + + $logger->pushHandler($fingersCrossedHandler); + + $processorUid1 = new Processor\UidProcessor(10); + $uid1 = $processorUid1->getUid(); + $groupHandler->pushProcessor($processorUid1); + + $processorUid2 = new Processor\UidProcessor(5); + $uid2 = $processorUid2->getUid(); + $logger->pushProcessor($processorUid2); + + $getProperty = function ($object, $property) { + $reflectionProperty = new \ReflectionProperty(get_class($object), $property); + $reflectionProperty->setAccessible(true); + + return $reflectionProperty->getValue($object); + }; + $that = $this; + $assertBufferOfBufferHandlerEmpty = function () use ($getProperty, $bufferHandler, $that) { + $that->assertEmpty($getProperty($bufferHandler, 'buffer')); + }; + $assertBuffersEmpty = function() use ($assertBufferOfBufferHandlerEmpty, $getProperty, $fingersCrossedHandler, $that) { + $assertBufferOfBufferHandlerEmpty(); + $that->assertEmpty($getProperty($fingersCrossedHandler, 'buffer')); + }; + + $logger->debug('debug'); + $logger->reset(); + $assertBuffersEmpty(); + $this->assertFalse($testHandler->hasDebugRecords()); + $this->assertFalse($testHandler->hasErrorRecords()); + $this->assertNotSame($uid1, $uid1 = $processorUid1->getUid()); + $this->assertNotSame($uid2, $uid2 = $processorUid2->getUid()); + + $logger->debug('debug'); + $logger->error('error'); + $logger->reset(); + $assertBuffersEmpty(); + $this->assertTrue($testHandler->hasDebugRecords()); + $this->assertTrue($testHandler->hasErrorRecords()); + $this->assertNotSame($uid1, $uid1 = $processorUid1->getUid()); + $this->assertNotSame($uid2, $uid2 = $processorUid2->getUid()); + + $logger->info('info'); + $this->assertNotEmpty($getProperty($fingersCrossedHandler, 'buffer')); + $assertBufferOfBufferHandlerEmpty(); + $this->assertFalse($testHandler->hasInfoRecords()); + + $logger->reset(); + $assertBuffersEmpty(); + $this->assertFalse($testHandler->hasInfoRecords()); + $this->assertNotSame($uid1, $uid1 = $processorUid1->getUid()); + $this->assertNotSame($uid2, $uid2 = $processorUid2->getUid()); + + $logger->notice('notice'); + $logger->emergency('emergency'); + $logger->reset(); + $assertBuffersEmpty(); + $this->assertFalse($testHandler->hasInfoRecords()); + $this->assertTrue($testHandler->hasNoticeRecords()); + $this->assertTrue($testHandler->hasEmergencyRecords()); + $this->assertNotSame($uid1, $processorUid1->getUid()); + $this->assertNotSame($uid2, $processorUid2->getUid()); + } } diff --git a/vendor/monolog/monolog/tests/Monolog/SignalHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/SignalHandlerTest.php new file mode 100644 index 000000000..9fa079290 --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/SignalHandlerTest.php @@ -0,0 +1,287 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog; + +use Monolog\Handler\StreamHandler; +use Monolog\Handler\TestHandler; +use Psr\Log\LogLevel; + +/** + * @author Robert Gust-Bardon + * @covers Monolog\SignalHandler + */ +class SignalHandlerTest extends TestCase +{ + + private $asyncSignalHandling; + private $blockedSignals; + private $signalHandlers; + + protected function setUp() + { + $this->signalHandlers = array(); + if (extension_loaded('pcntl')) { + if (function_exists('pcntl_async_signals')) { + $this->asyncSignalHandling = pcntl_async_signals(); + } + if (function_exists('pcntl_sigprocmask')) { + pcntl_sigprocmask(SIG_BLOCK, array(), $this->blockedSignals); + } + } + } + + protected function tearDown() + { + if ($this->asyncSignalHandling !== null) { + pcntl_async_signals($this->asyncSignalHandling); + } + if ($this->blockedSignals !== null) { + pcntl_sigprocmask(SIG_SETMASK, $this->blockedSignals); + } + if ($this->signalHandlers) { + pcntl_signal_dispatch(); + foreach ($this->signalHandlers as $signo => $handler) { + pcntl_signal($signo, $handler); + } + } + } + + private function setSignalHandler($signo, $handler = SIG_DFL) { + if (function_exists('pcntl_signal_get_handler')) { + $this->signalHandlers[$signo] = pcntl_signal_get_handler($signo); + } else { + $this->signalHandlers[$signo] = SIG_DFL; + } + $this->assertTrue(pcntl_signal($signo, $handler)); + } + + public function testHandleSignal() + { + $logger = new Logger('test', array($handler = new TestHandler)); + $errHandler = new SignalHandler($logger); + $signo = 2; // SIGINT. + $siginfo = array('signo' => $signo, 'errno' => 0, 'code' => 0); + $errHandler->handleSignal($signo, $siginfo); + $this->assertCount(1, $handler->getRecords()); + $this->assertTrue($handler->hasCriticalRecords()); + $records = $handler->getRecords(); + $this->assertSame($siginfo, $records[0]['context']); + } + + /** + * @depends testHandleSignal + * @requires extension pcntl + * @requires extension posix + * @requires function pcntl_signal + * @requires function pcntl_signal_dispatch + * @requires function posix_getpid + * @requires function posix_kill + */ + public function testRegisterSignalHandler() + { + // SIGCONT and SIGURG should be ignored by default. + if (!defined('SIGCONT') || !defined('SIGURG')) { + $this->markTestSkipped('This test requires the SIGCONT and SIGURG pcntl constants.'); + } + + $this->setSignalHandler(SIGCONT, SIG_IGN); + $this->setSignalHandler(SIGURG, SIG_IGN); + + $logger = new Logger('test', array($handler = new TestHandler)); + $errHandler = new SignalHandler($logger); + $pid = posix_getpid(); + + $this->assertTrue(posix_kill($pid, SIGURG)); + $this->assertTrue(pcntl_signal_dispatch()); + $this->assertCount(0, $handler->getRecords()); + + $errHandler->registerSignalHandler(SIGURG, LogLevel::INFO, false, false, false); + + $this->assertTrue(posix_kill($pid, SIGCONT)); + $this->assertTrue(pcntl_signal_dispatch()); + $this->assertCount(0, $handler->getRecords()); + + $this->assertTrue(posix_kill($pid, SIGURG)); + $this->assertTrue(pcntl_signal_dispatch()); + $this->assertCount(1, $handler->getRecords()); + $this->assertTrue($handler->hasInfoThatContains('SIGURG')); + } + + /** + * @dataProvider defaultPreviousProvider + * @depends testRegisterSignalHandler + * @requires function pcntl_fork + * @requires function pcntl_sigprocmask + * @requires function pcntl_waitpid + */ + public function testRegisterDefaultPreviousSignalHandler($signo, $callPrevious, $expected) + { + $this->setSignalHandler($signo, SIG_DFL); + + $path = tempnam(sys_get_temp_dir(), 'monolog-'); + $this->assertNotFalse($path); + + $pid = pcntl_fork(); + if ($pid === 0) { // Child. + $streamHandler = new StreamHandler($path); + $streamHandler->setFormatter($this->getIdentityFormatter()); + $logger = new Logger('test', array($streamHandler)); + $errHandler = new SignalHandler($logger); + $errHandler->registerSignalHandler($signo, LogLevel::INFO, $callPrevious, false, false); + pcntl_sigprocmask(SIG_SETMASK, array(SIGCONT)); + posix_kill(posix_getpid(), $signo); + pcntl_signal_dispatch(); + // If $callPrevious is true, SIGINT should terminate by this line. + pcntl_sigprocmask(SIG_BLOCK, array(), $oldset); + file_put_contents($path, implode(' ', $oldset), FILE_APPEND); + posix_kill(posix_getpid(), $signo); + pcntl_signal_dispatch(); + exit(); + } + + $this->assertNotSame(-1, $pid); + $this->assertNotSame(-1, pcntl_waitpid($pid, $status)); + $this->assertNotSame(-1, $status); + $this->assertSame($expected, file_get_contents($path)); + } + + public function defaultPreviousProvider() + { + if (!defined('SIGCONT') || !defined('SIGINT') || !defined('SIGURG')) { + return array(); + } + + return array( + array(SIGINT, false, 'Program received signal SIGINT'.SIGCONT.'Program received signal SIGINT'), + array(SIGINT, true, 'Program received signal SIGINT'), + array(SIGURG, false, 'Program received signal SIGURG'.SIGCONT.'Program received signal SIGURG'), + array(SIGURG, true, 'Program received signal SIGURG'.SIGCONT.'Program received signal SIGURG'), + ); + } + + /** + * @dataProvider callablePreviousProvider + * @depends testRegisterSignalHandler + * @requires function pcntl_signal_get_handler + */ + public function testRegisterCallablePreviousSignalHandler($callPrevious) + { + $this->setSignalHandler(SIGURG, SIG_IGN); + + $logger = new Logger('test', array($handler = new TestHandler)); + $errHandler = new SignalHandler($logger); + $previousCalled = 0; + pcntl_signal(SIGURG, function ($signo, array $siginfo = null) use (&$previousCalled) { + ++$previousCalled; + }); + $errHandler->registerSignalHandler(SIGURG, LogLevel::INFO, $callPrevious, false, false); + $this->assertTrue(posix_kill(posix_getpid(), SIGURG)); + $this->assertTrue(pcntl_signal_dispatch()); + $this->assertCount(1, $handler->getRecords()); + $this->assertTrue($handler->hasInfoThatContains('SIGURG')); + $this->assertSame($callPrevious ? 1 : 0, $previousCalled); + } + + public function callablePreviousProvider() + { + return array( + array(false), + array(true), + ); + } + + /** + * @dataProvider restartSyscallsProvider + * @depends testRegisterDefaultPreviousSignalHandler + * @requires function pcntl_fork + * @requires function pcntl_waitpid + */ + public function testRegisterSyscallRestartingSignalHandler($restartSyscalls) + { + $this->setSignalHandler(SIGURG, SIG_IGN); + + $parentPid = posix_getpid(); + $microtime = microtime(true); + + $pid = pcntl_fork(); + if ($pid === 0) { // Child. + usleep(100000); + posix_kill($parentPid, SIGURG); + usleep(100000); + exit(); + } + + $this->assertNotSame(-1, $pid); + $logger = new Logger('test', array($handler = new TestHandler)); + $errHandler = new SignalHandler($logger); + $errHandler->registerSignalHandler(SIGURG, LogLevel::INFO, false, $restartSyscalls, false); + if ($restartSyscalls) { + // pcntl_wait is expected to be restarted after the signal handler. + $this->assertNotSame(-1, pcntl_waitpid($pid, $status)); + } else { + // pcntl_wait is expected to be interrupted when the signal handler is invoked. + $this->assertSame(-1, pcntl_waitpid($pid, $status)); + } + $this->assertSame($restartSyscalls, microtime(true) - $microtime > 0.15); + $this->assertTrue(pcntl_signal_dispatch()); + $this->assertCount(1, $handler->getRecords()); + if ($restartSyscalls) { + // The child has already exited. + $this->assertSame(-1, pcntl_waitpid($pid, $status)); + } else { + // The child has not exited yet. + $this->assertNotSame(-1, pcntl_waitpid($pid, $status)); + } + } + + public function restartSyscallsProvider() + { + return array( + array(false), + array(true), + array(false), + array(true), + ); + } + + /** + * @dataProvider asyncProvider + * @depends testRegisterDefaultPreviousSignalHandler + * @requires function pcntl_async_signals + */ + public function testRegisterAsyncSignalHandler($initialAsync, $desiredAsync, $expectedBefore, $expectedAfter) + { + $this->setSignalHandler(SIGURG, SIG_IGN); + pcntl_async_signals($initialAsync); + + $logger = new Logger('test', array($handler = new TestHandler)); + $errHandler = new SignalHandler($logger); + $errHandler->registerSignalHandler(SIGURG, LogLevel::INFO, false, false, $desiredAsync); + $this->assertTrue(posix_kill(posix_getpid(), SIGURG)); + $this->assertCount($expectedBefore, $handler->getRecords()); + $this->assertTrue(pcntl_signal_dispatch()); + $this->assertCount($expectedAfter, $handler->getRecords()); + } + + public function asyncProvider() + { + return array( + array(false, false, 0, 1), + array(false, null, 0, 1), + array(false, true, 1, 1), + array(true, false, 0, 1), + array(true, null, 1, 1), + array(true, true, 1, 1), + ); + } + +} diff --git a/vendor/nesbot/carbon/LICENSE b/vendor/nesbot/carbon/LICENSE new file mode 100644 index 000000000..6de45ebf8 --- /dev/null +++ b/vendor/nesbot/carbon/LICENSE @@ -0,0 +1,19 @@ +Copyright (C) Brian Nesbitt + +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/nesbot/carbon/composer.json b/vendor/nesbot/carbon/composer.json new file mode 100644 index 000000000..40be3f55f --- /dev/null +++ b/vendor/nesbot/carbon/composer.json @@ -0,0 +1,60 @@ +{ + "name": "nesbot/carbon", + "type": "library", + "description": "A simple API extension for DateTime.", + "keywords": [ + "date", + "time", + "DateTime" + ], + "homepage": "http://carbon.nesbot.com", + "support": { + "issues": "https://github.com/briannesbitt/Carbon/issues", + "source": "https://github.com/briannesbitt/Carbon" + }, + "license": "MIT", + "authors": [ + { + "name": "Brian Nesbitt", + "email": "brian@nesbot.com", + "homepage": "http://nesbot.com" + } + ], + "require": { + "php": ">=5.3.9", + "symfony/translation": "~2.6 || ~3.0 || ~4.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "~2", + "phpunit/phpunit": "^4.8.35 || ^5.7" + }, + "autoload": { + "psr-4": { + "": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Tests\\": "tests/" + } + }, + "config": { + "sort-packages": true + }, + "scripts": { + "test": [ + "@phpunit", + "@phpcs" + ], + "phpunit": "phpunit --verbose --coverage-clover=coverage.xml", + "phpcs": "php-cs-fixer fix -v --diff --dry-run", + "phpstan": "phpstan analyse --configuration phpstan.neon --level 3 src tests" + }, + "extra": { + "laravel": { + "providers": [ + "Carbon\\Laravel\\ServiceProvider" + ] + } + } +} diff --git a/vendor/nesbot/carbon/readme.md b/vendor/nesbot/carbon/readme.md new file mode 100644 index 000000000..5e9d1ccbc --- /dev/null +++ b/vendor/nesbot/carbon/readme.md @@ -0,0 +1,94 @@ +# Carbon + +[![Latest Stable Version](https://poser.pugx.org/nesbot/carbon/v/stable.png)](https://packagist.org/packages/nesbot/carbon) +[![Total Downloads](https://poser.pugx.org/nesbot/carbon/downloads.png)](https://packagist.org/packages/nesbot/carbon) +[![Build Status](https://travis-ci.org/briannesbitt/Carbon.svg?branch=master)](https://travis-ci.org/briannesbitt/Carbon) +[![StyleCI](https://styleci.io/repos/5724990/shield?style=flat)](https://styleci.io/repos/5724990) +[![codecov.io](https://codecov.io/github/briannesbitt/Carbon/coverage.svg?branch=master)](https://codecov.io/github/briannesbitt/Carbon?branch=master) +[![PHP-Eye](https://php-eye.com/badge/nesbot/carbon/tested.svg?style=flat)](https://php-eye.com/package/nesbot/carbon) +[![PHPStan](https://img.shields.io/badge/PHPStan-enabled-brightgreen.svg?style=flat)](https://github.com/phpstan/phpstan) + +A simple PHP API extension for DateTime. [http://carbon.nesbot.com](http://carbon.nesbot.com) + +```php +use Carbon\Carbon; + +printf("Right now is %s", Carbon::now()->toDateTimeString()); +printf("Right now in Vancouver is %s", Carbon::now('America/Vancouver')); //implicit __toString() +$tomorrow = Carbon::now()->addDay(); +$lastWeek = Carbon::now()->subWeek(); +$nextSummerOlympics = Carbon::createFromDate(2016)->addYears(4); + +$officialDate = Carbon::now()->toRfc2822String(); + +$howOldAmI = Carbon::createFromDate(1975, 5, 21)->age; + +$noonTodayLondonTime = Carbon::createFromTime(12, 0, 0, 'Europe/London'); + +$internetWillBlowUpOn = Carbon::create(2038, 01, 19, 3, 14, 7, 'GMT'); + +// Don't really want this to happen so mock now +Carbon::setTestNow(Carbon::createFromDate(2000, 1, 1)); + +// comparisons are always done in UTC +if (Carbon::now()->gte($internetWillBlowUpOn)) { + die(); +} + +// Phew! Return to normal behaviour +Carbon::setTestNow(); + +if (Carbon::now()->isWeekend()) { + echo 'Party!'; +} +echo Carbon::now()->subMinutes(2)->diffForHumans(); // '2 minutes ago' + +// ... but also does 'from now', 'after' and 'before' +// rolling up to seconds, minutes, hours, days, months, years + +$daysSinceEpoch = Carbon::createFromTimestamp(0)->diffInDays(); +``` + +## Installation + +### With Composer + +``` +$ composer require nesbot/carbon +``` + +```json +{ + "require": { + "nesbot/carbon": "~1.21" + } +} +``` + +```php + + +### Without Composer + +Why are you not using [composer](http://getcomposer.org/)? Download [Carbon.php](https://github.com/briannesbitt/Carbon/blob/master/src/Carbon/Carbon.php) from the repo and save the file into your project path somewhere. + +```php + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon; + +use Carbon\Exceptions\InvalidDateException; +use Closure; +use DatePeriod; +use DateTime; +use DateTimeInterface; +use DateTimeZone; +use InvalidArgumentException; +use JsonSerializable; +use Symfony\Component\Translation\TranslatorInterface; + +/** + * A simple API extension for DateTime + * + * @property int $year + * @property int $yearIso + * @property int $month + * @property int $day + * @property int $hour + * @property int $minute + * @property int $second + * @property int $timestamp seconds since the Unix Epoch + * @property \DateTimeZone $timezone the current timezone + * @property \DateTimeZone $tz alias of timezone + * @property-read int $micro + * @property-read int $dayOfWeek 0 (for Sunday) through 6 (for Saturday) + * @property-read int $dayOfWeekIso 1 (for Monday) through 7 (for Sunday) + * @property-read int $dayOfYear 0 through 365 + * @property-read int $weekOfMonth 1 through 5 + * @property-read int $weekNumberInMonth 1 through 5 + * @property-read int $weekOfYear ISO-8601 week number of year, weeks starting on Monday + * @property-read int $daysInMonth number of days in the given month + * @property-read int $age does a diffInYears() with default parameters + * @property-read int $quarter the quarter of this instance, 1 - 4 + * @property-read int $offset the timezone offset in seconds from UTC + * @property-read int $offsetHours the timezone offset in hours from UTC + * @property-read bool $dst daylight savings time indicator, true if DST, false otherwise + * @property-read bool $local checks if the timezone is local, true if local, false otherwise + * @property-read bool $utc checks if the timezone is UTC, true if UTC, false otherwise + * @property-read string $timezoneName + * @property-read string $tzName + * @property-read string $englishDayOfWeek the day of week in English + * @property-read string $shortEnglishDayOfWeek the abbreviated day of week in English + * @property-read string $englishMonth the day of week in English + * @property-read string $shortEnglishMonth the abbreviated day of week in English + * @property-read string $localeDayOfWeek the day of week in current locale LC_TIME + * @property-read string $shortLocaleDayOfWeek the abbreviated day of week in current locale LC_TIME + * @property-read string $localeMonth the month in current locale LC_TIME + * @property-read string $shortLocaleMonth the abbreviated month in current locale LC_TIME + */ +class Carbon extends DateTime implements JsonSerializable +{ + const NO_ZERO_DIFF = 01; + const JUST_NOW = 02; + const ONE_DAY_WORDS = 04; + const TWO_DAY_WORDS = 010; + + /** + * The day constants. + */ + const SUNDAY = 0; + const MONDAY = 1; + const TUESDAY = 2; + const WEDNESDAY = 3; + const THURSDAY = 4; + const FRIDAY = 5; + const SATURDAY = 6; + + /** + * Names of days of the week. + * + * @var array + */ + protected static $days = array( + self::SUNDAY => 'Sunday', + self::MONDAY => 'Monday', + self::TUESDAY => 'Tuesday', + self::WEDNESDAY => 'Wednesday', + self::THURSDAY => 'Thursday', + self::FRIDAY => 'Friday', + self::SATURDAY => 'Saturday', + ); + + /** + * Number of X in Y. + */ + const YEARS_PER_CENTURY = 100; + const YEARS_PER_DECADE = 10; + const MONTHS_PER_YEAR = 12; + const MONTHS_PER_QUARTER = 3; + const WEEKS_PER_YEAR = 52; + const WEEKS_PER_MONTH = 4; + const DAYS_PER_WEEK = 7; + const HOURS_PER_DAY = 24; + const MINUTES_PER_HOUR = 60; + const SECONDS_PER_MINUTE = 60; + + /** + * RFC7231 DateTime format. + * + * @var string + */ + const RFC7231_FORMAT = 'D, d M Y H:i:s \G\M\T'; + + /** + * Default format to use for __toString method when type juggling occurs. + * + * @var string + */ + const DEFAULT_TO_STRING_FORMAT = 'Y-m-d H:i:s'; + + /** + * Format for converting mocked time, includes microseconds. + * + * @var string + */ + const MOCK_DATETIME_FORMAT = 'Y-m-d H:i:s.u'; + + /** + * Customizable PHP_INT_SIZE override. + * + * @var int + */ + public static $PHPIntSize = PHP_INT_SIZE; + + /** + * Format to use for __toString method when type juggling occurs. + * + * @var string + */ + protected static $toStringFormat = self::DEFAULT_TO_STRING_FORMAT; + + /** + * First day of week. + * + * @var int + */ + protected static $weekStartsAt = self::MONDAY; + + /** + * Last day of week. + * + * @var int + */ + protected static $weekEndsAt = self::SUNDAY; + + /** + * Days of weekend. + * + * @var array + */ + protected static $weekendDays = array( + self::SATURDAY, + self::SUNDAY, + ); + + /** + * Midday/noon hour. + * + * @var int + */ + protected static $midDayAt = 12; + + /** + * Format regex patterns. + * + * @var array + */ + protected static $regexFormats = array( + 'd' => '(3[01]|[12][0-9]|0[1-9])', + 'D' => '([a-zA-Z]{3})', + 'j' => '([123][0-9]|[1-9])', + 'l' => '([a-zA-Z]{2,})', + 'N' => '([1-7])', + 'S' => '([a-zA-Z]{2})', + 'w' => '([0-6])', + 'z' => '(36[0-5]|3[0-5][0-9]|[12][0-9]{2}|[1-9]?[0-9])', + 'W' => '(5[012]|[1-4][0-9]|[1-9])', + 'F' => '([a-zA-Z]{2,})', + 'm' => '(1[012]|0[1-9])', + 'M' => '([a-zA-Z]{3})', + 'n' => '(1[012]|[1-9])', + 't' => '(2[89]|3[01])', + 'L' => '(0|1)', + 'o' => '([1-9][0-9]{0,4})', + 'Y' => '([1-9][0-9]{0,4})', + 'y' => '([0-9]{2})', + 'a' => '(am|pm)', + 'A' => '(AM|PM)', + 'B' => '([0-9]{3})', + 'g' => '(1[012]|[1-9])', + 'G' => '(2[0-3]|1?[0-9])', + 'h' => '(1[012]|0[1-9])', + 'H' => '(2[0-3]|[01][0-9])', + 'i' => '([0-5][0-9])', + 's' => '([0-5][0-9])', + 'u' => '([0-9]{1,6})', + 'v' => '([0-9]{1,3})', + 'e' => '([a-zA-Z]{1,5})|([a-zA-Z]*\/[a-zA-Z]*)', + 'I' => '(0|1)', + 'O' => '([\+\-](1[012]|0[0-9])[0134][05])', + 'P' => '([\+\-](1[012]|0[0-9]):[0134][05])', + 'T' => '([a-zA-Z]{1,5})', + 'Z' => '(-?[1-5]?[0-9]{1,4})', + 'U' => '([0-9]*)', + + // The formats below are combinations of the above formats. + 'c' => '(([1-9][0-9]{0,4})\-(1[012]|0[1-9])\-(3[01]|[12][0-9]|0[1-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])[\+\-](1[012]|0[0-9]):([0134][05]))', // Y-m-dTH:i:sP + 'r' => '(([a-zA-Z]{3}), ([123][0-9]|[1-9]) ([a-zA-Z]{3}) ([1-9][0-9]{0,4}) (2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9]) [\+\-](1[012]|0[0-9])([0134][05]))', // D, j M Y H:i:s O + ); + + /** + * A test Carbon instance to be returned when now instances are created. + * + * @var \Carbon\Carbon + */ + protected static $testNow; + + /** + * A translator to ... er ... translate stuff. + * + * @var \Symfony\Component\Translation\TranslatorInterface + */ + protected static $translator; + + /** + * The errors that can occur. + * + * @var array + */ + protected static $lastErrors; + + /** + * The custom Carbon JSON serializer. + * + * @var callable|null + */ + protected static $serializer; + + /** + * The registered string macros. + * + * @var array + */ + protected static $localMacros = array(); + + /** + * Will UTF8 encoding be used to print localized date/time ? + * + * @var bool + */ + protected static $utf8 = false; + + /** + * Add microseconds to now on PHP < 7.1 and 7.1.3. true by default. + * + * @var bool + */ + protected static $microsecondsFallback = true; + + /** + * Indicates if months should be calculated with overflow. + * + * @var bool + */ + protected static $monthsOverflow = true; + + /** + * Indicates if years should be calculated with overflow. + * + * @var bool + */ + protected static $yearsOverflow = true; + + /** + * Indicates if years are compared with month by default so isSameMonth and isSameQuarter have $ofSameYear set + * to true by default. + * + * @var bool + */ + protected static $compareYearWithMonth = false; + + /** + * Options for diffForHumans(). + * + * @var int + */ + protected static $humanDiffOptions = self::NO_ZERO_DIFF; + + /** + * @param int $humanDiffOptions + */ + public static function setHumanDiffOptions($humanDiffOptions) + { + static::$humanDiffOptions = $humanDiffOptions; + } + + /** + * @param int $humanDiffOption + */ + public static function enableHumanDiffOption($humanDiffOption) + { + static::$humanDiffOptions = static::getHumanDiffOptions() | $humanDiffOption; + } + + /** + * @param int $humanDiffOption + */ + public static function disableHumanDiffOption($humanDiffOption) + { + static::$humanDiffOptions = static::getHumanDiffOptions() & ~$humanDiffOption; + } + + /** + * @return int + */ + public static function getHumanDiffOptions() + { + return static::$humanDiffOptions; + } + + /** + * Add microseconds to now on PHP < 7.1 and 7.1.3 if set to true, + * let microseconds to 0 on those PHP versions if false. + * + * @param bool $microsecondsFallback + */ + public static function useMicrosecondsFallback($microsecondsFallback = true) + { + static::$microsecondsFallback = $microsecondsFallback; + } + + /** + * Return true if microseconds fallback on PHP < 7.1 and 7.1.3 is + * enabled. false if disabled. + * + * @return bool + */ + public static function isMicrosecondsFallbackEnabled() + { + return static::$microsecondsFallback; + } + + /** + * Indicates if months should be calculated with overflow. + * + * @param bool $monthsOverflow + * + * @return void + */ + public static function useMonthsOverflow($monthsOverflow = true) + { + static::$monthsOverflow = $monthsOverflow; + } + + /** + * Reset the month overflow behavior. + * + * @return void + */ + public static function resetMonthsOverflow() + { + static::$monthsOverflow = true; + } + + /** + * Get the month overflow behavior. + * + * @return bool + */ + public static function shouldOverflowMonths() + { + return static::$monthsOverflow; + } + + /** + * Indicates if years should be calculated with overflow. + * + * @param bool $yearsOverflow + * + * @return void + */ + public static function useYearsOverflow($yearsOverflow = true) + { + static::$yearsOverflow = $yearsOverflow; + } + + /** + * Reset the month overflow behavior. + * + * @return void + */ + public static function resetYearsOverflow() + { + static::$yearsOverflow = true; + } + + /** + * Get the month overflow behavior. + * + * @return bool + */ + public static function shouldOverflowYears() + { + return static::$yearsOverflow; + } + + /** + * Get the month comparison default behavior. + * + * @return bool + */ + public static function compareYearWithMonth($compareYearWithMonth = true) + { + static::$compareYearWithMonth = $compareYearWithMonth; + } + + /** + * Get the month comparison default behavior. + * + * @return bool + */ + public static function shouldCompareYearWithMonth() + { + return static::$compareYearWithMonth; + } + + /** + * Creates a DateTimeZone from a string, DateTimeZone or integer offset. + * + * @param \DateTimeZone|string|int|null $object + * + * @throws \InvalidArgumentException + * + * @return \DateTimeZone + */ + protected static function safeCreateDateTimeZone($object) + { + if ($object === null) { + // Don't return null... avoid Bug #52063 in PHP <5.3.6 + return new DateTimeZone(date_default_timezone_get()); + } + + if ($object instanceof DateTimeZone) { + return $object; + } + + if (is_numeric($object)) { + $tzName = timezone_name_from_abbr(null, $object * 3600, true); + + if ($tzName === false) { + throw new InvalidArgumentException('Unknown or bad timezone ('.$object.')'); + } + + $object = $tzName; + } + + $tz = @timezone_open($object = (string) $object); + + if ($tz !== false) { + return $tz; + } + + // Work-around for a bug fixed in PHP 5.5.10 https://bugs.php.net/bug.php?id=45528 + // See: https://stackoverflow.com/q/14068594/2646927 + // @codeCoverageIgnoreStart + if (strpos($object, ':') !== false) { + try { + return static::createFromFormat('O', $object)->getTimezone(); + } catch (InvalidArgumentException $e) { + // + } + } + // @codeCoverageIgnoreEnd + + throw new InvalidArgumentException('Unknown or bad timezone ('.$object.')'); + } + + /////////////////////////////////////////////////////////////////// + //////////////////////////// CONSTRUCTORS ///////////////////////// + /////////////////////////////////////////////////////////////////// + + /** + * Create a new Carbon instance. + * + * Please see the testing aids section (specifically static::setTestNow()) + * for more on the possibility of this constructor returning a test instance. + * + * @param string|null $time + * @param \DateTimeZone|string|null $tz + */ + public function __construct($time = null, $tz = null) + { + // If the class has a test now set and we are trying to create a now() + // instance then override as required + $isNow = empty($time) || $time === 'now'; + if (static::hasTestNow() && ($isNow || static::hasRelativeKeywords($time))) { + $testInstance = clone static::getTestNow(); + + //shift the time according to the given time zone + if ($tz !== null && $tz !== static::getTestNow()->getTimezone()) { + $testInstance->setTimezone($tz); + } else { + $tz = $testInstance->getTimezone(); + } + + if (static::hasRelativeKeywords($time)) { + $testInstance->modify($time); + } + + $time = $testInstance->format(static::MOCK_DATETIME_FORMAT); + } + + $timezone = static::safeCreateDateTimeZone($tz); + // @codeCoverageIgnoreStart + if ($isNow && !isset($testInstance) && static::isMicrosecondsFallbackEnabled() && ( + version_compare(PHP_VERSION, '7.1.0-dev', '<') + || + version_compare(PHP_VERSION, '7.1.3-dev', '>=') && version_compare(PHP_VERSION, '7.1.4-dev', '<') + ) + ) { + // Get microseconds from microtime() if "now" asked and PHP < 7.1 and PHP 7.1.3 if fallback enabled. + list($microTime, $timeStamp) = explode(' ', microtime()); + $dateTime = new DateTime('now', $timezone); + $dateTime->setTimestamp($timeStamp); // Use the timestamp returned by microtime as now can happen in the next second + $time = $dateTime->format(static::DEFAULT_TO_STRING_FORMAT).substr($microTime, 1, 7); + } + // @codeCoverageIgnoreEnd + + // Work-around for PHP bug https://bugs.php.net/bug.php?id=67127 + if (strpos((string) .1, '.') === false) { + $locale = setlocale(LC_NUMERIC, '0'); + setlocale(LC_NUMERIC, 'C'); + } + parent::__construct($time, $timezone); + if (isset($locale)) { + setlocale(LC_NUMERIC, $locale); + } + static::setLastErrors(parent::getLastErrors()); + } + + /** + * Create a Carbon instance from a DateTime one. + * + * @param \DateTime|\DateTimeInterface $date + * + * @return static + */ + public static function instance($date) + { + if ($date instanceof static) { + return clone $date; + } + + static::expectDateTime($date); + + return new static($date->format('Y-m-d H:i:s.u'), $date->getTimezone()); + } + + /** + * Create a carbon instance from a string. + * + * This is an alias for the constructor that allows better fluent syntax + * as it allows you to do Carbon::parse('Monday next week')->fn() rather + * than (new Carbon('Monday next week'))->fn(). + * + * @param string|null $time + * @param \DateTimeZone|string|null $tz + * + * @return static + */ + public static function parse($time = null, $tz = null) + { + return new static($time, $tz); + } + + /** + * Get a Carbon instance for the current date and time. + * + * @param \DateTimeZone|string|null $tz + * + * @return static + */ + public static function now($tz = null) + { + return new static(null, $tz); + } + + /** + * Create a Carbon instance for today. + * + * @param \DateTimeZone|string|null $tz + * + * @return static + */ + public static function today($tz = null) + { + return static::parse('today', $tz); + } + + /** + * Create a Carbon instance for tomorrow. + * + * @param \DateTimeZone|string|null $tz + * + * @return static + */ + public static function tomorrow($tz = null) + { + return static::parse('tomorrow', $tz); + } + + /** + * Create a Carbon instance for yesterday. + * + * @param \DateTimeZone|string|null $tz + * + * @return static + */ + public static function yesterday($tz = null) + { + return static::parse('yesterday', $tz); + } + + /** + * Create a Carbon instance for the greatest supported date. + * + * @return static + */ + public static function maxValue() + { + if (self::$PHPIntSize === 4) { + // 32 bit + return static::createFromTimestamp(PHP_INT_MAX); // @codeCoverageIgnore + } + + // 64 bit + return static::create(9999, 12, 31, 23, 59, 59); + } + + /** + * Create a Carbon instance for the lowest supported date. + * + * @return static + */ + public static function minValue() + { + if (self::$PHPIntSize === 4) { + // 32 bit + return static::createFromTimestamp(~PHP_INT_MAX); // @codeCoverageIgnore + } + + // 64 bit + return static::create(1, 1, 1, 0, 0, 0); + } + + /** + * Create a new Carbon instance from a specific date and time. + * + * If any of $year, $month or $day are set to null their now() values will + * be used. + * + * If $hour is null it will be set to its now() value and the default + * values for $minute and $second will be their now() values. + * + * If $hour is not null then the default values for $minute and $second + * will be 0. + * + * @param int|null $year + * @param int|null $month + * @param int|null $day + * @param int|null $hour + * @param int|null $minute + * @param int|null $second + * @param \DateTimeZone|string|null $tz + * + * @throws \InvalidArgumentException + * + * @return static + */ + public static function create($year = null, $month = null, $day = null, $hour = null, $minute = null, $second = null, $tz = null) + { + $now = static::hasTestNow() ? static::getTestNow() : static::now($tz); + + $defaults = array_combine(array( + 'year', + 'month', + 'day', + 'hour', + 'minute', + 'second', + ), explode('-', $now->format('Y-n-j-G-i-s'))); + + $year = $year === null ? $defaults['year'] : $year; + $month = $month === null ? $defaults['month'] : $month; + $day = $day === null ? $defaults['day'] : $day; + + if ($hour === null) { + $hour = $defaults['hour']; + $minute = $minute === null ? $defaults['minute'] : $minute; + $second = $second === null ? $defaults['second'] : $second; + } else { + $minute = $minute === null ? 0 : $minute; + $second = $second === null ? 0 : $second; + } + + $fixYear = null; + + if ($year < 0) { + $fixYear = $year; + $year = 0; + } elseif ($year > 9999) { + $fixYear = $year - 9999; + $year = 9999; + } + + $instance = static::createFromFormat('!Y-n-j G:i:s', sprintf('%s-%s-%s %s:%02s:%02s', $year, $month, $day, $hour, $minute, $second), $tz); + + if ($fixYear !== null) { + $instance->addYears($fixYear); + } + + return $instance; + } + + /** + * Create a new safe Carbon instance from a specific date and time. + * + * If any of $year, $month or $day are set to null their now() values will + * be used. + * + * If $hour is null it will be set to its now() value and the default + * values for $minute and $second will be their now() values. + * + * If $hour is not null then the default values for $minute and $second + * will be 0. + * + * If one of the set values is not valid, an \InvalidArgumentException + * will be thrown. + * + * @param int|null $year + * @param int|null $month + * @param int|null $day + * @param int|null $hour + * @param int|null $minute + * @param int|null $second + * @param \DateTimeZone|string|null $tz + * + * @throws \Carbon\Exceptions\InvalidDateException|\InvalidArgumentException + * + * @return static + */ + public static function createSafe($year = null, $month = null, $day = null, $hour = null, $minute = null, $second = null, $tz = null) + { + $fields = array( + 'year' => array(0, 9999), + 'month' => array(0, 12), + 'day' => array(0, 31), + 'hour' => array(0, 24), + 'minute' => array(0, 59), + 'second' => array(0, 59), + ); + + foreach ($fields as $field => $range) { + if ($$field !== null && (!is_int($$field) || $$field < $range[0] || $$field > $range[1])) { + throw new InvalidDateException($field, $$field); + } + } + + $instance = static::create($year, $month, $day, $hour, $minute, $second, $tz); + + foreach (array_reverse($fields) as $field => $range) { + if ($$field !== null && (!is_int($$field) || $$field !== $instance->$field)) { + throw new InvalidDateException($field, $$field); + } + } + + return $instance; + } + + /** + * Create a Carbon instance from just a date. The time portion is set to now. + * + * @param int|null $year + * @param int|null $month + * @param int|null $day + * @param \DateTimeZone|string|null $tz + * + * @throws \InvalidArgumentException + * + * @return static + */ + public static function createFromDate($year = null, $month = null, $day = null, $tz = null) + { + return static::create($year, $month, $day, null, null, null, $tz); + } + + /** + * Create a Carbon instance from just a date. The time portion is set to midnight. + * + * @param int|null $year + * @param int|null $month + * @param int|null $day + * @param \DateTimeZone|string|null $tz + * + * @return static + */ + public static function createMidnightDate($year = null, $month = null, $day = null, $tz = null) + { + return static::create($year, $month, $day, 0, 0, 0, $tz); + } + + /** + * Create a Carbon instance from just a time. The date portion is set to today. + * + * @param int|null $hour + * @param int|null $minute + * @param int|null $second + * @param \DateTimeZone|string|null $tz + * + * @throws \InvalidArgumentException + * + * @return static + */ + public static function createFromTime($hour = null, $minute = null, $second = null, $tz = null) + { + return static::create(null, null, null, $hour, $minute, $second, $tz); + } + + /** + * Create a Carbon instance from a time string. The date portion is set to today. + * + * @param string $time + * @param \DateTimeZone|string|null $tz + * + * @throws \InvalidArgumentException + * + * @return static + */ + public static function createFromTimeString($time, $tz = null) + { + return static::today($tz)->setTimeFromTimeString($time); + } + + private static function createFromFormatAndTimezone($format, $time, $tz) + { + return $tz !== null + ? parent::createFromFormat($format, $time, static::safeCreateDateTimeZone($tz)) + : parent::createFromFormat($format, $time); + } + + /** + * Create a Carbon instance from a specific format. + * + * @param string $format Datetime format + * @param string $time + * @param \DateTimeZone|string|null $tz + * + * @throws InvalidArgumentException + * + * @return static + */ + public static function createFromFormat($format, $time, $tz = null) + { + // First attempt to create an instance, so that error messages are based on the unmodified format. + $date = self::createFromFormatAndTimezone($format, $time, $tz); + $lastErrors = parent::getLastErrors(); + + if (($mock = static::getTestNow()) && ($date instanceof DateTime || $date instanceof DateTimeInterface)) { + // Set timezone from mock if custom timezone was neither given directly nor as a part of format. + // First let's skip the part that will be ignored by the parser. + $nonEscaped = '(?getTimezone(); + } + + // Prepend mock datetime only if the format does not contain non escaped unix epoch reset flag. + if (!preg_match("/{$nonEscaped}[!|]/", $format)) { + $format = static::MOCK_DATETIME_FORMAT.' '.$format; + $time = $mock->format(static::MOCK_DATETIME_FORMAT).' '.$time; + } + + // Regenerate date from the modified format to base result on the mocked instance instead of now. + $date = self::createFromFormatAndTimezone($format, $time, $tz); + } + + if ($date instanceof DateTime || $date instanceof DateTimeInterface) { + $instance = static::instance($date); + $instance::setLastErrors($lastErrors); + + return $instance; + } + + throw new InvalidArgumentException(implode(PHP_EOL, $lastErrors['errors'])); + } + + /** + * Set last errors. + * + * @param array $lastErrors + * + * @return void + */ + private static function setLastErrors(array $lastErrors) + { + static::$lastErrors = $lastErrors; + } + + /** + * {@inheritdoc} + */ + public static function getLastErrors() + { + return static::$lastErrors; + } + + /** + * Create a Carbon instance from a timestamp. + * + * @param int $timestamp + * @param \DateTimeZone|string|null $tz + * + * @return static + */ + public static function createFromTimestamp($timestamp, $tz = null) + { + return static::today($tz)->setTimestamp($timestamp); + } + + /** + * Create a Carbon instance from a timestamp in milliseconds. + * + * @param int $timestamp + * @param \DateTimeZone|string|null $tz + * + * @return static + */ + public static function createFromTimestampMs($timestamp, $tz = null) + { + return static::createFromFormat('U.u', sprintf('%F', $timestamp / 1000)) + ->setTimezone($tz); + } + + /** + * Create a Carbon instance from an UTC timestamp. + * + * @param int $timestamp + * + * @return static + */ + public static function createFromTimestampUTC($timestamp) + { + return new static('@'.$timestamp); + } + + /** + * Make a Carbon instance from given variable if possible. + * + * Always return a new instance. Parse only strings and only these likely to be dates (skip intervals + * and recurrences). Throw an exception for invalid format, but otherwise return null. + * + * @param mixed $var + * + * @return static|null + */ + public static function make($var) + { + if ($var instanceof DateTime || $var instanceof DateTimeInterface) { + return static::instance($var); + } + + if (is_string($var)) { + $var = trim($var); + $first = substr($var, 0, 1); + + if (is_string($var) && $first !== 'P' && $first !== 'R' && preg_match('/[a-z0-9]/i', $var)) { + return static::parse($var); + } + } + } + + /** + * Get a copy of the instance. + * + * @return static + */ + public function copy() + { + return clone $this; + } + + /** + * Returns a present instance in the same timezone. + * + * @return static + */ + public function nowWithSameTz() + { + return static::now($this->getTimezone()); + } + + /** + * Throws an exception if the given object is not a DateTime and does not implement DateTimeInterface + * and not in $other. + * + * @param mixed $date + * @param string|array $other + * + * @throws \InvalidArgumentException + */ + protected static function expectDateTime($date, $other = array()) + { + $message = 'Expected '; + foreach ((array) $other as $expect) { + $message .= "{$expect}, "; + } + + if (!$date instanceof DateTime && !$date instanceof DateTimeInterface) { + throw new InvalidArgumentException( + $message.'DateTime or DateTimeInterface, '. + (is_object($date) ? get_class($date) : gettype($date)).' given' + ); + } + } + + /** + * Return the Carbon instance passed through, a now instance in the same timezone + * if null given or parse the input if string given. + * + * @param \Carbon\Carbon|\DateTimeInterface|string|null $date + * + * @return static + */ + protected function resolveCarbon($date = null) + { + if (!$date) { + return $this->nowWithSameTz(); + } + + if (is_string($date)) { + return static::parse($date, $this->getTimezone()); + } + + static::expectDateTime($date, array('null', 'string')); + + return $date instanceof self ? $date : static::instance($date); + } + + /////////////////////////////////////////////////////////////////// + ///////////////////////// GETTERS AND SETTERS ///////////////////// + /////////////////////////////////////////////////////////////////// + + /** + * Get a part of the Carbon object + * + * @param string $name + * + * @throws \InvalidArgumentException + * + * @return string|int|bool|\DateTimeZone + */ + public function __get($name) + { + static $formats = array( + 'year' => 'Y', + 'yearIso' => 'o', + 'month' => 'n', + 'day' => 'j', + 'hour' => 'G', + 'minute' => 'i', + 'second' => 's', + 'micro' => 'u', + 'dayOfWeek' => 'w', + 'dayOfWeekIso' => 'N', + 'dayOfYear' => 'z', + 'weekOfYear' => 'W', + 'daysInMonth' => 't', + 'timestamp' => 'U', + 'englishDayOfWeek' => 'l', + 'shortEnglishDayOfWeek' => 'D', + 'englishMonth' => 'F', + 'shortEnglishMonth' => 'M', + 'localeDayOfWeek' => '%A', + 'shortLocaleDayOfWeek' => '%a', + 'localeMonth' => '%B', + 'shortLocaleMonth' => '%b', + ); + + switch (true) { + case isset($formats[$name]): + $format = $formats[$name]; + $method = substr($format, 0, 1) === '%' ? 'formatLocalized' : 'format'; + $value = $this->$method($format); + + return is_numeric($value) ? (int) $value : $value; + + case $name === 'weekOfMonth': + return (int) ceil($this->day / static::DAYS_PER_WEEK); + + case $name === 'weekNumberInMonth': + return (int) ceil(($this->day + $this->copy()->startOfMonth()->dayOfWeek - 1) / static::DAYS_PER_WEEK); + + case $name === 'age': + return $this->diffInYears(); + + case $name === 'quarter': + return (int) ceil($this->month / static::MONTHS_PER_QUARTER); + + case $name === 'offset': + return $this->getOffset(); + + case $name === 'offsetHours': + return $this->getOffset() / static::SECONDS_PER_MINUTE / static::MINUTES_PER_HOUR; + + case $name === 'dst': + return $this->format('I') === '1'; + + case $name === 'local': + return $this->getOffset() === $this->copy()->setTimezone(date_default_timezone_get())->getOffset(); + + case $name === 'utc': + return $this->getOffset() === 0; + + case $name === 'timezone' || $name === 'tz': + return $this->getTimezone(); + + case $name === 'timezoneName' || $name === 'tzName': + return $this->getTimezone()->getName(); + + default: + throw new InvalidArgumentException(sprintf("Unknown getter '%s'", $name)); + } + } + + /** + * Check if an attribute exists on the object + * + * @param string $name + * + * @return bool + */ + public function __isset($name) + { + try { + $this->__get($name); + } catch (InvalidArgumentException $e) { + return false; + } + + return true; + } + + /** + * Set a part of the Carbon object + * + * @param string $name + * @param string|int|\DateTimeZone $value + * + * @throws \InvalidArgumentException + * + * @return void + */ + public function __set($name, $value) + { + switch ($name) { + case 'year': + case 'month': + case 'day': + case 'hour': + case 'minute': + case 'second': + list($year, $month, $day, $hour, $minute, $second) = explode('-', $this->format('Y-n-j-G-i-s')); + $$name = $value; + $this->setDateTime($year, $month, $day, $hour, $minute, $second); + break; + + case 'timestamp': + parent::setTimestamp($value); + break; + + case 'timezone': + case 'tz': + $this->setTimezone($value); + break; + + default: + throw new InvalidArgumentException(sprintf("Unknown setter '%s'", $name)); + } + } + + /** + * Set the instance's year + * + * @param int $value + * + * @return static + */ + public function year($value) + { + $this->year = $value; + + return $this; + } + + /** + * Set the instance's month + * + * @param int $value + * + * @return static + */ + public function month($value) + { + $this->month = $value; + + return $this; + } + + /** + * Set the instance's day + * + * @param int $value + * + * @return static + */ + public function day($value) + { + $this->day = $value; + + return $this; + } + + /** + * Set the instance's hour + * + * @param int $value + * + * @return static + */ + public function hour($value) + { + $this->hour = $value; + + return $this; + } + + /** + * Set the instance's minute + * + * @param int $value + * + * @return static + */ + public function minute($value) + { + $this->minute = $value; + + return $this; + } + + /** + * Set the instance's second + * + * @param int $value + * + * @return static + */ + public function second($value) + { + $this->second = $value; + + return $this; + } + + /** + * Sets the current date of the DateTime object to a different date. + * Calls modify as a workaround for a php bug + * + * @param int $year + * @param int $month + * @param int $day + * + * @return static + * + * @see https://github.com/briannesbitt/Carbon/issues/539 + * @see https://bugs.php.net/bug.php?id=63863 + */ + public function setDate($year, $month, $day) + { + $this->modify('+0 day'); + + return parent::setDate($year, $month, $day); + } + + /** + * Set the date and time all together + * + * @param int $year + * @param int $month + * @param int $day + * @param int $hour + * @param int $minute + * @param int $second + * + * @return static + */ + public function setDateTime($year, $month, $day, $hour, $minute, $second = 0) + { + return $this->setDate($year, $month, $day)->setTime($hour, $minute, $second); + } + + /** + * Set the time by time string + * + * @param string $time + * + * @return static + */ + public function setTimeFromTimeString($time) + { + if (strpos($time, ':') === false) { + $time .= ':0'; + } + + return $this->modify($time); + } + + /** + * Set the instance's timestamp + * + * @param int $value + * + * @return static + */ + public function timestamp($value) + { + return $this->setTimestamp($value); + } + + /** + * Alias for setTimezone() + * + * @param \DateTimeZone|string $value + * + * @return static + */ + public function timezone($value) + { + return $this->setTimezone($value); + } + + /** + * Alias for setTimezone() + * + * @param \DateTimeZone|string $value + * + * @return static + */ + public function tz($value) + { + return $this->setTimezone($value); + } + + /** + * Set the instance's timezone from a string or object + * + * @param \DateTimeZone|string $value + * + * @return static + */ + public function setTimezone($value) + { + parent::setTimezone(static::safeCreateDateTimeZone($value)); + // https://bugs.php.net/bug.php?id=72338 + // just workaround on this bug + $this->getTimestamp(); + + return $this; + } + + /** + * Set the year, month, and date for this instance to that of the passed instance. + * + * @param \Carbon\Carbon|\DateTimeInterface $date + * + * @return static + */ + public function setDateFrom($date) + { + $date = static::instance($date); + + $this->setDate($date->year, $date->month, $date->day); + + return $this; + } + + /** + * Set the hour, day, and time for this instance to that of the passed instance. + * + * @param \Carbon\Carbon|\DateTimeInterface $date + * + * @return static + */ + public function setTimeFrom($date) + { + $date = static::instance($date); + + $this->setTime($date->hour, $date->minute, $date->second); + + return $this; + } + + /** + * Get the days of the week + * + * @return array + */ + public static function getDays() + { + return static::$days; + } + + /////////////////////////////////////////////////////////////////// + /////////////////////// WEEK SPECIAL DAYS ///////////////////////// + /////////////////////////////////////////////////////////////////// + + /** + * Get the first day of week + * + * @return int + */ + public static function getWeekStartsAt() + { + return static::$weekStartsAt; + } + + /** + * Set the first day of week + * + * @param int $day week start day + * + * @throws InvalidArgumentException + * + * @return void + */ + public static function setWeekStartsAt($day) + { + if ($day > static::SATURDAY || $day < static::SUNDAY) { + throw new InvalidArgumentException('Day of a week should be greater than or equal to 0 and less than or equal to 6.'); + } + + static::$weekStartsAt = $day; + } + + /** + * Get the last day of week + * + * @return int + */ + public static function getWeekEndsAt() + { + return static::$weekEndsAt; + } + + /** + * Set the last day of week + * + * @param int $day + * + * @throws InvalidArgumentException + * + * @return void + */ + public static function setWeekEndsAt($day) + { + if ($day > static::SATURDAY || $day < static::SUNDAY) { + throw new InvalidArgumentException('Day of a week should be greater than or equal to 0 and less than or equal to 6.'); + } + + static::$weekEndsAt = $day; + } + + /** + * Get weekend days + * + * @return array + */ + public static function getWeekendDays() + { + return static::$weekendDays; + } + + /** + * Set weekend days + * + * @param array $days + * + * @return void + */ + public static function setWeekendDays($days) + { + static::$weekendDays = $days; + } + + /** + * get midday/noon hour + * + * @return int + */ + public static function getMidDayAt() + { + return static::$midDayAt; + } + + /** + * Set midday/noon hour + * + * @param int $hour midday hour + * + * @return void + */ + public static function setMidDayAt($hour) + { + static::$midDayAt = $hour; + } + + /////////////////////////////////////////////////////////////////// + ///////////////////////// TESTING AIDS //////////////////////////// + /////////////////////////////////////////////////////////////////// + + /** + * Set a Carbon instance (real or mock) to be returned when a "now" + * instance is created. The provided instance will be returned + * specifically under the following conditions: + * - A call to the static now() method, ex. Carbon::now() + * - When a null (or blank string) is passed to the constructor or parse(), ex. new Carbon(null) + * - When the string "now" is passed to the constructor or parse(), ex. new Carbon('now') + * - When a string containing the desired time is passed to Carbon::parse(). + * + * Note the timezone parameter was left out of the examples above and + * has no affect as the mock value will be returned regardless of its value. + * + * To clear the test instance call this method using the default + * parameter of null. + * + * @param \Carbon\Carbon|null $testNow real or mock Carbon instance + * @param \Carbon\Carbon|string|null $testNow + */ + public static function setTestNow($testNow = null) + { + static::$testNow = is_string($testNow) ? static::parse($testNow) : $testNow; + } + + /** + * Get the Carbon instance (real or mock) to be returned when a "now" + * instance is created. + * + * @return static the current instance used for testing + */ + public static function getTestNow() + { + return static::$testNow; + } + + /** + * Determine if there is a valid test instance set. A valid test instance + * is anything that is not null. + * + * @return bool true if there is a test instance, otherwise false + */ + public static function hasTestNow() + { + return static::getTestNow() !== null; + } + + /** + * Determine if a time string will produce a relative date. + * + * @param string $time + * + * @return bool true if time match a relative date, false if absolute or invalid time string + */ + public static function hasRelativeKeywords($time) + { + if (strtotime($time) === false) { + return false; + } + + $date1 = new DateTime('2000-01-01T00:00:00Z'); + $date1->modify($time); + $date2 = new DateTime('2001-12-25T00:00:00Z'); + $date2->modify($time); + + return $date1 != $date2; + } + + /////////////////////////////////////////////////////////////////// + /////////////////////// LOCALIZATION ////////////////////////////// + /////////////////////////////////////////////////////////////////// + + /** + * Initialize the translator instance if necessary. + * + * @return \Symfony\Component\Translation\TranslatorInterface + */ + protected static function translator() + { + if (static::$translator === null) { + static::$translator = Translator::get(); + } + + return static::$translator; + } + + /** + * Get the translator instance in use + * + * @return \Symfony\Component\Translation\TranslatorInterface + */ + public static function getTranslator() + { + return static::translator(); + } + + /** + * Set the translator instance to use + * + * @param \Symfony\Component\Translation\TranslatorInterface $translator + * + * @return void + */ + public static function setTranslator(TranslatorInterface $translator) + { + static::$translator = $translator; + } + + /** + * Get the current translator locale + * + * @return string + */ + public static function getLocale() + { + return static::translator()->getLocale(); + } + + /** + * Set the current translator locale and indicate if the source locale file exists + * + * @param string $locale locale ex. en + * + * @return bool + */ + public static function setLocale($locale) + { + return static::translator()->setLocale($locale) !== false; + } + + /** + * Set the current locale to the given, execute the passed function, reset the locale to previous one, + * then return the result of the closure (or null if the closure was void). + * + * @param string $locale locale ex. en + * + * @return mixed + */ + public static function executeWithLocale($locale, $func) + { + $currentLocale = static::getLocale(); + $result = call_user_func($func, static::setLocale($locale) ? static::getLocale() : false, static::translator()); + static::setLocale($currentLocale); + + return $result; + } + + /** + * Returns true if the given locale is internally supported and has short-units support. + * Support is considered enabled if either year, day or hour has a short variant translated. + * + * @param string $locale locale ex. en + * + * @return bool + */ + public static function localeHasShortUnits($locale) + { + return static::executeWithLocale($locale, function ($newLocale, TranslatorInterface $translator) { + return $newLocale && + ( + ($y = $translator->trans('y')) !== 'y' && + $y !== $translator->trans('year') + ) || ( + ($y = $translator->trans('d')) !== 'd' && + $y !== $translator->trans('day') + ) || ( + ($y = $translator->trans('h')) !== 'h' && + $y !== $translator->trans('hour') + ); + }); + } + + /** + * Returns true if the given locale is internally supported and has diff syntax support (ago, from now, before, after). + * Support is considered enabled if the 4 sentences are translated in the given locale. + * + * @param string $locale locale ex. en + * + * @return bool + */ + public static function localeHasDiffSyntax($locale) + { + return static::executeWithLocale($locale, function ($newLocale, TranslatorInterface $translator) { + return $newLocale && + $translator->trans('ago') !== 'ago' && + $translator->trans('from_now') !== 'from_now' && + $translator->trans('before') !== 'before' && + $translator->trans('after') !== 'after'; + }); + } + + /** + * Returns true if the given locale is internally supported and has words for 1-day diff (just now, yesterday, tomorrow). + * Support is considered enabled if the 3 words are translated in the given locale. + * + * @param string $locale locale ex. en + * + * @return bool + */ + public static function localeHasDiffOneDayWords($locale) + { + return static::executeWithLocale($locale, function ($newLocale, TranslatorInterface $translator) { + return $newLocale && + $translator->trans('diff_now') !== 'diff_now' && + $translator->trans('diff_yesterday') !== 'diff_yesterday' && + $translator->trans('diff_tomorrow') !== 'diff_tomorrow'; + }); + } + + /** + * Returns true if the given locale is internally supported and has words for 2-days diff (before yesterday, after tomorrow). + * Support is considered enabled if the 2 words are translated in the given locale. + * + * @param string $locale locale ex. en + * + * @return bool + */ + public static function localeHasDiffTwoDayWords($locale) + { + return static::executeWithLocale($locale, function ($newLocale, TranslatorInterface $translator) { + return $newLocale && + $translator->trans('diff_before_yesterday') !== 'diff_before_yesterday' && + $translator->trans('diff_after_tomorrow') !== 'diff_after_tomorrow'; + }); + } + + /** + * Returns true if the given locale is internally supported and has period syntax support (X times, every X, from X, to X). + * Support is considered enabled if the 4 sentences are translated in the given locale. + * + * @param string $locale locale ex. en + * + * @return bool + */ + public static function localeHasPeriodSyntax($locale) + { + return static::executeWithLocale($locale, function ($newLocale, TranslatorInterface $translator) { + return $newLocale && + $translator->trans('period_recurrences') !== 'period_recurrences' && + $translator->trans('period_interval') !== 'period_interval' && + $translator->trans('period_start_date') !== 'period_start_date' && + $translator->trans('period_end_date') !== 'period_end_date'; + }); + } + + /** + * Returns the list of internally available locales and already loaded custom locales. + * (It will ignore custom translator dynamic loading.) + * + * @return array + */ + public static function getAvailableLocales() + { + $translator = static::translator(); + $locales = array(); + if ($translator instanceof Translator) { + foreach (glob(__DIR__.'/Lang/*.php') as $file) { + $locales[] = substr($file, strrpos($file, '/') + 1, -4); + } + + $locales = array_unique(array_merge($locales, array_keys($translator->getMessages()))); + } + + return $locales; + } + + /////////////////////////////////////////////////////////////////// + /////////////////////// STRING FORMATTING ///////////////////////// + /////////////////////////////////////////////////////////////////// + + /** + * Set if UTF8 will be used for localized date/time + * + * @param bool $utf8 + */ + public static function setUtf8($utf8) + { + static::$utf8 = $utf8; + } + + /** + * Format the instance with the current locale. You can set the current + * locale using setlocale() http://php.net/setlocale. + * + * @param string $format + * + * @return string + */ + public function formatLocalized($format) + { + // Check for Windows to find and replace the %e modifier correctly. + if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { + $format = preg_replace('#(?toDateTimeString())); + + return static::$utf8 ? utf8_encode($formatted) : $formatted; + } + + /** + * Reset the format used to the default when type juggling a Carbon instance to a string + * + * @return void + */ + public static function resetToStringFormat() + { + static::setToStringFormat(static::DEFAULT_TO_STRING_FORMAT); + } + + /** + * Set the default format used when type juggling a Carbon instance to a string + * + * @param string|Closure $format + * + * @return void + */ + public static function setToStringFormat($format) + { + static::$toStringFormat = $format; + } + + /** + * Format the instance as a string using the set format + * + * @return string + */ + public function __toString() + { + $format = static::$toStringFormat; + + return $this->format($format instanceof Closure ? $format($this) : $format); + } + + /** + * Format the instance as date + * + * @return string + */ + public function toDateString() + { + return $this->format('Y-m-d'); + } + + /** + * Format the instance as a readable date + * + * @return string + */ + public function toFormattedDateString() + { + return $this->format('M j, Y'); + } + + /** + * Format the instance as time + * + * @return string + */ + public function toTimeString() + { + return $this->format('H:i:s'); + } + + /** + * Format the instance as date and time + * + * @return string + */ + public function toDateTimeString() + { + return $this->format('Y-m-d H:i:s'); + } + + /** + * Format the instance with day, date and time + * + * @return string + */ + public function toDayDateTimeString() + { + return $this->format('D, M j, Y g:i A'); + } + + /** + * Format the instance as ATOM + * + * @return string + */ + public function toAtomString() + { + return $this->format(static::ATOM); + } + + /** + * Format the instance as COOKIE + * + * @return string + */ + public function toCookieString() + { + return $this->format(static::COOKIE); + } + + /** + * Format the instance as ISO8601 + * + * @return string + */ + public function toIso8601String() + { + return $this->toAtomString(); + } + + /** + * Format the instance as RFC822 + * + * @return string + */ + public function toRfc822String() + { + return $this->format(static::RFC822); + } + + /** + * Convert the instance to UTC and return as Zulu ISO8601 + * + * @return string + */ + public function toIso8601ZuluString() + { + return $this->copy()->setTimezone('UTC')->format('Y-m-d\TH:i:s\Z'); + } + + /** + * Format the instance as RFC850 + * + * @return string + */ + public function toRfc850String() + { + return $this->format(static::RFC850); + } + + /** + * Format the instance as RFC1036 + * + * @return string + */ + public function toRfc1036String() + { + return $this->format(static::RFC1036); + } + + /** + * Format the instance as RFC1123 + * + * @return string + */ + public function toRfc1123String() + { + return $this->format(static::RFC1123); + } + + /** + * Format the instance as RFC2822 + * + * @return string + */ + public function toRfc2822String() + { + return $this->format(static::RFC2822); + } + + /** + * Format the instance as RFC3339 + * + * @return string + */ + public function toRfc3339String() + { + return $this->format(static::RFC3339); + } + + /** + * Format the instance as RSS + * + * @return string + */ + public function toRssString() + { + return $this->format(static::RSS); + } + + /** + * Format the instance as W3C + * + * @return string + */ + public function toW3cString() + { + return $this->format(static::W3C); + } + + /** + * Format the instance as RFC7231 + * + * @return string + */ + public function toRfc7231String() + { + return $this->copy() + ->setTimezone('GMT') + ->format(static::RFC7231_FORMAT); + } + + /** + * Get default array representation + * + * @return array + */ + public function toArray() + { + return array( + 'year' => $this->year, + 'month' => $this->month, + 'day' => $this->day, + 'dayOfWeek' => $this->dayOfWeek, + 'dayOfYear' => $this->dayOfYear, + 'hour' => $this->hour, + 'minute' => $this->minute, + 'second' => $this->second, + 'micro' => $this->micro, + 'timestamp' => $this->timestamp, + 'formatted' => $this->format(self::DEFAULT_TO_STRING_FORMAT), + 'timezone' => $this->timezone, + ); + } + + /////////////////////////////////////////////////////////////////// + ////////////////////////// COMPARISONS //////////////////////////// + /////////////////////////////////////////////////////////////////// + + /** + * Determines if the instance is equal to another + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date + * + * @return bool + */ + public function eq($date) + { + return $this == $date; + } + + /** + * Determines if the instance is equal to another + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date + * + * @see eq() + * + * @return bool + */ + public function equalTo($date) + { + return $this->eq($date); + } + + /** + * Determines if the instance is not equal to another + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date + * + * @return bool + */ + public function ne($date) + { + return !$this->eq($date); + } + + /** + * Determines if the instance is not equal to another + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date + * + * @see ne() + * + * @return bool + */ + public function notEqualTo($date) + { + return $this->ne($date); + } + + /** + * Determines if the instance is greater (after) than another + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date + * + * @return bool + */ + public function gt($date) + { + return $this > $date; + } + + /** + * Determines if the instance is greater (after) than another + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date + * + * @see gt() + * + * @return bool + */ + public function greaterThan($date) + { + return $this->gt($date); + } + + /** + * Determines if the instance is greater (after) than or equal to another + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date + * + * @return bool + */ + public function gte($date) + { + return $this >= $date; + } + + /** + * Determines if the instance is greater (after) than or equal to another + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date + * + * @see gte() + * + * @return bool + */ + public function greaterThanOrEqualTo($date) + { + return $this->gte($date); + } + + /** + * Determines if the instance is less (before) than another + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date + * + * @return bool + */ + public function lt($date) + { + return $this < $date; + } + + /** + * Determines if the instance is less (before) than another + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date + * + * @see lt() + * + * @return bool + */ + public function lessThan($date) + { + return $this->lt($date); + } + + /** + * Determines if the instance is less (before) or equal to another + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date + * + * @return bool + */ + public function lte($date) + { + return $this <= $date; + } + + /** + * Determines if the instance is less (before) or equal to another + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date + * + * @see lte() + * + * @return bool + */ + public function lessThanOrEqualTo($date) + { + return $this->lte($date); + } + + /** + * Determines if the instance is between two others + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date1 + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date2 + * @param bool $equal Indicates if an equal to comparison should be done + * + * @return bool + */ + public function between($date1, $date2, $equal = true) + { + if ($date1->gt($date2)) { + $temp = $date1; + $date1 = $date2; + $date2 = $temp; + } + + if ($equal) { + return $this->gte($date1) && $this->lte($date2); + } + + return $this->gt($date1) && $this->lt($date2); + } + + /** + * Get the closest date from the instance. + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date1 + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date2 + * + * @return static + */ + public function closest($date1, $date2) + { + return $this->diffInSeconds($date1) < $this->diffInSeconds($date2) ? $date1 : $date2; + } + + /** + * Get the farthest date from the instance. + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date1 + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date2 + * + * @return static + */ + public function farthest($date1, $date2) + { + return $this->diffInSeconds($date1) > $this->diffInSeconds($date2) ? $date1 : $date2; + } + + /** + * Get the minimum instance between a given instance (default now) and the current instance. + * + * @param \Carbon\Carbon|\DateTimeInterface|string|null $date + * + * @return static + */ + public function min($date = null) + { + $date = $this->resolveCarbon($date); + + return $this->lt($date) ? $this : $date; + } + + /** + * Get the minimum instance between a given instance (default now) and the current instance. + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date + * + * @see min() + * + * @return static + */ + public function minimum($date = null) + { + return $this->min($date); + } + + /** + * Get the maximum instance between a given instance (default now) and the current instance. + * + * @param \Carbon\Carbon|\DateTimeInterface|string|null $date + * + * @return static + */ + public function max($date = null) + { + $date = $this->resolveCarbon($date); + + return $this->gt($date) ? $this : $date; + } + + /** + * Get the maximum instance between a given instance (default now) and the current instance. + * + * @param \Carbon\Carbon|\DateTimeInterface|mixed $date + * + * @see max() + * + * @return static + */ + public function maximum($date = null) + { + return $this->max($date); + } + + /** + * Determines if the instance is a weekday. + * + * @return bool + */ + public function isWeekday() + { + return !$this->isWeekend(); + } + + /** + * Determines if the instance is a weekend day. + * + * @return bool + */ + public function isWeekend() + { + return in_array($this->dayOfWeek, static::$weekendDays); + } + + /** + * Determines if the instance is yesterday. + * + * @return bool + */ + public function isYesterday() + { + return $this->toDateString() === static::yesterday($this->getTimezone())->toDateString(); + } + + /** + * Determines if the instance is today. + * + * @return bool + */ + public function isToday() + { + return $this->toDateString() === $this->nowWithSameTz()->toDateString(); + } + + /** + * Determines if the instance is tomorrow. + * + * @return bool + */ + public function isTomorrow() + { + return $this->toDateString() === static::tomorrow($this->getTimezone())->toDateString(); + } + + /** + * Determines if the instance is within the next week. + * + * @return bool + */ + public function isNextWeek() + { + return $this->weekOfYear === $this->nowWithSameTz()->addWeek()->weekOfYear; + } + + /** + * Determines if the instance is within the last week. + * + * @return bool + */ + public function isLastWeek() + { + return $this->weekOfYear === $this->nowWithSameTz()->subWeek()->weekOfYear; + } + + /** + * Determines if the instance is within the next quarter. + * + * @return bool + */ + public function isNextQuarter() + { + return $this->quarter === $this->nowWithSameTz()->addQuarter()->quarter; + } + + /** + * Determines if the instance is within the last quarter. + * + * @return bool + */ + public function isLastQuarter() + { + return $this->quarter === $this->nowWithSameTz()->subQuarter()->quarter; + } + + /** + * Determines if the instance is within the next month. + * + * @return bool + */ + public function isNextMonth() + { + return $this->month === $this->nowWithSameTz()->addMonthNoOverflow()->month; + } + + /** + * Determines if the instance is within the last month. + * + * @return bool + */ + public function isLastMonth() + { + return $this->month === $this->nowWithSameTz()->subMonthNoOverflow()->month; + } + + /** + * Determines if the instance is within next year. + * + * @return bool + */ + public function isNextYear() + { + return $this->year === $this->nowWithSameTz()->addYear()->year; + } + + /** + * Determines if the instance is within the previous year. + * + * @return bool + */ + public function isLastYear() + { + return $this->year === $this->nowWithSameTz()->subYear()->year; + } + + /** + * Determines if the instance is in the future, ie. greater (after) than now. + * + * @return bool + */ + public function isFuture() + { + return $this->gt($this->nowWithSameTz()); + } + + /** + * Determines if the instance is in the past, ie. less (before) than now. + * + * @return bool + */ + public function isPast() + { + return $this->lt($this->nowWithSameTz()); + } + + /** + * Determines if the instance is a leap year. + * + * @return bool + */ + public function isLeapYear() + { + return $this->format('L') === '1'; + } + + /** + * Determines if the instance is a long year + * + * @see https://en.wikipedia.org/wiki/ISO_8601#Week_dates + * + * @return bool + */ + public function isLongYear() + { + return static::create($this->year, 12, 28, 0, 0, 0, $this->tz)->weekOfYear === 53; + } + + /** + * Compares the formatted values of the two dates. + * + * @param string $format The date formats to compare. + * @param \Carbon\Carbon|\DateTimeInterface|null $date The instance to compare with or null to use current day. + * + * @throws \InvalidArgumentException + * + * @return bool + */ + public function isSameAs($format, $date = null) + { + $date = $date ?: static::now($this->tz); + + static::expectDateTime($date, 'null'); + + return $this->format($format) === $date->format($format); + } + + /** + * Determines if the instance is in the current year. + * + * @return bool + */ + public function isCurrentYear() + { + return $this->isSameYear(); + } + + /** + * Checks if the passed in date is in the same year as the instance year. + * + * @param \Carbon\Carbon|\DateTimeInterface|null $date The instance to compare with or null to use current day. + * + * @return bool + */ + public function isSameYear($date = null) + { + return $this->isSameAs('Y', $date); + } + + /** + * Determines if the instance is in the current month. + * + * @return bool + */ + public function isCurrentQuarter() + { + return $this->isSameQuarter(); + } + + /** + * Checks if the passed in date is in the same quarter as the instance quarter (and year if needed). + * + * @param \Carbon\Carbon|\DateTimeInterface|null $date The instance to compare with or null to use current day. + * @param bool $ofSameYear Check if it is the same month in the same year. + * + * @return bool + */ + public function isSameQuarter($date = null, $ofSameYear = null) + { + $date = $date ? static::instance($date) : static::now($this->tz); + + static::expectDateTime($date, 'null'); + + $ofSameYear = is_null($ofSameYear) ? static::shouldCompareYearWithMonth() : $ofSameYear; + + return $this->quarter === $date->quarter && (!$ofSameYear || $this->isSameYear($date)); + } + + /** + * Determines if the instance is in the current month. + * + * @param bool $ofSameYear Check if it is the same month in the same year. + * + * @return bool + */ + public function isCurrentMonth($ofSameYear = null) + { + return $this->isSameMonth($ofSameYear); + } + + /** + * Checks if the passed in date is in the same month as the instance´s month. + * + * Note that this defaults to only comparing the month while ignoring the year. + * To test if it is the same exact month of the same year, pass in true as the second parameter. + * + * @param \Carbon\Carbon|\DateTimeInterface|null $date The instance to compare with or null to use the current date. + * @param bool $ofSameYear Check if it is the same month in the same year. + * + * @return bool + */ + public function isSameMonth($date = null, $ofSameYear = null) + { + $ofSameYear = is_null($ofSameYear) ? static::shouldCompareYearWithMonth() : $ofSameYear; + + return $this->isSameAs($ofSameYear ? 'Y-m' : 'm', $date); + } + + /** + * Determines if the instance is in the current day. + * + * @return bool + */ + public function isCurrentDay() + { + return $this->isSameDay(); + } + + /** + * Checks if the passed in date is the same exact day as the instance´s day. + * + * @param \Carbon\Carbon|\DateTimeInterface|null $date The instance to compare with or null to use the current date. + * + * @return bool + */ + public function isSameDay($date = null) + { + return $this->isSameAs('Y-m-d', $date); + } + + /** + * Determines if the instance is in the current hour. + * + * @return bool + */ + public function isCurrentHour() + { + return $this->isSameHour(); + } + + /** + * Checks if the passed in date is the same exact hour as the instance´s hour. + * + * @param \Carbon\Carbon|\DateTimeInterface|null $date The instance to compare with or null to use the current date. + * + * @return bool + */ + public function isSameHour($date = null) + { + return $this->isSameAs('Y-m-d H', $date); + } + + /** + * Determines if the instance is in the current minute. + * + * @return bool + */ + public function isCurrentMinute() + { + return $this->isSameMinute(); + } + + /** + * Checks if the passed in date is the same exact minute as the instance´s minute. + * + * @param \Carbon\Carbon|\DateTimeInterface|null $date The instance to compare with or null to use the current date. + * + * @return bool + */ + public function isSameMinute($date = null) + { + return $this->isSameAs('Y-m-d H:i', $date); + } + + /** + * Determines if the instance is in the current second. + * + * @return bool + */ + public function isCurrentSecond() + { + return $this->isSameSecond(); + } + + /** + * Checks if the passed in date is the same exact second as the instance´s second. + * + * @param \Carbon\Carbon|\DateTimeInterface|null $date The instance to compare with or null to use the current date. + * + * @return bool + */ + public function isSameSecond($date = null) + { + return $this->isSameAs('Y-m-d H:i:s', $date); + } + + /** + * Checks if this day is a specific day of the week. + * + * @param int $dayOfWeek + * + * @return bool + */ + public function isDayOfWeek($dayOfWeek) + { + return $this->dayOfWeek === $dayOfWeek; + } + + /** + * Checks if this day is a Sunday. + * + * @return bool + */ + public function isSunday() + { + return $this->dayOfWeek === static::SUNDAY; + } + + /** + * Checks if this day is a Monday. + * + * @return bool + */ + public function isMonday() + { + return $this->dayOfWeek === static::MONDAY; + } + + /** + * Checks if this day is a Tuesday. + * + * @return bool + */ + public function isTuesday() + { + return $this->dayOfWeek === static::TUESDAY; + } + + /** + * Checks if this day is a Wednesday. + * + * @return bool + */ + public function isWednesday() + { + return $this->dayOfWeek === static::WEDNESDAY; + } + + /** + * Checks if this day is a Thursday. + * + * @return bool + */ + public function isThursday() + { + return $this->dayOfWeek === static::THURSDAY; + } + + /** + * Checks if this day is a Friday. + * + * @return bool + */ + public function isFriday() + { + return $this->dayOfWeek === static::FRIDAY; + } + + /** + * Checks if this day is a Saturday. + * + * @return bool + */ + public function isSaturday() + { + return $this->dayOfWeek === static::SATURDAY; + } + + /** + * Check if its the birthday. Compares the date/month values of the two dates. + * + * @param \Carbon\Carbon|\DateTimeInterface|null $date The instance to compare with or null to use current day. + * + * @return bool + */ + public function isBirthday($date = null) + { + return $this->isSameAs('md', $date); + } + + /** + * Check if today is the last day of the Month + * + * @return bool + */ + public function isLastOfMonth() + { + return $this->day === $this->daysInMonth; + } + + /** + * Check if the instance is start of day / midnight. + * + * @param bool $checkMicroseconds check time at microseconds precision + * /!\ Warning, this is not reliable with PHP < 7.1.4 + * + * @return bool + */ + public function isStartOfDay($checkMicroseconds = false) + { + return $checkMicroseconds + ? $this->format('H:i:s.u') === '00:00:00.000000' + : $this->format('H:i:s') === '00:00:00'; + } + + /** + * Check if the instance is end of day. + * + * @param bool $checkMicroseconds check time at microseconds precision + * /!\ Warning, this is not reliable with PHP < 7.1.4 + * + * @return bool + */ + public function isEndOfDay($checkMicroseconds = false) + { + return $checkMicroseconds + ? $this->format('H:i:s.u') === '23:59:59.999999' + : $this->format('H:i:s') === '23:59:59'; + } + + /** + * Check if the instance is start of day / midnight. + * + * @return bool + */ + public function isMidnight() + { + return $this->isStartOfDay(); + } + + /** + * Check if the instance is midday. + * + * @return bool + */ + public function isMidday() + { + return $this->format('G:i:s') === static::$midDayAt.':00:00'; + } + + /** + * Checks if the (date)time string is in a given format. + * + * @param string $date + * @param string $format + * + * @return bool + */ + public static function hasFormat($date, $format) + { + try { + // Try to create a DateTime object. Throws an InvalidArgumentException if the provided time string + // doesn't match the format in any way. + static::createFromFormat($format, $date); + + // createFromFormat() is known to handle edge cases silently. + // E.g. "1975-5-1" (Y-n-j) will still be parsed correctly when "Y-m-d" is supplied as the format. + // To ensure we're really testing against our desired format, perform an additional regex validation. + $regex = strtr( + preg_quote($format, '/'), + static::$regexFormats + ); + + return (bool) preg_match('/^'.$regex.'$/', $date); + } catch (InvalidArgumentException $e) { + } + + return false; + } + + /////////////////////////////////////////////////////////////////// + /////////////////// ADDITIONS AND SUBTRACTIONS //////////////////// + /////////////////////////////////////////////////////////////////// + + /** + * Add centuries to the instance. Positive $value travels forward while + * negative $value travels into the past. + * + * @param int $value + * + * @return static + */ + public function addCenturies($value) + { + return $this->addYears(static::YEARS_PER_CENTURY * $value); + } + + /** + * Add a century to the instance + * + * @param int $value + * + * @return static + */ + public function addCentury($value = 1) + { + return $this->addCenturies($value); + } + + /** + * Remove centuries from the instance + * + * @param int $value + * + * @return static + */ + public function subCenturies($value) + { + return $this->addCenturies(-1 * $value); + } + + /** + * Remove a century from the instance + * + * @param int $value + * + * @return static + */ + public function subCentury($value = 1) + { + return $this->subCenturies($value); + } + + /** + * Add years to the instance. Positive $value travel forward while + * negative $value travel into the past. + * + * @param int $value + * + * @return static + */ + public function addYears($value) + { + if ($this->shouldOverflowYears()) { + return $this->addYearsWithOverflow($value); + } + + return $this->addYearsNoOverflow($value); + } + + /** + * Add a year to the instance + * + * @param int $value + * + * @return static + */ + public function addYear($value = 1) + { + return $this->addYears($value); + } + + /** + * Add years to the instance with no overflow of months + * Positive $value travel forward while + * negative $value travel into the past. + * + * @param int $value + * + * @return static + */ + public function addYearsNoOverflow($value) + { + return $this->addMonthsNoOverflow($value * static::MONTHS_PER_YEAR); + } + + /** + * Add year with overflow months set to false + * + * @param int $value + * + * @return static + */ + public function addYearNoOverflow($value = 1) + { + return $this->addYearsNoOverflow($value); + } + + /** + * Add years to the instance. + * Positive $value travel forward while + * negative $value travel into the past. + * + * @param int $value + * + * @return static + */ + public function addYearsWithOverflow($value) + { + return $this->modify((int) $value.' year'); + } + + /** + * Add year with overflow. + * + * @param int $value + * + * @return static + */ + public function addYearWithOverflow($value = 1) + { + return $this->addYearsWithOverflow($value); + } + + /** + * Remove years from the instance. + * + * @param int $value + * + * @return static + */ + public function subYears($value) + { + return $this->addYears(-1 * $value); + } + + /** + * Remove a year from the instance + * + * @param int $value + * + * @return static + */ + public function subYear($value = 1) + { + return $this->subYears($value); + } + + /** + * Remove years from the instance with no month overflow. + * + * @param int $value + * + * @return static + */ + public function subYearsNoOverflow($value) + { + return $this->subMonthsNoOverflow($value * static::MONTHS_PER_YEAR); + } + + /** + * Remove year from the instance with no month overflow + * + * @param int $value + * + * @return static + */ + public function subYearNoOverflow($value = 1) + { + return $this->subYearsNoOverflow($value); + } + + /** + * Remove years from the instance. + * + * @param int $value + * + * @return static + */ + public function subYearsWithOverflow($value) + { + return $this->subMonthsWithOverflow($value * static::MONTHS_PER_YEAR); + } + + /** + * Remove year from the instance. + * + * @param int $value + * + * @return static + */ + public function subYearWithOverflow($value = 1) + { + return $this->subYearsWithOverflow($value); + } + + /** + * Add quarters to the instance. Positive $value travels forward while + * negative $value travels into the past. + * + * @param int $value + * + * @return static + */ + public function addQuarters($value) + { + return $this->addMonths(static::MONTHS_PER_QUARTER * $value); + } + + /** + * Add a quarter to the instance + * + * @param int $value + * + * @return static + */ + public function addQuarter($value = 1) + { + return $this->addQuarters($value); + } + + /** + * Remove quarters from the instance + * + * @param int $value + * + * @return static + */ + public function subQuarters($value) + { + return $this->addQuarters(-1 * $value); + } + + /** + * Remove a quarter from the instance + * + * @param int $value + * + * @return static + */ + public function subQuarter($value = 1) + { + return $this->subQuarters($value); + } + + /** + * Add months to the instance. Positive $value travels forward while + * negative $value travels into the past. + * + * @param int $value + * + * @return static + */ + public function addMonths($value) + { + if (static::shouldOverflowMonths()) { + return $this->addMonthsWithOverflow($value); + } + + return $this->addMonthsNoOverflow($value); + } + + /** + * Add a month to the instance + * + * @param int $value + * + * @return static + */ + public function addMonth($value = 1) + { + return $this->addMonths($value); + } + + /** + * Remove months from the instance + * + * @param int $value + * + * @return static + */ + public function subMonths($value) + { + return $this->addMonths(-1 * $value); + } + + /** + * Remove a month from the instance + * + * @param int $value + * + * @return static + */ + public function subMonth($value = 1) + { + return $this->subMonths($value); + } + + /** + * Add months to the instance. Positive $value travels forward while + * negative $value travels into the past. + * + * @param int $value + * + * @return static + */ + public function addMonthsWithOverflow($value) + { + return $this->modify((int) $value.' month'); + } + + /** + * Add a month to the instance + * + * @param int $value + * + * @return static + */ + public function addMonthWithOverflow($value = 1) + { + return $this->addMonthsWithOverflow($value); + } + + /** + * Remove months from the instance + * + * @param int $value + * + * @return static + */ + public function subMonthsWithOverflow($value) + { + return $this->addMonthsWithOverflow(-1 * $value); + } + + /** + * Remove a month from the instance + * + * @param int $value + * + * @return static + */ + public function subMonthWithOverflow($value = 1) + { + return $this->subMonthsWithOverflow($value); + } + + /** + * Add months without overflowing to the instance. Positive $value + * travels forward while negative $value travels into the past. + * + * @param int $value + * + * @return static + */ + public function addMonthsNoOverflow($value) + { + $day = $this->day; + + $this->modify((int) $value.' month'); + + if ($day !== $this->day) { + $this->modify('last day of previous month'); + } + + return $this; + } + + /** + * Add a month with no overflow to the instance + * + * @param int $value + * + * @return static + */ + public function addMonthNoOverflow($value = 1) + { + return $this->addMonthsNoOverflow($value); + } + + /** + * Remove months with no overflow from the instance + * + * @param int $value + * + * @return static + */ + public function subMonthsNoOverflow($value) + { + return $this->addMonthsNoOverflow(-1 * $value); + } + + /** + * Remove a month with no overflow from the instance + * + * @param int $value + * + * @return static + */ + public function subMonthNoOverflow($value = 1) + { + return $this->subMonthsNoOverflow($value); + } + + /** + * Add days to the instance. Positive $value travels forward while + * negative $value travels into the past. + * + * @param int $value + * + * @return static + */ + public function addDays($value) + { + return $this->modify((int) $value.' day'); + } + + /** + * Add a day to the instance + * + * @param int $value + * + * @return static + */ + public function addDay($value = 1) + { + return $this->addDays($value); + } + + /** + * Remove days from the instance + * + * @param int $value + * + * @return static + */ + public function subDays($value) + { + return $this->addDays(-1 * $value); + } + + /** + * Remove a day from the instance + * + * @param int $value + * + * @return static + */ + public function subDay($value = 1) + { + return $this->subDays($value); + } + + /** + * Add weekdays to the instance. Positive $value travels forward while + * negative $value travels into the past. + * + * @param int $value + * + * @return static + */ + public function addWeekdays($value) + { + // Fix for weekday bug https://bugs.php.net/bug.php?id=54909 + $t = $this->toTimeString(); + $this->modify((int) $value.' weekday'); + + return $this->setTimeFromTimeString($t); + } + + /** + * Add a weekday to the instance + * + * @param int $value + * + * @return static + */ + public function addWeekday($value = 1) + { + return $this->addWeekdays($value); + } + + /** + * Remove weekdays from the instance + * + * @param int $value + * + * @return static + */ + public function subWeekdays($value) + { + return $this->addWeekdays(-1 * $value); + } + + /** + * Remove a weekday from the instance + * + * @param int $value + * + * @return static + */ + public function subWeekday($value = 1) + { + return $this->subWeekdays($value); + } + + /** + * Add weeks to the instance. Positive $value travels forward while + * negative $value travels into the past. + * + * @param int $value + * + * @return static + */ + public function addWeeks($value) + { + return $this->modify((int) $value.' week'); + } + + /** + * Add a week to the instance + * + * @param int $value + * + * @return static + */ + public function addWeek($value = 1) + { + return $this->addWeeks($value); + } + + /** + * Remove weeks to the instance + * + * @param int $value + * + * @return static + */ + public function subWeeks($value) + { + return $this->addWeeks(-1 * $value); + } + + /** + * Remove a week from the instance + * + * @param int $value + * + * @return static + */ + public function subWeek($value = 1) + { + return $this->subWeeks($value); + } + + /** + * Add hours to the instance. Positive $value travels forward while + * negative $value travels into the past. + * + * @param int $value + * + * @return static + */ + public function addHours($value) + { + return $this->modify((int) $value.' hour'); + } + + /** + * Add hours to the instance using timestamp. Positive $value travels + * forward while negative $value travels into the past. + * + * @param int $value + * + * @return static + */ + public function addRealHours($value) + { + return $this->addRealMinutes($value * static::MINUTES_PER_HOUR); + } + + /** + * Add an hour to the instance. + * + * @param int $value + * + * @return static + */ + public function addHour($value = 1) + { + return $this->addHours($value); + } + + /** + * Add an hour to the instance using timestamp. + * + * @param int $value + * + * @return static + */ + public function addRealHour($value = 1) + { + return $this->addRealHours($value); + } + + /** + * Remove hours from the instance. + * + * @param int $value + * + * @return static + */ + public function subHours($value) + { + return $this->addHours(-1 * $value); + } + + /** + * Remove hours from the instance using timestamp. + * + * @param int $value + * + * @return static + */ + public function subRealHours($value) + { + return $this->addRealHours(-1 * $value); + } + + /** + * Remove an hour from the instance. + * + * @param int $value + * + * @return static + */ + public function subHour($value = 1) + { + return $this->subHours($value); + } + + /** + * Remove an hour from the instance. + * + * @param int $value + * + * @return static + */ + public function subRealHour($value = 1) + { + return $this->subRealHours($value); + } + + /** + * Add minutes to the instance using timestamp. Positive $value + * travels forward while negative $value travels into the past. + * + * @param int $value + * + * @return static + */ + public function addMinutes($value) + { + return $this->modify((int) $value.' minute'); + } + + /** + * Add minutes to the instance using timestamp. Positive $value travels + * forward while negative $value travels into the past. + * + * @param int $value + * + * @return static + */ + public function addRealMinutes($value) + { + return $this->addRealSeconds($value * static::SECONDS_PER_MINUTE); + } + + /** + * Add a minute to the instance. + * + * @param int $value + * + * @return static + */ + public function addMinute($value = 1) + { + return $this->addMinutes($value); + } + + /** + * Add a minute to the instance using timestamp. + * + * @param int $value + * + * @return static + */ + public function addRealMinute($value = 1) + { + return $this->addRealMinutes($value); + } + + /** + * Remove a minute from the instance. + * + * @param int $value + * + * @return static + */ + public function subMinute($value = 1) + { + return $this->subMinutes($value); + } + + /** + * Remove a minute from the instance using timestamp. + * + * @param int $value + * + * @return static + */ + public function subRealMinute($value = 1) + { + return $this->addRealMinutes(-1 * $value); + } + + /** + * Remove minutes from the instance. + * + * @param int $value + * + * @return static + */ + public function subMinutes($value) + { + return $this->addMinutes(-1 * $value); + } + + /** + * Remove a minute from the instance using timestamp. + * + * @param int $value + * + * @return static + */ + public function subRealMinutes($value = 1) + { + return $this->subRealMinute($value); + } + + /** + * Add seconds to the instance. Positive $value travels forward while + * negative $value travels into the past. + * + * @param int $value + * + * @return static + */ + public function addSeconds($value) + { + return $this->modify((int) $value.' second'); + } + + /** + * Add seconds to the instance using timestamp. Positive $value travels + * forward while negative $value travels into the past. + * + * @param int $value + * + * @return static + */ + public function addRealSeconds($value) + { + return $this->setTimestamp($this->getTimestamp() + $value); + } + + /** + * Add a second to the instance. + * + * @param int $value + * + * @return static + */ + public function addSecond($value = 1) + { + return $this->addSeconds($value); + } + + /** + * Add a second to the instance using timestamp. + * + * @param int $value + * + * @return static + */ + public function addRealSecond($value = 1) + { + return $this->addRealSeconds($value); + } + + /** + * Remove seconds from the instance. + * + * @param int $value + * + * @return static + */ + public function subSeconds($value) + { + return $this->addSeconds(-1 * $value); + } + + /** + * Remove seconds from the instance using timestamp. + * + * @param int $value + * + * @return static + */ + public function subRealSeconds($value) + { + return $this->addRealSeconds(-1 * $value); + } + + /** + * Remove a second from the instance + * + * @param int $value + * + * @return static + */ + public function subSecond($value = 1) + { + return $this->subSeconds($value); + } + + /** + * Remove a second from the instance using timestamp. + * + * @param int $value + * + * @return static + */ + public function subRealSecond($value = 1) + { + return $this->subRealSeconds($value); + } + + /////////////////////////////////////////////////////////////////// + /////////////////////////// DIFFERENCES /////////////////////////// + /////////////////////////////////////////////////////////////////// + + /** + * Get the difference as a CarbonInterval instance + * + * @param \Carbon\Carbon|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return CarbonInterval + */ + public function diffAsCarbonInterval($date = null, $absolute = true) + { + return CarbonInterval::instance($this->diff($this->resolveCarbon($date), $absolute)); + } + + /** + * Get the difference in years + * + * @param \Carbon\Carbon|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInYears($date = null, $absolute = true) + { + return (int) $this->diff($this->resolveCarbon($date), $absolute)->format('%r%y'); + } + + /** + * Get the difference in months + * + * @param \Carbon\Carbon|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInMonths($date = null, $absolute = true) + { + $date = $this->resolveCarbon($date); + + return $this->diffInYears($date, $absolute) * static::MONTHS_PER_YEAR + (int) $this->diff($date, $absolute)->format('%r%m'); + } + + /** + * Get the difference in weeks + * + * @param \Carbon\Carbon|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInWeeks($date = null, $absolute = true) + { + return (int) ($this->diffInDays($date, $absolute) / static::DAYS_PER_WEEK); + } + + /** + * Get the difference in days + * + * @param \Carbon\Carbon|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInDays($date = null, $absolute = true) + { + return (int) $this->diff($this->resolveCarbon($date), $absolute)->format('%r%a'); + } + + /** + * Get the difference in days using a filter closure + * + * @param Closure $callback + * @param \Carbon\Carbon|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInDaysFiltered(Closure $callback, $date = null, $absolute = true) + { + return $this->diffFiltered(CarbonInterval::day(), $callback, $date, $absolute); + } + + /** + * Get the difference in hours using a filter closure + * + * @param Closure $callback + * @param \Carbon\Carbon|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInHoursFiltered(Closure $callback, $date = null, $absolute = true) + { + return $this->diffFiltered(CarbonInterval::hour(), $callback, $date, $absolute); + } + + /** + * Get the difference by the given interval using a filter closure + * + * @param CarbonInterval $ci An interval to traverse by + * @param Closure $callback + * @param \Carbon\Carbon|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffFiltered(CarbonInterval $ci, Closure $callback, $date = null, $absolute = true) + { + $start = $this; + $end = $this->resolveCarbon($date); + $inverse = false; + + if ($end < $start) { + $start = $end; + $end = $this; + $inverse = true; + } + + $period = new DatePeriod($start, $ci, $end); + $values = array_filter(iterator_to_array($period), function ($date) use ($callback) { + return call_user_func($callback, Carbon::instance($date)); + }); + + $diff = count($values); + + return $inverse && !$absolute ? -$diff : $diff; + } + + /** + * Get the difference in weekdays + * + * @param \Carbon\Carbon|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInWeekdays($date = null, $absolute = true) + { + return $this->diffInDaysFiltered(function (Carbon $date) { + return $date->isWeekday(); + }, $date, $absolute); + } + + /** + * Get the difference in weekend days using a filter + * + * @param \Carbon\Carbon|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInWeekendDays($date = null, $absolute = true) + { + return $this->diffInDaysFiltered(function (Carbon $date) { + return $date->isWeekend(); + }, $date, $absolute); + } + + /** + * Get the difference in hours. + * + * @param \Carbon\Carbon|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInHours($date = null, $absolute = true) + { + return (int) ($this->diffInSeconds($date, $absolute) / static::SECONDS_PER_MINUTE / static::MINUTES_PER_HOUR); + } + + /** + * Get the difference in hours using timestamps. + * + * @param \Carbon\Carbon|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInRealHours($date = null, $absolute = true) + { + return (int) ($this->diffInRealSeconds($date, $absolute) / static::SECONDS_PER_MINUTE / static::MINUTES_PER_HOUR); + } + + /** + * Get the difference in minutes. + * + * @param \Carbon\Carbon|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInMinutes($date = null, $absolute = true) + { + return (int) ($this->diffInSeconds($date, $absolute) / static::SECONDS_PER_MINUTE); + } + + /** + * Get the difference in minutes using timestamps. + * + * @param \Carbon\Carbon|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInRealMinutes($date = null, $absolute = true) + { + return (int) ($this->diffInRealSeconds($date, $absolute) / static::SECONDS_PER_MINUTE); + } + + /** + * Get the difference in seconds. + * + * @param \Carbon\Carbon|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInSeconds($date = null, $absolute = true) + { + $diff = $this->diff($this->resolveCarbon($date)); + $value = $diff->days * static::HOURS_PER_DAY * static::MINUTES_PER_HOUR * static::SECONDS_PER_MINUTE + + $diff->h * static::MINUTES_PER_HOUR * static::SECONDS_PER_MINUTE + + $diff->i * static::SECONDS_PER_MINUTE + + $diff->s; + + return $absolute || !$diff->invert ? $value : -$value; + } + + /** + * Get the difference in seconds using timestamps. + * + * @param \Carbon\Carbon|\DateTimeInterface|string|null $date + * @param bool $absolute Get the absolute of the difference + * + * @return int + */ + public function diffInRealSeconds($date = null, $absolute = true) + { + $date = $this->resolveCarbon($date); + $value = $date->getTimestamp() - $this->getTimestamp(); + + return $absolute ? abs($value) : $value; + } + + /** + * The number of seconds since midnight. + * + * @return int + */ + public function secondsSinceMidnight() + { + return $this->diffInSeconds($this->copy()->startOfDay()); + } + + /** + * The number of seconds until 23:59:59. + * + * @return int + */ + public function secondsUntilEndOfDay() + { + return $this->diffInSeconds($this->copy()->endOfDay()); + } + + /** + * Get the difference in a human readable format in the current locale. + * + * When comparing a value in the past to default now: + * 1 hour ago + * 5 months ago + * + * When comparing a value in the future to default now: + * 1 hour from now + * 5 months from now + * + * When comparing a value in the past to another value: + * 1 hour before + * 5 months before + * + * When comparing a value in the future to another value: + * 1 hour after + * 5 months after + * + * @param Carbon|null $other + * @param bool $absolute removes time difference modifiers ago, after, etc + * @param bool $short displays short format of time units + * @param int $parts displays number of parts in the interval + * + * @return string + */ + public function diffForHumans($other = null, $absolute = false, $short = false, $parts = 1) + { + $isNow = $other === null; + $interval = array(); + + $parts = min(6, max(1, (int) $parts)); + $count = 1; + $unit = $short ? 's' : 'second'; + + if ($isNow) { + $other = $this->nowWithSameTz(); + } elseif (!$other instanceof DateTime && !$other instanceof DateTimeInterface) { + $other = static::parse($other); + } + + $diffInterval = $this->diff($other); + + $diffIntervalArray = array( + array('value' => $diffInterval->y, 'unit' => 'year', 'unitShort' => 'y'), + array('value' => $diffInterval->m, 'unit' => 'month', 'unitShort' => 'm'), + array('value' => $diffInterval->d, 'unit' => 'day', 'unitShort' => 'd'), + array('value' => $diffInterval->h, 'unit' => 'hour', 'unitShort' => 'h'), + array('value' => $diffInterval->i, 'unit' => 'minute', 'unitShort' => 'min'), + array('value' => $diffInterval->s, 'unit' => 'second', 'unitShort' => 's'), + ); + + foreach ($diffIntervalArray as $diffIntervalData) { + if ($diffIntervalData['value'] > 0) { + $unit = $short ? $diffIntervalData['unitShort'] : $diffIntervalData['unit']; + $count = $diffIntervalData['value']; + + if ($diffIntervalData['unit'] === 'day' && $count >= static::DAYS_PER_WEEK) { + $unit = $short ? 'w' : 'week'; + $count = (int) ($count / static::DAYS_PER_WEEK); + + $interval[] = static::translator()->transChoice($unit, $count, array(':count' => $count)); + + // get the count days excluding weeks (might be zero) + $numOfDaysCount = (int) ($diffIntervalData['value'] - ($count * static::DAYS_PER_WEEK)); + + if ($numOfDaysCount > 0 && count($interval) < $parts) { + $unit = $short ? 'd' : 'day'; + $count = $numOfDaysCount; + $interval[] = static::translator()->transChoice($unit, $count, array(':count' => $count)); + } + } else { + $interval[] = static::translator()->transChoice($unit, $count, array(':count' => $count)); + } + } + + // break the loop after we get the required number of parts in array + if (count($interval) >= $parts) { + break; + } + } + + if (count($interval) === 0) { + if ($isNow && static::getHumanDiffOptions() & self::JUST_NOW) { + $key = 'diff_now'; + $translation = static::translator()->trans($key); + if ($translation !== $key) { + return $translation; + } + } + $count = static::getHumanDiffOptions() & self::NO_ZERO_DIFF ? 1 : 0; + $unit = $short ? 's' : 'second'; + $interval[] = static::translator()->transChoice($unit, $count, array(':count' => $count)); + } + + // join the interval parts by a space + $time = implode(' ', $interval); + + unset($diffIntervalArray, $interval); + + if ($absolute) { + return $time; + } + + $isFuture = $diffInterval->invert === 1; + + $transId = $isNow ? ($isFuture ? 'from_now' : 'ago') : ($isFuture ? 'after' : 'before'); + + if ($parts === 1) { + if ($isNow && $unit === 'day') { + if ($count === 1 && static::getHumanDiffOptions() & self::ONE_DAY_WORDS) { + $key = $isFuture ? 'diff_tomorrow' : 'diff_yesterday'; + $translation = static::translator()->trans($key); + if ($translation !== $key) { + return $translation; + } + } + if ($count === 2 && static::getHumanDiffOptions() & self::TWO_DAY_WORDS) { + $key = $isFuture ? 'diff_after_tomorrow' : 'diff_before_yesterday'; + $translation = static::translator()->trans($key); + if ($translation !== $key) { + return $translation; + } + } + } + // Some languages have special pluralization for past and future tense. + $key = $unit.'_'.$transId; + if ($key !== static::translator()->transChoice($key, $count)) { + $time = static::translator()->transChoice($key, $count, array(':count' => $count)); + } + } + + return static::translator()->trans($transId, array(':time' => $time)); + } + + /////////////////////////////////////////////////////////////////// + //////////////////////////// MODIFIERS //////////////////////////// + /////////////////////////////////////////////////////////////////// + + /** + * Resets the time to 00:00:00 start of day + * + * @return static + */ + public function startOfDay() + { + return $this->modify('00:00:00.000000'); + } + + /** + * Resets the time to 23:59:59 end of day + * + * @return static + */ + public function endOfDay() + { + return $this->modify('23:59:59.999999'); + } + + /** + * Resets the date to the first day of the month and the time to 00:00:00 + * + * @return static + */ + public function startOfMonth() + { + return $this->setDate($this->year, $this->month, 1)->startOfDay(); + } + + /** + * Resets the date to end of the month and time to 23:59:59 + * + * @return static + */ + public function endOfMonth() + { + return $this->setDate($this->year, $this->month, $this->daysInMonth)->endOfDay(); + } + + /** + * Resets the date to the first day of the quarter and the time to 00:00:00 + * + * @return static + */ + public function startOfQuarter() + { + $month = ($this->quarter - 1) * static::MONTHS_PER_QUARTER + 1; + + return $this->setDate($this->year, $month, 1)->startOfDay(); + } + + /** + * Resets the date to end of the quarter and time to 23:59:59 + * + * @return static + */ + public function endOfQuarter() + { + return $this->startOfQuarter()->addMonths(static::MONTHS_PER_QUARTER - 1)->endOfMonth(); + } + + /** + * Resets the date to the first day of the year and the time to 00:00:00 + * + * @return static + */ + public function startOfYear() + { + return $this->setDate($this->year, 1, 1)->startOfDay(); + } + + /** + * Resets the date to end of the year and time to 23:59:59 + * + * @return static + */ + public function endOfYear() + { + return $this->setDate($this->year, 12, 31)->endOfDay(); + } + + /** + * Resets the date to the first day of the decade and the time to 00:00:00 + * + * @return static + */ + public function startOfDecade() + { + $year = $this->year - $this->year % static::YEARS_PER_DECADE; + + return $this->setDate($year, 1, 1)->startOfDay(); + } + + /** + * Resets the date to end of the decade and time to 23:59:59 + * + * @return static + */ + public function endOfDecade() + { + $year = $this->year - $this->year % static::YEARS_PER_DECADE + static::YEARS_PER_DECADE - 1; + + return $this->setDate($year, 12, 31)->endOfDay(); + } + + /** + * Resets the date to the first day of the century and the time to 00:00:00 + * + * @return static + */ + public function startOfCentury() + { + $year = $this->year - ($this->year - 1) % static::YEARS_PER_CENTURY; + + return $this->setDate($year, 1, 1)->startOfDay(); + } + + /** + * Resets the date to end of the century and time to 23:59:59 + * + * @return static + */ + public function endOfCentury() + { + $year = $this->year - 1 - ($this->year - 1) % static::YEARS_PER_CENTURY + static::YEARS_PER_CENTURY; + + return $this->setDate($year, 12, 31)->endOfDay(); + } + + /** + * Resets the date to the first day of week (defined in $weekStartsAt) and the time to 00:00:00 + * + * @return static + */ + public function startOfWeek() + { + while ($this->dayOfWeek !== static::$weekStartsAt) { + $this->subDay(); + } + + return $this->startOfDay(); + } + + /** + * Resets the date to end of week (defined in $weekEndsAt) and time to 23:59:59 + * + * @return static + */ + public function endOfWeek() + { + while ($this->dayOfWeek !== static::$weekEndsAt) { + $this->addDay(); + } + + return $this->endOfDay(); + } + + /** + * Modify to start of current hour, minutes and seconds become 0 + * + * @return static + */ + public function startOfHour() + { + return $this->setTime($this->hour, 0, 0); + } + + /** + * Modify to end of current hour, minutes and seconds become 59 + * + * @return static + */ + public function endOfHour() + { + return $this->modify("$this->hour:59:59.999999"); + } + + /** + * Modify to start of current minute, seconds become 0 + * + * @return static + */ + public function startOfMinute() + { + return $this->setTime($this->hour, $this->minute, 0); + } + + /** + * Modify to end of current minute, seconds become 59 + * + * @return static + */ + public function endOfMinute() + { + return $this->modify("$this->hour:$this->minute:59.999999"); + } + + /** + * Modify to start of current minute, seconds become 0 + * + * @return static + */ + public function startOfSecond() + { + return $this->modify("$this->hour:$this->minute:$this->second.0"); + } + + /** + * Modify to end of current minute, seconds become 59 + * + * @return static + */ + public function endOfSecond() + { + return $this->modify("$this->hour:$this->minute:$this->second.999999"); + } + + /** + * Modify to midday, default to self::$midDayAt + * + * @return static + */ + public function midDay() + { + return $this->setTime(self::$midDayAt, 0, 0); + } + + /** + * Modify to the next occurrence of a given day of the week. + * If no dayOfWeek is provided, modify to the next occurrence + * of the current day of the week. Use the supplied constants + * to indicate the desired dayOfWeek, ex. static::MONDAY. + * + * @param int|null $dayOfWeek + * + * @return static + */ + public function next($dayOfWeek = null) + { + if ($dayOfWeek === null) { + $dayOfWeek = $this->dayOfWeek; + } + + return $this->startOfDay()->modify('next '.static::$days[$dayOfWeek]); + } + + /** + * Go forward or backward to the next week- or weekend-day. + * + * @param bool $weekday + * @param bool $forward + * + * @return $this + */ + private function nextOrPreviousDay($weekday = true, $forward = true) + { + $step = $forward ? 1 : -1; + + do { + $this->addDay($step); + } while ($weekday ? $this->isWeekend() : $this->isWeekday()); + + return $this; + } + + /** + * Go forward to the next weekday. + * + * @return $this + */ + public function nextWeekday() + { + return $this->nextOrPreviousDay(); + } + + /** + * Go backward to the previous weekday. + * + * @return $this + */ + public function previousWeekday() + { + return $this->nextOrPreviousDay(true, false); + } + + /** + * Go forward to the next weekend day. + * + * @return $this + */ + public function nextWeekendDay() + { + return $this->nextOrPreviousDay(false); + } + + /** + * Go backward to the previous weekend day. + * + * @return $this + */ + public function previousWeekendDay() + { + return $this->nextOrPreviousDay(false, false); + } + + /** + * Modify to the previous occurrence of a given day of the week. + * If no dayOfWeek is provided, modify to the previous occurrence + * of the current day of the week. Use the supplied constants + * to indicate the desired dayOfWeek, ex. static::MONDAY. + * + * @param int|null $dayOfWeek + * + * @return static + */ + public function previous($dayOfWeek = null) + { + if ($dayOfWeek === null) { + $dayOfWeek = $this->dayOfWeek; + } + + return $this->startOfDay()->modify('last '.static::$days[$dayOfWeek]); + } + + /** + * Modify to the first occurrence of a given day of the week + * in the current month. If no dayOfWeek is provided, modify to the + * first day of the current month. Use the supplied constants + * to indicate the desired dayOfWeek, ex. static::MONDAY. + * + * @param int|null $dayOfWeek + * + * @return static + */ + public function firstOfMonth($dayOfWeek = null) + { + $this->startOfDay(); + + if ($dayOfWeek === null) { + return $this->day(1); + } + + return $this->modify('first '.static::$days[$dayOfWeek].' of '.$this->format('F').' '.$this->year); + } + + /** + * Modify to the last occurrence of a given day of the week + * in the current month. If no dayOfWeek is provided, modify to the + * last day of the current month. Use the supplied constants + * to indicate the desired dayOfWeek, ex. static::MONDAY. + * + * @param int|null $dayOfWeek + * + * @return static + */ + public function lastOfMonth($dayOfWeek = null) + { + $this->startOfDay(); + + if ($dayOfWeek === null) { + return $this->day($this->daysInMonth); + } + + return $this->modify('last '.static::$days[$dayOfWeek].' of '.$this->format('F').' '.$this->year); + } + + /** + * Modify to the given occurrence of a given day of the week + * in the current month. If the calculated occurrence is outside the scope + * of the current month, then return false and no modifications are made. + * Use the supplied constants to indicate the desired dayOfWeek, ex. static::MONDAY. + * + * @param int $nth + * @param int $dayOfWeek + * + * @return mixed + */ + public function nthOfMonth($nth, $dayOfWeek) + { + $date = $this->copy()->firstOfMonth(); + $check = $date->format('Y-m'); + $date->modify('+'.$nth.' '.static::$days[$dayOfWeek]); + + return $date->format('Y-m') === $check ? $this->modify($date) : false; + } + + /** + * Modify to the first occurrence of a given day of the week + * in the current quarter. If no dayOfWeek is provided, modify to the + * first day of the current quarter. Use the supplied constants + * to indicate the desired dayOfWeek, ex. static::MONDAY. + * + * @param int|null $dayOfWeek day of the week default null + * + * @return static + */ + public function firstOfQuarter($dayOfWeek = null) + { + return $this->setDate($this->year, $this->quarter * static::MONTHS_PER_QUARTER - 2, 1)->firstOfMonth($dayOfWeek); + } + + /** + * Modify to the last occurrence of a given day of the week + * in the current quarter. If no dayOfWeek is provided, modify to the + * last day of the current quarter. Use the supplied constants + * to indicate the desired dayOfWeek, ex. static::MONDAY. + * + * @param int|null $dayOfWeek day of the week default null + * + * @return static + */ + public function lastOfQuarter($dayOfWeek = null) + { + return $this->setDate($this->year, $this->quarter * static::MONTHS_PER_QUARTER, 1)->lastOfMonth($dayOfWeek); + } + + /** + * Modify to the given occurrence of a given day of the week + * in the current quarter. If the calculated occurrence is outside the scope + * of the current quarter, then return false and no modifications are made. + * Use the supplied constants to indicate the desired dayOfWeek, ex. static::MONDAY. + * + * @param int $nth + * @param int $dayOfWeek + * + * @return mixed + */ + public function nthOfQuarter($nth, $dayOfWeek) + { + $date = $this->copy()->day(1)->month($this->quarter * static::MONTHS_PER_QUARTER); + $lastMonth = $date->month; + $year = $date->year; + $date->firstOfQuarter()->modify('+'.$nth.' '.static::$days[$dayOfWeek]); + + return ($lastMonth < $date->month || $year !== $date->year) ? false : $this->modify($date); + } + + /** + * Modify to the first occurrence of a given day of the week + * in the current year. If no dayOfWeek is provided, modify to the + * first day of the current year. Use the supplied constants + * to indicate the desired dayOfWeek, ex. static::MONDAY. + * + * @param int|null $dayOfWeek day of the week default null + * + * @return static + */ + public function firstOfYear($dayOfWeek = null) + { + return $this->month(1)->firstOfMonth($dayOfWeek); + } + + /** + * Modify to the last occurrence of a given day of the week + * in the current year. If no dayOfWeek is provided, modify to the + * last day of the current year. Use the supplied constants + * to indicate the desired dayOfWeek, ex. static::MONDAY. + * + * @param int|null $dayOfWeek day of the week default null + * + * @return static + */ + public function lastOfYear($dayOfWeek = null) + { + return $this->month(static::MONTHS_PER_YEAR)->lastOfMonth($dayOfWeek); + } + + /** + * Modify to the given occurrence of a given day of the week + * in the current year. If the calculated occurrence is outside the scope + * of the current year, then return false and no modifications are made. + * Use the supplied constants to indicate the desired dayOfWeek, ex. static::MONDAY. + * + * @param int $nth + * @param int $dayOfWeek + * + * @return mixed + */ + public function nthOfYear($nth, $dayOfWeek) + { + $date = $this->copy()->firstOfYear()->modify('+'.$nth.' '.static::$days[$dayOfWeek]); + + return $this->year === $date->year ? $this->modify($date) : false; + } + + /** + * Modify the current instance to the average of a given instance (default now) and the current instance. + * + * @param \Carbon\Carbon|\DateTimeInterface|string|null $date + * + * @return static + */ + public function average($date = null) + { + return $this->addSeconds((int) ($this->diffInSeconds($this->resolveCarbon($date), false) / 2)); + } + + /////////////////////////////////////////////////////////////////// + /////////////////////////// SERIALIZATION ///////////////////////// + /////////////////////////////////////////////////////////////////// + + /** + * Return a serialized string of the instance. + * + * @return string + */ + public function serialize() + { + return serialize($this); + } + + /** + * Create an instance from a serialized string. + * + * @param string $value + * + * @throws \InvalidArgumentException + * + * @return static + */ + public static function fromSerialized($value) + { + $instance = @unserialize($value); + + if (!$instance instanceof static) { + throw new InvalidArgumentException('Invalid serialized value.'); + } + + return $instance; + } + + /** + * The __set_state handler. + * + * @param array $array + * + * @return static + */ + public static function __set_state($array) + { + return static::instance(parent::__set_state($array)); + } + + /** + * Prepare the object for JSON serialization. + * + * @return array|string + */ + public function jsonSerialize() + { + if (static::$serializer) { + return call_user_func(static::$serializer, $this); + } + + $carbon = $this; + + return call_user_func(function () use ($carbon) { + return get_object_vars($carbon); + }); + } + + /** + * JSON serialize all Carbon instances using the given callback. + * + * @param callable $callback + * + * @return void + */ + public static function serializeUsing($callback) + { + static::$serializer = $callback; + } + + /////////////////////////////////////////////////////////////////// + /////////////////////////////// MACRO ///////////////////////////// + /////////////////////////////////////////////////////////////////// + + /** + * Register a custom macro. + * + * @param string $name + * @param object|callable $macro + * + * @return void + */ + public static function macro($name, $macro) + { + static::$localMacros[$name] = $macro; + } + + /** + * Mix another object into the class. + * + * @param object $mixin + * + * @return void + */ + public static function mixin($mixin) + { + $reflection = new \ReflectionClass($mixin); + $methods = $reflection->getMethods( + \ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED + ); + + foreach ($methods as $method) { + $method->setAccessible(true); + + static::macro($method->name, $method->invoke($mixin)); + } + } + + /** + * Checks if macro is registered. + * + * @param string $name + * + * @return bool + */ + public static function hasMacro($name) + { + return isset(static::$localMacros[$name]); + } + + /** + * Dynamically handle calls to the class. + * + * @param string $method + * @param array $parameters + * + * @throws \BadMethodCallException + * + * @return mixed + */ + public static function __callStatic($method, $parameters) + { + if (!static::hasMacro($method)) { + throw new \BadMethodCallException("Method $method does not exist."); + } + + if (static::$localMacros[$method] instanceof Closure && method_exists('Closure', 'bind')) { + return call_user_func_array(Closure::bind(static::$localMacros[$method], null, get_called_class()), $parameters); + } + + return call_user_func_array(static::$localMacros[$method], $parameters); + } + + /** + * Dynamically handle calls to the class. + * + * @param string $method + * @param array $parameters + * + * @throws \BadMethodCallException|\ReflectionException + * + * @return mixed + */ + public function __call($method, $parameters) + { + if (!static::hasMacro($method)) { + throw new \BadMethodCallException("Method $method does not exist."); + } + + $macro = static::$localMacros[$method]; + + $reflexion = new \ReflectionFunction($macro); + $reflectionParameters = $reflexion->getParameters(); + $expectedCount = count($reflectionParameters); + $actualCount = count($parameters); + if ($expectedCount > $actualCount && $reflectionParameters[$expectedCount - 1]->name === 'self') { + for ($i = $actualCount; $i < $expectedCount - 1; $i++) { + $parameters[] = $reflectionParameters[$i]->getDefaultValue(); + } + $parameters[] = $this; + } + + if ($macro instanceof Closure && method_exists($macro, 'bindTo')) { + return call_user_func_array($macro->bindTo($this, get_class($this)), $parameters); + } + + return call_user_func_array($macro, $parameters); + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/CarbonInterval.php b/vendor/nesbot/carbon/src/Carbon/CarbonInterval.php new file mode 100644 index 000000000..a4d785005 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/CarbonInterval.php @@ -0,0 +1,1130 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon; + +use Closure; +use DateInterval; +use InvalidArgumentException; +use ReflectionClass; +use ReflectionFunction; +use ReflectionMethod; +use Symfony\Component\Translation\TranslatorInterface; + +/** + * A simple API extension for DateInterval. + * The implementation provides helpers to handle weeks but only days are saved. + * Weeks are calculated based on the total days of the current instance. + * + * @property int $years Total years of the current interval. + * @property int $months Total months of the current interval. + * @property int $weeks Total weeks of the current interval calculated from the days. + * @property int $dayz Total days of the current interval (weeks * 7 + days). + * @property int $hours Total hours of the current interval. + * @property int $minutes Total minutes of the current interval. + * @property int $seconds Total seconds of the current interval. + * @property-read int $dayzExcludeWeeks Total days remaining in the final week of the current instance (days % 7). + * @property-read int $daysExcludeWeeks alias of dayzExcludeWeeks + * @property-read float $totalYears Number of years equivalent to the interval. + * @property-read float $totalMonths Number of months equivalent to the interval. + * @property-read float $totalWeeks Number of weeks equivalent to the interval. + * @property-read float $totalDays Number of days equivalent to the interval. + * @property-read float $totalDayz Alias for totalDays. + * @property-read float $totalHours Number of hours equivalent to the interval. + * @property-read float $totalMinutes Number of minutes equivalent to the interval. + * @property-read float $totalSeconds Number of seconds equivalent to the interval. + * + * @method static CarbonInterval years($years = 1) Create instance specifying a number of years. + * @method static CarbonInterval year($years = 1) Alias for years() + * @method static CarbonInterval months($months = 1) Create instance specifying a number of months. + * @method static CarbonInterval month($months = 1) Alias for months() + * @method static CarbonInterval weeks($weeks = 1) Create instance specifying a number of weeks. + * @method static CarbonInterval week($weeks = 1) Alias for weeks() + * @method static CarbonInterval days($days = 1) Create instance specifying a number of days. + * @method static CarbonInterval dayz($days = 1) Alias for days() + * @method static CarbonInterval day($days = 1) Alias for days() + * @method static CarbonInterval hours($hours = 1) Create instance specifying a number of hours. + * @method static CarbonInterval hour($hours = 1) Alias for hours() + * @method static CarbonInterval minutes($minutes = 1) Create instance specifying a number of minutes. + * @method static CarbonInterval minute($minutes = 1) Alias for minutes() + * @method static CarbonInterval seconds($seconds = 1) Create instance specifying a number of seconds. + * @method static CarbonInterval second($seconds = 1) Alias for seconds() + * @method CarbonInterval years($years = 1) Set the years portion of the current interval. + * @method CarbonInterval year($years = 1) Alias for years(). + * @method CarbonInterval months($months = 1) Set the months portion of the current interval. + * @method CarbonInterval month($months = 1) Alias for months(). + * @method CarbonInterval weeks($weeks = 1) Set the weeks portion of the current interval. Will overwrite dayz value. + * @method CarbonInterval week($weeks = 1) Alias for weeks(). + * @method CarbonInterval days($days = 1) Set the days portion of the current interval. + * @method CarbonInterval dayz($days = 1) Alias for days(). + * @method CarbonInterval day($days = 1) Alias for days(). + * @method CarbonInterval hours($hours = 1) Set the hours portion of the current interval. + * @method CarbonInterval hour($hours = 1) Alias for hours(). + * @method CarbonInterval minutes($minutes = 1) Set the minutes portion of the current interval. + * @method CarbonInterval minute($minutes = 1) Alias for minutes(). + * @method CarbonInterval seconds($seconds = 1) Set the seconds portion of the current interval. + * @method CarbonInterval second($seconds = 1) Alias for seconds(). + */ +class CarbonInterval extends DateInterval +{ + /** + * Interval spec period designators + */ + const PERIOD_PREFIX = 'P'; + const PERIOD_YEARS = 'Y'; + const PERIOD_MONTHS = 'M'; + const PERIOD_DAYS = 'D'; + const PERIOD_TIME_PREFIX = 'T'; + const PERIOD_HOURS = 'H'; + const PERIOD_MINUTES = 'M'; + const PERIOD_SECONDS = 'S'; + + /** + * A translator to ... er ... translate stuff + * + * @var \Symfony\Component\Translation\TranslatorInterface + */ + protected static $translator; + + /** + * @var array|null + */ + protected static $cascadeFactors; + + /** + * @var array|null + */ + private static $flipCascadeFactors; + + /** + * The registered macros. + * + * @var array + */ + protected static $macros = array(); + + /** + * Before PHP 5.4.20/5.5.4 instead of FALSE days will be set to -99999 when the interval instance + * was created by DateTime::diff(). + */ + const PHP_DAYS_FALSE = -99999; + + /** + * Mapping of units and factors for cascading. + * + * Should only be modified by changing the factors or referenced constants. + * + * @return array + */ + public static function getCascadeFactors() + { + return static::$cascadeFactors ?: array( + 'minutes' => array(Carbon::SECONDS_PER_MINUTE, 'seconds'), + 'hours' => array(Carbon::MINUTES_PER_HOUR, 'minutes'), + 'dayz' => array(Carbon::HOURS_PER_DAY, 'hours'), + 'months' => array(Carbon::DAYS_PER_WEEK * Carbon::WEEKS_PER_MONTH, 'dayz'), + 'years' => array(Carbon::MONTHS_PER_YEAR, 'months'), + ); + } + + private static function standardizeUnit($unit) + { + $unit = rtrim($unit, 'sz').'s'; + + return $unit === 'days' ? 'dayz' : $unit; + } + + private static function getFlipCascadeFactors() + { + if (!self::$flipCascadeFactors) { + self::$flipCascadeFactors = array(); + foreach (static::getCascadeFactors() as $to => $tuple) { + list($factor, $from) = $tuple; + + self::$flipCascadeFactors[self::standardizeUnit($from)] = array(self::standardizeUnit($to), $factor); + } + } + + return self::$flipCascadeFactors; + } + + /** + * @param array $cascadeFactors + */ + public static function setCascadeFactors(array $cascadeFactors) + { + self::$flipCascadeFactors = null; + static::$cascadeFactors = $cascadeFactors; + } + + /** + * Determine if the interval was created via DateTime:diff() or not. + * + * @param DateInterval $interval + * + * @return bool + */ + private static function wasCreatedFromDiff(DateInterval $interval) + { + return $interval->days !== false && $interval->days !== static::PHP_DAYS_FALSE; + } + + /////////////////////////////////////////////////////////////////// + //////////////////////////// CONSTRUCTORS ///////////////////////// + /////////////////////////////////////////////////////////////////// + + /** + * Create a new CarbonInterval instance. + * + * @param int $years + * @param int $months + * @param int $weeks + * @param int $days + * @param int $hours + * @param int $minutes + * @param int $seconds + */ + public function __construct($years = 1, $months = null, $weeks = null, $days = null, $hours = null, $minutes = null, $seconds = null) + { + $spec = $years; + + if (!is_string($spec) || floatval($years) || preg_match('/^[0-9.]/', $years)) { + $spec = static::PERIOD_PREFIX; + + $spec .= $years > 0 ? $years.static::PERIOD_YEARS : ''; + $spec .= $months > 0 ? $months.static::PERIOD_MONTHS : ''; + + $specDays = 0; + $specDays += $weeks > 0 ? $weeks * static::getDaysPerWeek() : 0; + $specDays += $days > 0 ? $days : 0; + + $spec .= $specDays > 0 ? $specDays.static::PERIOD_DAYS : ''; + + if ($hours > 0 || $minutes > 0 || $seconds > 0) { + $spec .= static::PERIOD_TIME_PREFIX; + $spec .= $hours > 0 ? $hours.static::PERIOD_HOURS : ''; + $spec .= $minutes > 0 ? $minutes.static::PERIOD_MINUTES : ''; + $spec .= $seconds > 0 ? $seconds.static::PERIOD_SECONDS : ''; + } + + if ($spec === static::PERIOD_PREFIX) { + // Allow the zero interval. + $spec .= '0'.static::PERIOD_YEARS; + } + } + + parent::__construct($spec); + } + + /** + * Returns the factor for a given source-to-target couple. + * + * @param string $source + * @param string $target + * + * @return int|null + */ + public static function getFactor($source, $target) + { + $source = self::standardizeUnit($source); + $target = self::standardizeUnit($target); + $factors = static::getFlipCascadeFactors(); + if (isset($factors[$source])) { + list($to, $factor) = $factors[$source]; + if ($to === $target) { + return $factor; + } + } + + return null; + } + + /** + * Returns current config for days per week. + * + * @return int + */ + public static function getDaysPerWeek() + { + return static::getFactor('dayz', 'weeks') ?: Carbon::DAYS_PER_WEEK; + } + + /** + * Returns current config for hours per day. + * + * @return int + */ + public static function getHoursPerDay() + { + return static::getFactor('hours', 'dayz') ?: Carbon::HOURS_PER_DAY; + } + + /** + * Returns current config for minutes per hour. + * + * @return int + */ + public static function getMinutesPerHours() + { + return static::getFactor('minutes', 'hours') ?: Carbon::MINUTES_PER_HOUR; + } + + /** + * Returns current config for seconds per minute. + * + * @return int + */ + public static function getSecondsPerMinutes() + { + return static::getFactor('seconds', 'minutes') ?: Carbon::SECONDS_PER_MINUTE; + } + + /** + * Create a new CarbonInterval instance from specific values. + * This is an alias for the constructor that allows better fluent + * syntax as it allows you to do CarbonInterval::create(1)->fn() rather than + * (new CarbonInterval(1))->fn(). + * + * @param int $years + * @param int $months + * @param int $weeks + * @param int $days + * @param int $hours + * @param int $minutes + * @param int $seconds + * + * @return static + */ + public static function create($years = 1, $months = null, $weeks = null, $days = null, $hours = null, $minutes = null, $seconds = null) + { + return new static($years, $months, $weeks, $days, $hours, $minutes, $seconds); + } + + /** + * Get a copy of the instance. + * + * @return static + */ + public function copy() + { + $date = new static($this->spec()); + $date->invert = $this->invert; + + return $date; + } + + /** + * Provide static helpers to create instances. Allows CarbonInterval::years(3). + * + * Note: This is done using the magic method to allow static and instance methods to + * have the same names. + * + * @param string $name + * @param array $args + * + * @return static + */ + public static function __callStatic($name, $args) + { + $arg = count($args) === 0 ? 1 : $args[0]; + + switch ($name) { + case 'years': + case 'year': + return new static($arg); + + case 'months': + case 'month': + return new static(null, $arg); + + case 'weeks': + case 'week': + return new static(null, null, $arg); + + case 'days': + case 'dayz': + case 'day': + return new static(null, null, null, $arg); + + case 'hours': + case 'hour': + return new static(null, null, null, null, $arg); + + case 'minutes': + case 'minute': + return new static(null, null, null, null, null, $arg); + + case 'seconds': + case 'second': + return new static(null, null, null, null, null, null, $arg); + } + + if (static::hasMacro($name)) { + return call_user_func_array( + array(new static(0), $name), $args + ); + } + } + + /** + * Creates a CarbonInterval from string. + * + * Format: + * + * Suffix | Unit | Example | DateInterval expression + * -------|---------|---------|------------------------ + * y | years | 1y | P1Y + * mo | months | 3mo | P3M + * w | weeks | 2w | P2W + * d | days | 28d | P28D + * h | hours | 4h | PT4H + * m | minutes | 12m | PT12M + * s | seconds | 59s | PT59S + * + * e. g. `1w 3d 4h 32m 23s` is converted to 10 days 4 hours 32 minutes and 23 seconds. + * + * Special cases: + * - An empty string will return a zero interval + * - Fractions are allowed for weeks, days, hours and minutes and will be converted + * and rounded to the next smaller value (caution: 0.5w = 4d) + * + * @param string $intervalDefinition + * + * @return static + */ + public static function fromString($intervalDefinition) + { + if (empty($intervalDefinition)) { + return new static(0); + } + + $years = 0; + $months = 0; + $weeks = 0; + $days = 0; + $hours = 0; + $minutes = 0; + $seconds = 0; + + $pattern = '/(\d+(?:\.\d+)?)\h*([^\d\h]*)/i'; + preg_match_all($pattern, $intervalDefinition, $parts, PREG_SET_ORDER); + while ($match = array_shift($parts)) { + list($part, $value, $unit) = $match; + $intValue = intval($value); + $fraction = floatval($value) - $intValue; + switch (strtolower($unit)) { + case 'year': + case 'years': + case 'y': + $years += $intValue; + break; + + case 'month': + case 'months': + case 'mo': + $months += $intValue; + break; + + case 'week': + case 'weeks': + case 'w': + $weeks += $intValue; + if ($fraction) { + $parts[] = array(null, $fraction * static::getDaysPerWeek(), 'd'); + } + break; + + case 'day': + case 'days': + case 'd': + $days += $intValue; + if ($fraction) { + $parts[] = array(null, $fraction * static::getHoursPerDay(), 'h'); + } + break; + + case 'hour': + case 'hours': + case 'h': + $hours += $intValue; + if ($fraction) { + $parts[] = array(null, $fraction * static::getMinutesPerHours(), 'm'); + } + break; + + case 'minute': + case 'minutes': + case 'm': + $minutes += $intValue; + if ($fraction) { + $seconds += round($fraction * static::getSecondsPerMinutes()); + } + break; + + case 'second': + case 'seconds': + case 's': + $seconds += $intValue; + break; + + default: + throw new InvalidArgumentException( + sprintf('Invalid part %s in definition %s', $part, $intervalDefinition) + ); + } + } + + return new static($years, $months, $weeks, $days, $hours, $minutes, $seconds); + } + + /** + * Create a CarbonInterval instance from a DateInterval one. Can not instance + * DateInterval objects created from DateTime::diff() as you can't externally + * set the $days field. + * + * @param DateInterval $di + * + * @return static + */ + public static function instance(DateInterval $di) + { + $instance = new static(static::getDateIntervalSpec($di)); + $instance->invert = $di->invert; + + return $instance; + } + + /** + * Make a CarbonInterval instance from given variable if possible. + * + * Always return a new instance. Parse only strings and only these likely to be intervals (skip dates + * and recurrences). Throw an exception for invalid format, but otherwise return null. + * + * @param mixed $var + * + * @return static|null + */ + public static function make($var) + { + if ($var instanceof DateInterval) { + return static::instance($var); + } + + if (is_string($var)) { + $var = trim($var); + + if (substr($var, 0, 1) === 'P') { + return new static($var); + } + + if (preg_match('/^(?:\h*\d+(?:\.\d+)?\h*[a-z]+)+$/i', $var)) { + return static::fromString($var); + } + } + } + + /////////////////////////////////////////////////////////////////// + /////////////////////// LOCALIZATION ////////////////////////////// + /////////////////////////////////////////////////////////////////// + + /** + * Initialize the translator instance if necessary. + * + * @return \Symfony\Component\Translation\TranslatorInterface + */ + protected static function translator() + { + if (static::$translator === null) { + static::$translator = Translator::get(); + } + + return static::$translator; + } + + /** + * Get the translator instance in use. + * + * @return \Symfony\Component\Translation\TranslatorInterface + */ + public static function getTranslator() + { + return static::translator(); + } + + /** + * Set the translator instance to use. + * + * @param TranslatorInterface $translator + */ + public static function setTranslator(TranslatorInterface $translator) + { + static::$translator = $translator; + } + + /** + * Get the current translator locale. + * + * @return string + */ + public static function getLocale() + { + return static::translator()->getLocale(); + } + + /** + * Set the current translator locale. + * + * @param string $locale + */ + public static function setLocale($locale) + { + return static::translator()->setLocale($locale) !== false; + } + + /////////////////////////////////////////////////////////////////// + ///////////////////////// GETTERS AND SETTERS ///////////////////// + /////////////////////////////////////////////////////////////////// + + /** + * Get a part of the CarbonInterval object. + * + * @param string $name + * + * @throws \InvalidArgumentException + * + * @return int|float + */ + public function __get($name) + { + if (substr($name, 0, 5) === 'total') { + return $this->total(substr($name, 5)); + } + + switch ($name) { + case 'years': + return $this->y; + + case 'months': + return $this->m; + + case 'dayz': + return $this->d; + + case 'hours': + return $this->h; + + case 'minutes': + return $this->i; + + case 'seconds': + return $this->s; + + case 'weeks': + return (int) floor($this->d / static::getDaysPerWeek()); + + case 'daysExcludeWeeks': + case 'dayzExcludeWeeks': + return $this->d % static::getDaysPerWeek(); + + default: + throw new InvalidArgumentException(sprintf("Unknown getter '%s'", $name)); + } + } + + /** + * Set a part of the CarbonInterval object. + * + * @param string $name + * @param int $val + * + * @throws \InvalidArgumentException + */ + public function __set($name, $val) + { + switch ($name) { + case 'years': + $this->y = $val; + break; + + case 'months': + $this->m = $val; + break; + + case 'weeks': + $this->d = $val * static::getDaysPerWeek(); + break; + + case 'dayz': + $this->d = $val; + break; + + case 'hours': + $this->h = $val; + break; + + case 'minutes': + $this->i = $val; + break; + + case 'seconds': + $this->s = $val; + break; + } + } + + /** + * Allow setting of weeks and days to be cumulative. + * + * @param int $weeks Number of weeks to set + * @param int $days Number of days to set + * + * @return static + */ + public function weeksAndDays($weeks, $days) + { + $this->dayz = ($weeks * static::getDaysPerWeek()) + $days; + + return $this; + } + + /** + * Register a custom macro. + * + * @param string $name + * @param object|callable $macro + * + * @return void + */ + public static function macro($name, $macro) + { + static::$macros[$name] = $macro; + } + + /** + * Register macros from a mixin object. + * + * @param object $mixin + * + * @throws \ReflectionException + * + * @return void + */ + public static function mixin($mixin) + { + $reflection = new ReflectionClass($mixin); + + $methods = $reflection->getMethods( + ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED + ); + + foreach ($methods as $method) { + $method->setAccessible(true); + + static::macro($method->name, $method->invoke($mixin)); + } + } + + /** + * Check if macro is registered. + * + * @param string $name + * + * @return bool + */ + public static function hasMacro($name) + { + return isset(static::$macros[$name]); + } + + /** + * Call given macro. + * + * @param string $name + * @param array $parameters + * + * @return mixed + */ + protected function callMacro($name, $parameters) + { + $macro = static::$macros[$name]; + + $reflection = new ReflectionFunction($macro); + + $reflectionParameters = $reflection->getParameters(); + + $expectedCount = count($reflectionParameters); + $actualCount = count($parameters); + + if ($expectedCount > $actualCount && $reflectionParameters[$expectedCount - 1]->name === 'self') { + for ($i = $actualCount; $i < $expectedCount - 1; $i++) { + $parameters[] = $reflectionParameters[$i]->getDefaultValue(); + } + + $parameters[] = $this; + } + + if ($macro instanceof Closure && method_exists($macro, 'bindTo')) { + $macro = $macro->bindTo($this, get_class($this)); + } + + return call_user_func_array($macro, $parameters); + } + + /** + * Allow fluent calls on the setters... CarbonInterval::years(3)->months(5)->day(). + * + * Note: This is done using the magic method to allow static and instance methods to + * have the same names. + * + * @param string $name + * @param array $args + * + * @return static + */ + public function __call($name, $args) + { + if (static::hasMacro($name)) { + return $this->callMacro($name, $args); + } + + $arg = count($args) === 0 ? 1 : $args[0]; + + switch ($name) { + case 'years': + case 'year': + $this->years = $arg; + break; + + case 'months': + case 'month': + $this->months = $arg; + break; + + case 'weeks': + case 'week': + $this->dayz = $arg * static::getDaysPerWeek(); + break; + + case 'days': + case 'dayz': + case 'day': + $this->dayz = $arg; + break; + + case 'hours': + case 'hour': + $this->hours = $arg; + break; + + case 'minutes': + case 'minute': + $this->minutes = $arg; + break; + + case 'seconds': + case 'second': + $this->seconds = $arg; + break; + } + + return $this; + } + + /** + * Get the current interval in a human readable format in the current locale. + * + * @param bool $short (false by default), returns short units if true + * + * @return string + */ + public function forHumans($short = false) + { + $periods = array( + 'year' => array('y', $this->years), + 'month' => array('m', $this->months), + 'week' => array('w', $this->weeks), + 'day' => array('d', $this->daysExcludeWeeks), + 'hour' => array('h', $this->hours), + 'minute' => array('min', $this->minutes), + 'second' => array('s', $this->seconds), + ); + + $parts = array(); + foreach ($periods as $unit => $options) { + list($shortUnit, $count) = $options; + if ($count > 0) { + $parts[] = static::translator()->transChoice($short ? $shortUnit : $unit, $count, array(':count' => $count)); + } + } + + return implode(' ', $parts); + } + + /** + * Format the instance as a string using the forHumans() function. + * + * @return string + */ + public function __toString() + { + return $this->forHumans(); + } + + /** + * Convert the interval to a CarbonPeriod. + * + * @return CarbonPeriod + */ + public function toPeriod() + { + return CarbonPeriod::createFromArray( + array_merge(array($this), func_get_args()) + ); + } + + /** + * Invert the interval. + * + * @return $this + */ + public function invert() + { + $this->invert = $this->invert ? 0 : 1; + + return $this; + } + + /** + * Add the passed interval to the current instance. + * + * @param DateInterval $interval + * + * @return static + */ + public function add(DateInterval $interval) + { + $sign = $interval->invert === 1 ? -1 : 1; + + if (static::wasCreatedFromDiff($interval)) { + $this->dayz += $interval->days * $sign; + } else { + $this->years += $interval->y * $sign; + $this->months += $interval->m * $sign; + $this->dayz += $interval->d * $sign; + $this->hours += $interval->h * $sign; + $this->minutes += $interval->i * $sign; + $this->seconds += $interval->s * $sign; + } + + return $this; + } + + /** + * Multiply current instance given number of times + * + * @param float $factor + * + * @return $this + */ + public function times($factor) + { + if ($factor < 0) { + $this->invert = $this->invert ? 0 : 1; + $factor = -$factor; + } + + $this->years = (int) round($this->years * $factor); + $this->months = (int) round($this->months * $factor); + $this->dayz = (int) round($this->dayz * $factor); + $this->hours = (int) round($this->hours * $factor); + $this->minutes = (int) round($this->minutes * $factor); + $this->seconds = (int) round($this->seconds * $factor); + + return $this; + } + + /** + * Get the interval_spec string of a date interval. + * + * @param DateInterval $interval + * + * @return string + */ + public static function getDateIntervalSpec(DateInterval $interval) + { + $date = array_filter(array( + static::PERIOD_YEARS => $interval->y, + static::PERIOD_MONTHS => $interval->m, + static::PERIOD_DAYS => $interval->d, + )); + + $time = array_filter(array( + static::PERIOD_HOURS => $interval->h, + static::PERIOD_MINUTES => $interval->i, + static::PERIOD_SECONDS => $interval->s, + )); + + $specString = static::PERIOD_PREFIX; + + foreach ($date as $key => $value) { + $specString .= $value.$key; + } + + if (count($time) > 0) { + $specString .= static::PERIOD_TIME_PREFIX; + foreach ($time as $key => $value) { + $specString .= $value.$key; + } + } + + return $specString === static::PERIOD_PREFIX ? 'PT0S' : $specString; + } + + /** + * Get the interval_spec string. + * + * @return string + */ + public function spec() + { + return static::getDateIntervalSpec($this); + } + + /** + * Comparing 2 date intervals. + * + * @param DateInterval $a + * @param DateInterval $b + * + * @return int + */ + public static function compareDateIntervals(DateInterval $a, DateInterval $b) + { + $current = Carbon::now(); + $passed = $current->copy()->add($b); + $current->add($a); + + if ($current < $passed) { + return -1; + } + if ($current > $passed) { + return 1; + } + + return 0; + } + + /** + * Comparing with passed interval. + * + * @param DateInterval $interval + * + * @return int + */ + public function compare(DateInterval $interval) + { + return static::compareDateIntervals($this, $interval); + } + + /** + * Convert overflowed values into bigger units. + * + * @return $this + */ + public function cascade() + { + foreach (static::getFlipCascadeFactors() as $source => $cascade) { + list($target, $factor) = $cascade; + + if ($source === 'dayz' && $target === 'weeks') { + continue; + } + + $value = $this->$source; + $this->$source = $modulo = $value % $factor; + $this->$target += ($value - $modulo) / $factor; + } + + return $this; + } + + /** + * Get amount of given unit equivalent to the interval. + * + * @param string $unit + * + * @throws \InvalidArgumentException + * + * @return float + */ + public function total($unit) + { + $realUnit = $unit = strtolower($unit); + + if (in_array($unit, array('days', 'weeks'))) { + $realUnit = 'dayz'; + } elseif (!in_array($unit, array('seconds', 'minutes', 'hours', 'dayz', 'months', 'years'))) { + throw new InvalidArgumentException("Unknown unit '$unit'."); + } + + $result = 0; + $cumulativeFactor = 0; + $unitFound = false; + + foreach (static::getFlipCascadeFactors() as $source => $cascade) { + list($target, $factor) = $cascade; + + if ($source === $realUnit) { + $unitFound = true; + $result += $this->$source; + $cumulativeFactor = 1; + } + + if ($factor === false) { + if ($unitFound) { + break; + } + + $result = 0; + $cumulativeFactor = 0; + + continue; + } + + if ($target === $realUnit) { + $unitFound = true; + } + + if ($cumulativeFactor) { + $cumulativeFactor *= $factor; + $result += $this->$target * $cumulativeFactor; + + continue; + } + + $result = ($result + $this->$source) / $factor; + } + + if (isset($target) && !$cumulativeFactor) { + $result += $this->$target; + } + + if (!$unitFound) { + throw new \InvalidArgumentException("Unit $unit have no configuration to get total from other units."); + } + + if ($unit === 'weeks') { + return $result / static::getDaysPerWeek(); + } + + return $result; + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/CarbonPeriod.php b/vendor/nesbot/carbon/src/Carbon/CarbonPeriod.php new file mode 100644 index 000000000..ae52c0b8b --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/CarbonPeriod.php @@ -0,0 +1,1445 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon; + +use BadMethodCallException; +use Closure; +use Countable; +use DateInterval; +use DateTime; +use DateTimeInterface; +use InvalidArgumentException; +use Iterator; +use ReflectionClass; +use ReflectionFunction; +use ReflectionMethod; +use RuntimeException; + +/** + * Substitution of DatePeriod with some modifications and many more features. + * Fully compatible with PHP 5.3+! + * + * @method static CarbonPeriod start($date, $inclusive = null) Create instance specifying start date. + * @method static CarbonPeriod since($date, $inclusive = null) Alias for start(). + * @method static CarbonPeriod sinceNow($inclusive = null) Create instance with start date set to now. + * @method static CarbonPeriod end($date = null, $inclusive = null) Create instance specifying end date. + * @method static CarbonPeriod until($date = null, $inclusive = null) Alias for end(). + * @method static CarbonPeriod untilNow($inclusive = null) Create instance with end date set to now. + * @method static CarbonPeriod dates($start, $end = null) Create instance with start and end date. + * @method static CarbonPeriod between($start, $end = null) Create instance with start and end date. + * @method static CarbonPeriod recurrences($recurrences = null) Create instance with maximum number of recurrences. + * @method static CarbonPeriod times($recurrences = null) Alias for recurrences(). + * @method static CarbonPeriod options($options = null) Create instance with options. + * @method static CarbonPeriod toggle($options, $state = null) Create instance with options toggled on or off. + * @method static CarbonPeriod filter($callback, $name = null) Create instance with filter added to the stack. + * @method static CarbonPeriod push($callback, $name = null) Alias for filter(). + * @method static CarbonPeriod prepend($callback, $name = null) Create instance with filter prepened to the stack. + * @method static CarbonPeriod filters(array $filters) Create instance with filters stack. + * @method static CarbonPeriod interval($interval) Create instance with given date interval. + * @method static CarbonPeriod each($interval) Create instance with given date interval. + * @method static CarbonPeriod every($interval) Create instance with given date interval. + * @method static CarbonPeriod step($interval) Create instance with given date interval. + * @method static CarbonPeriod stepBy($interval) Create instance with given date interval. + * @method static CarbonPeriod invert() Create instance with inverted date interval. + * @method static CarbonPeriod years($years = 1) Create instance specifying a number of years for date interval. + * @method static CarbonPeriod year($years = 1) Alias for years(). + * @method static CarbonPeriod months($months = 1) Create instance specifying a number of months for date interval. + * @method static CarbonPeriod month($months = 1) Alias for months(). + * @method static CarbonPeriod weeks($weeks = 1) Create instance specifying a number of weeks for date interval. + * @method static CarbonPeriod week($weeks = 1) Alias for weeks(). + * @method static CarbonPeriod days($days = 1) Create instance specifying a number of days for date interval. + * @method static CarbonPeriod dayz($days = 1) Alias for days(). + * @method static CarbonPeriod day($days = 1) Alias for days(). + * @method static CarbonPeriod hours($hours = 1) Create instance specifying a number of hours for date interval. + * @method static CarbonPeriod hour($hours = 1) Alias for hours(). + * @method static CarbonPeriod minutes($minutes = 1) Create instance specifying a number of minutes for date interval. + * @method static CarbonPeriod minute($minutes = 1) Alias for minutes(). + * @method static CarbonPeriod seconds($seconds = 1) Create instance specifying a number of seconds for date interval. + * @method static CarbonPeriod second($seconds = 1) Alias for seconds(). + * @method CarbonPeriod start($date, $inclusive = null) Change the period start date. + * @method CarbonPeriod since($date, $inclusive = null) Alias for start(). + * @method CarbonPeriod sinceNow($inclusive = null) Change the period start date to now. + * @method CarbonPeriod end($date = null, $inclusive = null) Change the period end date. + * @method CarbonPeriod until($date = null, $inclusive = null) Alias for end(). + * @method CarbonPeriod untilNow($inclusive = null) Change the period end date to now. + * @method CarbonPeriod dates($start, $end = null) Change the period start and end date. + * @method CarbonPeriod recurrences($recurrences = null) Change the maximum number of recurrences. + * @method CarbonPeriod times($recurrences = null) Alias for recurrences(). + * @method CarbonPeriod options($options = null) Change the period options. + * @method CarbonPeriod toggle($options, $state = null) Toggle given options on or off. + * @method CarbonPeriod filter($callback, $name = null) Add a filter to the stack. + * @method CarbonPeriod push($callback, $name = null) Alias for filter(). + * @method CarbonPeriod prepend($callback, $name = null) Prepend a filter to the stack. + * @method CarbonPeriod filters(array $filters = array()) Set filters stack. + * @method CarbonPeriod interval($interval) Change the period date interval. + * @method CarbonPeriod invert() Invert the period date interval. + * @method CarbonPeriod years($years = 1) Set the years portion of the date interval. + * @method CarbonPeriod year($years = 1) Alias for years(). + * @method CarbonPeriod months($months = 1) Set the months portion of the date interval. + * @method CarbonPeriod month($months = 1) Alias for months(). + * @method CarbonPeriod weeks($weeks = 1) Set the weeks portion of the date interval. + * @method CarbonPeriod week($weeks = 1) Alias for weeks(). + * @method CarbonPeriod days($days = 1) Set the days portion of the date interval. + * @method CarbonPeriod dayz($days = 1) Alias for days(). + * @method CarbonPeriod day($days = 1) Alias for days(). + * @method CarbonPeriod hours($hours = 1) Set the hours portion of the date interval. + * @method CarbonPeriod hour($hours = 1) Alias for hours(). + * @method CarbonPeriod minutes($minutes = 1) Set the minutes portion of the date interval. + * @method CarbonPeriod minute($minutes = 1) Alias for minutes(). + * @method CarbonPeriod seconds($seconds = 1) Set the seconds portion of the date interval. + * @method CarbonPeriod second($seconds = 1) Alias for seconds(). + */ +class CarbonPeriod implements Iterator, Countable +{ + /** + * Built-in filters. + * + * @var string + */ + const RECURRENCES_FILTER = 'Carbon\CarbonPeriod::filterRecurrences'; + const END_DATE_FILTER = 'Carbon\CarbonPeriod::filterEndDate'; + + /** + * Special value which can be returned by filters to end iteration. Also a filter. + * + * @var string + */ + const END_ITERATION = 'Carbon\CarbonPeriod::endIteration'; + + /** + * Available options. + * + * @var int + */ + const EXCLUDE_START_DATE = 1; + const EXCLUDE_END_DATE = 2; + + /** + * Number of maximum attempts before giving up on finding next valid date. + * + * @var int + */ + const NEXT_MAX_ATTEMPTS = 1000; + + /** + * The registered macros. + * + * @var array + */ + protected static $macros = array(); + + /** + * Underlying date interval instance. Always present, one day by default. + * + * @var CarbonInterval + */ + protected $dateInterval; + + /** + * Whether current date interval was set by default. + * + * @var bool + */ + protected $isDefaultInterval; + + /** + * The filters stack. + * + * @var array + */ + protected $filters = array(); + + /** + * Period start date. Applied on rewind. Always present, now by default. + * + * @var Carbon + */ + protected $startDate; + + /** + * Period end date. For inverted interval should be before the start date. Applied via a filter. + * + * @var Carbon|null + */ + protected $endDate; + + /** + * Limit for number of recurrences. Applied via a filter. + * + * @var int|null + */ + protected $recurrences; + + /** + * Iteration options. + * + * @var int + */ + protected $options; + + /** + * Index of current date. Always sequential, even if some dates are skipped by filters. + * Equal to null only before the first iteration. + * + * @var int + */ + protected $key; + + /** + * Current date. May temporarily hold unaccepted value when looking for a next valid date. + * Equal to null only before the first iteration. + * + * @var Carbon + */ + protected $current; + + /** + * Timezone of current date. Taken from the start date. + * + * @var \DateTimeZone|null + */ + protected $timezone; + + /** + * The cached validation result for current date. + * + * @var bool|string|null + */ + protected $validationResult; + + /** + * Create a new instance. + * + * @return static + */ + public static function create() + { + return static::createFromArray(func_get_args()); + } + + /** + * Create a new instance from an array of parameters. + * + * @param array $params + * + * @return static + */ + public static function createFromArray(array $params) + { + // PHP 5.3 equivalent of new static(...$params). + $reflection = new ReflectionClass(get_class()); + /** @var static $instance */ + $instance = $reflection->newInstanceArgs($params); + + return $instance; + } + + /** + * Create CarbonPeriod from ISO 8601 string. + * + * @param string $iso + * @param int|null $options + * + * @return static + */ + public static function createFromIso($iso, $options = null) + { + $params = static::parseIso8601($iso); + + $instance = static::createFromArray($params); + + if ($options !== null) { + $instance->setOptions($options); + } + + return $instance; + } + + /** + * Return whether given interval contains non zero value of any time unit. + * + * @param \DateInterval $interval + * + * @return bool + */ + protected static function intervalHasTime(DateInterval $interval) + { + // The array_key_exists and get_object_vars are used as a workaround to check microsecond support. + // Both isset and property_exists will fail on PHP 7.0.14 - 7.0.21 due to the following bug: + // https://bugs.php.net/bug.php?id=74852 + return $interval->h || $interval->i || $interval->s || array_key_exists('f', get_object_vars($interval)) && $interval->f; + } + + /** + * Return whether given callable is a string pointing to one of Carbon's is* methods + * and should be automatically converted to a filter callback. + * + * @param callable $callable + * + * @return bool + */ + protected static function isCarbonPredicateMethod($callable) + { + return is_string($callable) && substr($callable, 0, 2) === 'is' && (method_exists('Carbon\Carbon', $callable) || Carbon::hasMacro($callable)); + } + + /** + * Return whether given variable is an ISO 8601 specification. + * + * Note: Check is very basic, as actual validation will be done later when parsing. + * We just want to ensure that variable is not any other type of a valid parameter. + * + * @param mixed $var + * + * @return bool + */ + protected static function isIso8601($var) + { + if (!is_string($var)) { + return false; + } + + // Match slash but not within a timezone name. + $part = '[a-z]+(?:[_-][a-z]+)*'; + + preg_match("#\b$part/$part\b|(/)#i", $var, $match); + + return isset($match[1]); + } + + /** + * Parse given ISO 8601 string into an array of arguments. + * + * @param string $iso + * + * @return array + */ + protected static function parseIso8601($iso) + { + $result = array(); + + $interval = null; + $start = null; + $end = null; + + foreach (explode('/', $iso) as $key => $part) { + if ($key === 0 && preg_match('/^R([0-9]*)$/', $part, $match)) { + $parsed = strlen($match[1]) ? (int) $match[1] : null; + } elseif ($interval === null && $parsed = CarbonInterval::make($part)) { + $interval = $part; + } elseif ($start === null && $parsed = Carbon::make($part)) { + $start = $part; + } elseif ($end === null && $parsed = Carbon::make(static::addMissingParts($start, $part))) { + $end = $part; + } else { + throw new InvalidArgumentException("Invalid ISO 8601 specification: $iso."); + } + + $result[] = $parsed; + } + + return $result; + } + + /** + * Add missing parts of the target date from the soure date. + * + * @param string $source + * @param string $target + * + * @return string + */ + protected static function addMissingParts($source, $target) + { + $pattern = '/'.preg_replace('/[0-9]+/', '[0-9]+', preg_quote($target, '/')).'$/'; + + $result = preg_replace($pattern, $target, $source, 1, $count); + + return $count ? $result : $target; + } + + /** + * Register a custom macro. + * + * @param string $name + * @param object|callable $macro + * + * @return void + */ + public static function macro($name, $macro) + { + static::$macros[$name] = $macro; + } + + /** + * Register macros from a mixin object. + * + * @param object $mixin + * + * @throws \ReflectionException + * + * @return void + */ + public static function mixin($mixin) + { + $reflection = new ReflectionClass($mixin); + + $methods = $reflection->getMethods( + ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED + ); + + foreach ($methods as $method) { + $method->setAccessible(true); + + static::macro($method->name, $method->invoke($mixin)); + } + } + + /** + * Check if macro is registered. + * + * @param string $name + * + * @return bool + */ + public static function hasMacro($name) + { + return isset(static::$macros[$name]); + } + + /** + * Provide static proxy for instance aliases. + * + * @param string $method + * @param array $parameters + * + * @return mixed + */ + public static function __callStatic($method, $parameters) + { + return call_user_func_array( + array(new static, $method), $parameters + ); + } + + /** + * CarbonPeriod constructor. + * + * @throws InvalidArgumentException + */ + public function __construct() + { + // Parse and assign arguments one by one. First argument may be an ISO 8601 spec, + // which will be first parsed into parts and then processed the same way. + $arguments = func_get_args(); + + if (count($arguments) && static::isIso8601($iso = $arguments[0])) { + array_splice($arguments, 0, 1, static::parseIso8601($iso)); + } + + foreach ($arguments as $argument) { + if ($this->dateInterval === null && $parsed = CarbonInterval::make($argument)) { + $this->setDateInterval($parsed); + } elseif ($this->startDate === null && $parsed = Carbon::make($argument)) { + $this->setStartDate($parsed); + } elseif ($this->endDate === null && $parsed = Carbon::make($argument)) { + $this->setEndDate($parsed); + } elseif ($this->recurrences === null && $this->endDate === null && is_numeric($argument)) { + $this->setRecurrences($argument); + } elseif ($this->options === null && (is_int($argument) || $argument === null)) { + $this->setOptions($argument); + } else { + throw new InvalidArgumentException('Invalid constructor parameters.'); + } + } + + if ($this->startDate === null) { + $this->setStartDate(Carbon::now()); + } + + if ($this->dateInterval === null) { + $this->setDateInterval(CarbonInterval::day()); + + $this->isDefaultInterval = true; + } + + if ($this->options === null) { + $this->setOptions(0); + } + } + + /** + * Change the period date interval. + * + * @param DateInterval|string $interval + * + * @throws \InvalidArgumentException + * + * @return $this + */ + public function setDateInterval($interval) + { + if (!$interval = CarbonInterval::make($interval)) { + throw new InvalidArgumentException('Invalid interval.'); + } + + if ($interval->spec() === 'PT0S') { + throw new InvalidArgumentException('Empty interval is not accepted.'); + } + + $this->dateInterval = $interval; + + $this->isDefaultInterval = false; + + $this->handleChangedParameters(); + + return $this; + } + + /** + * Invert the period date interval. + * + * @return $this + */ + public function invertDateInterval() + { + $interval = $this->dateInterval->invert(); + + return $this->setDateInterval($interval); + } + + /** + * Set start and end date. + * + * @param DateTime|DateTimeInterface|string $start + * @param DateTime|DateTimeInterface|string|null $end + * + * @return $this + */ + public function setDates($start, $end) + { + $this->setStartDate($start); + $this->setEndDate($end); + + return $this; + } + + /** + * Change the period options. + * + * @param int|null $options + * + * @throws \InvalidArgumentException + * + * @return $this + */ + public function setOptions($options) + { + if (!is_int($options) && !is_null($options)) { + throw new InvalidArgumentException('Invalid options.'); + } + + $this->options = $options ?: 0; + + $this->handleChangedParameters(); + + return $this; + } + + /** + * Get the period options. + * + * @return int + */ + public function getOptions() + { + return $this->options; + } + + /** + * Toggle given options on or off. + * + * @param int $options + * @param bool|null $state + * + * @throws \InvalidArgumentException + * + * @return $this + */ + public function toggleOptions($options, $state = null) + { + if ($state === null) { + $state = ($this->options & $options) !== $options; + } + + return $this->setOptions($state ? + $this->options | $options : + $this->options & ~$options + ); + } + + /** + * Toggle EXCLUDE_START_DATE option. + * + * @param bool $state + * + * @return $this + */ + public function excludeStartDate($state = true) + { + return $this->toggleOptions(static::EXCLUDE_START_DATE, $state); + } + + /** + * Toggle EXCLUDE_END_DATE option. + * + * @param bool $state + * + * @return $this + */ + public function excludeEndDate($state = true) + { + return $this->toggleOptions(static::EXCLUDE_END_DATE, $state); + } + + /** + * Get the underlying date interval. + * + * @return CarbonInterval + */ + public function getDateInterval() + { + return $this->dateInterval->copy(); + } + + /** + * Get start date of the period. + * + * @return Carbon + */ + public function getStartDate() + { + return $this->startDate->copy(); + } + + /** + * Get end date of the period. + * + * @return Carbon|null + */ + public function getEndDate() + { + if ($this->endDate) { + return $this->endDate->copy(); + } + } + + /** + * Get number of recurrences. + * + * @return int|null + */ + public function getRecurrences() + { + return $this->recurrences; + } + + /** + * Returns true if the start date should be excluded. + * + * @return bool + */ + public function isStartExcluded() + { + return ($this->options & static::EXCLUDE_START_DATE) !== 0; + } + + /** + * Returns true if the end date should be excluded. + * + * @return bool + */ + public function isEndExcluded() + { + return ($this->options & static::EXCLUDE_END_DATE) !== 0; + } + + /** + * Add a filter to the stack. + * + * @param callable $callback + * @param string $name + * + * @return $this + */ + public function addFilter($callback, $name = null) + { + $tuple = $this->createFilterTuple(func_get_args()); + + $this->filters[] = $tuple; + + $this->handleChangedParameters(); + + return $this; + } + + /** + * Prepend a filter to the stack. + * + * @param callable $callback + * @param string $name + * + * @return $this + */ + public function prependFilter($callback, $name = null) + { + $tuple = $this->createFilterTuple(func_get_args()); + + array_unshift($this->filters, $tuple); + + $this->handleChangedParameters(); + + return $this; + } + + /** + * Create a filter tuple from raw parameters. + * + * Will create an automatic filter callback for one of Carbon's is* methods. + * + * @param array $parameters + * + * @return array + */ + protected function createFilterTuple(array $parameters) + { + $method = array_shift($parameters); + + if (!$this->isCarbonPredicateMethod($method)) { + return array($method, array_shift($parameters)); + } + + return array(function ($date) use ($method, $parameters) { + return call_user_func_array(array($date, $method), $parameters); + }, $method); + } + + /** + * Remove a filter by instance or name. + * + * @param callable|string $filter + * + * @return $this + */ + public function removeFilter($filter) + { + $key = is_callable($filter) ? 0 : 1; + + $this->filters = array_values(array_filter( + $this->filters, + function ($tuple) use ($key, $filter) { + return $tuple[$key] !== $filter; + } + )); + + $this->updateInternalState(); + + $this->handleChangedParameters(); + + return $this; + } + + /** + * Return whether given instance or name is in the filter stack. + * + * @param callable|string $filter + * + * @return bool + */ + public function hasFilter($filter) + { + $key = is_callable($filter) ? 0 : 1; + + foreach ($this->filters as $tuple) { + if ($tuple[$key] === $filter) { + return true; + } + } + + return false; + } + + /** + * Get filters stack. + * + * @return array + */ + public function getFilters() + { + return $this->filters; + } + + /** + * Set filters stack. + * + * @param array $filters + * + * @return $this + */ + public function setFilters(array $filters) + { + $this->filters = $filters; + + $this->updateInternalState(); + + $this->handleChangedParameters(); + + return $this; + } + + /** + * Reset filters stack. + * + * @return $this + */ + public function resetFilters() + { + $this->filters = array(); + + if ($this->endDate !== null) { + $this->filters[] = array(static::END_DATE_FILTER, null); + } + + if ($this->recurrences !== null) { + $this->filters[] = array(static::RECURRENCES_FILTER, null); + } + + $this->handleChangedParameters(); + + return $this; + } + + /** + * Update properties after removing built-in filters. + * + * @return void + */ + protected function updateInternalState() + { + if (!$this->hasFilter(static::END_DATE_FILTER)) { + $this->endDate = null; + } + + if (!$this->hasFilter(static::RECURRENCES_FILTER)) { + $this->recurrences = null; + } + } + + /** + * Add a recurrences filter (set maximum number of recurrences). + * + * @param int|null $recurrences + * + * @throws \InvalidArgumentException + * + * @return $this + */ + public function setRecurrences($recurrences) + { + if (!is_numeric($recurrences) && !is_null($recurrences) || $recurrences < 0) { + throw new InvalidArgumentException('Invalid number of recurrences.'); + } + + if ($recurrences === null) { + return $this->removeFilter(static::RECURRENCES_FILTER); + } + + $this->recurrences = (int) $recurrences; + + if (!$this->hasFilter(static::RECURRENCES_FILTER)) { + return $this->addFilter(static::RECURRENCES_FILTER); + } + + $this->handleChangedParameters(); + + return $this; + } + + /** + * Recurrences filter callback (limits number of recurrences). + * + * @param \Carbon\Carbon $current + * @param int $key + * + * @return bool|string + */ + protected function filterRecurrences($current, $key) + { + if ($key < $this->recurrences) { + return true; + } + + return static::END_ITERATION; + } + + /** + * Change the period start date. + * + * @param DateTime|DateTimeInterface|string $date + * @param bool|null $inclusive + * + * @throws \InvalidArgumentException + * + * @return $this + */ + public function setStartDate($date, $inclusive = null) + { + if (!$date = Carbon::make($date)) { + throw new InvalidArgumentException('Invalid start date.'); + } + + $this->startDate = $date; + + if ($inclusive !== null) { + $this->toggleOptions(static::EXCLUDE_START_DATE, !$inclusive); + } + + return $this; + } + + /** + * Change the period end date. + * + * @param DateTime|DateTimeInterface|string|null $date + * @param bool|null $inclusive + * + * @throws \InvalidArgumentException + * + * @return $this + */ + public function setEndDate($date, $inclusive = null) + { + if (!is_null($date) && !$date = Carbon::make($date)) { + throw new InvalidArgumentException('Invalid end date.'); + } + + if (!$date) { + return $this->removeFilter(static::END_DATE_FILTER); + } + + $this->endDate = $date; + + if ($inclusive !== null) { + $this->toggleOptions(static::EXCLUDE_END_DATE, !$inclusive); + } + + if (!$this->hasFilter(static::END_DATE_FILTER)) { + return $this->addFilter(static::END_DATE_FILTER); + } + + $this->handleChangedParameters(); + + return $this; + } + + /** + * End date filter callback. + * + * @param \Carbon\Carbon $current + * + * @return bool|string + */ + protected function filterEndDate($current) + { + if (!$this->isEndExcluded() && $current == $this->endDate) { + return true; + } + + if ($this->dateInterval->invert ? $current > $this->endDate : $current < $this->endDate) { + return true; + } + + return static::END_ITERATION; + } + + /** + * End iteration filter callback. + * + * @return string + */ + protected function endIteration() + { + return static::END_ITERATION; + } + + /** + * Handle change of the parameters. + */ + protected function handleChangedParameters() + { + $this->validationResult = null; + } + + /** + * Validate current date and stop iteration when necessary. + * + * Returns true when current date is valid, false if it is not, or static::END_ITERATION + * when iteration should be stopped. + * + * @return bool|static::END_ITERATION + */ + protected function validateCurrentDate() + { + if ($this->current === null) { + $this->rewind(); + } + + // Check after the first rewind to avoid repeating the initial validation. + if ($this->validationResult !== null) { + return $this->validationResult; + } + + return $this->validationResult = $this->checkFilters(); + } + + /** + * Check whether current value and key pass all the filters. + * + * @return bool|string + */ + protected function checkFilters() + { + $current = $this->prepareForReturn($this->current); + + foreach ($this->filters as $tuple) { + $result = call_user_func( + $tuple[0], $current->copy(), $this->key, $this + ); + + if ($result === static::END_ITERATION) { + return static::END_ITERATION; + } + + if (!$result) { + return false; + } + } + + return true; + } + + /** + * Prepare given date to be returned to the external logic. + * + * @param Carbon $date + * + * @return Carbon + */ + protected function prepareForReturn(Carbon $date) + { + $date = $date->copy(); + + if ($this->timezone) { + $date->setTimezone($this->timezone); + } + + return $date; + } + + /** + * Check if the current position is valid. + * + * @return bool + */ + public function valid() + { + return $this->validateCurrentDate() === true; + } + + /** + * Return the current key. + * + * @return int|null + */ + public function key() + { + if ($this->valid()) { + return $this->key; + } + } + + /** + * Return the current date. + * + * @return Carbon|null + */ + public function current() + { + if ($this->valid()) { + return $this->prepareForReturn($this->current); + } + } + + /** + * Move forward to the next date. + * + * @throws \RuntimeException + * + * @return void + */ + public function next() + { + if ($this->current === null) { + $this->rewind(); + } + + if ($this->validationResult !== static::END_ITERATION) { + $this->key++; + + $this->incrementCurrentDateUntilValid(); + } + } + + /** + * Rewind to the start date. + * + * Iterating over a date in the UTC timezone avoids bug during backward DST change. + * + * @see https://bugs.php.net/bug.php?id=72255 + * @see https://bugs.php.net/bug.php?id=74274 + * @see https://wiki.php.net/rfc/datetime_and_daylight_saving_time + * + * @throws \RuntimeException + * + * @return void + */ + public function rewind() + { + $this->key = 0; + $this->current = $this->startDate->copy(); + $this->timezone = static::intervalHasTime($this->dateInterval) ? $this->current->getTimezone() : null; + + if ($this->timezone) { + $this->current->setTimezone('UTC'); + } + + $this->validationResult = null; + + if ($this->isStartExcluded() || $this->validateCurrentDate() === false) { + $this->incrementCurrentDateUntilValid(); + } + } + + /** + * Skip iterations and returns iteration state (false if ended, true if still valid). + * + * @param int $count steps number to skip (1 by default) + * + * @return bool + */ + public function skip($count = 1) + { + for ($i = $count; $this->valid() && $i > 0; $i--) { + $this->next(); + } + + return $this->valid(); + } + + /** + * Keep incrementing the current date until a valid date is found or the iteration is ended. + * + * @throws \RuntimeException + * + * @return void + */ + protected function incrementCurrentDateUntilValid() + { + $attempts = 0; + + do { + $this->current->add($this->dateInterval); + + $this->validationResult = null; + + if (++$attempts > static::NEXT_MAX_ATTEMPTS) { + throw new RuntimeException('Could not find next valid date.'); + } + } while ($this->validateCurrentDate() === false); + } + + /** + * Format the date period as ISO 8601. + * + * @return string + */ + public function toIso8601String() + { + $parts = array(); + + if ($this->recurrences !== null) { + $parts[] = 'R'.$this->recurrences; + } + + $parts[] = $this->startDate->toIso8601String(); + + $parts[] = $this->dateInterval->spec(); + + if ($this->endDate !== null) { + $parts[] = $this->endDate->toIso8601String(); + } + + return implode('/', $parts); + } + + /** + * Convert the date period into a string. + * + * @return string + */ + public function toString() + { + $translator = Carbon::getTranslator(); + + $parts = array(); + + $format = !$this->startDate->isStartOfDay() || $this->endDate && !$this->endDate->isStartOfDay() + ? 'Y-m-d H:i:s' + : 'Y-m-d'; + + if ($this->recurrences !== null) { + $parts[] = $translator->transChoice('period_recurrences', $this->recurrences, array(':count' => $this->recurrences)); + } + + $parts[] = $translator->trans('period_interval', array(':interval' => $this->dateInterval->forHumans())); + + $parts[] = $translator->trans('period_start_date', array(':date' => $this->startDate->format($format))); + + if ($this->endDate !== null) { + $parts[] = $translator->trans('period_end_date', array(':date' => $this->endDate->format($format))); + } + + $result = implode(' ', $parts); + + return mb_strtoupper(mb_substr($result, 0, 1)).mb_substr($result, 1); + } + + /** + * Format the date period as ISO 8601. + * + * @return string + */ + public function spec() + { + return $this->toIso8601String(); + } + + /** + * Convert the date period into an array without changing current iteration state. + * + * @return array + */ + public function toArray() + { + $state = array( + $this->key, + $this->current ? $this->current->copy() : null, + $this->validationResult, + ); + + $result = iterator_to_array($this); + + list( + $this->key, + $this->current, + $this->validationResult + ) = $state; + + return $result; + } + + /** + * Count dates in the date period. + * + * @return int + */ + public function count() + { + return count($this->toArray()); + } + + /** + * Return the first date in the date period. + * + * @return Carbon|null + */ + public function first() + { + if ($array = $this->toArray()) { + return $array[0]; + } + } + + /** + * Return the last date in the date period. + * + * @return Carbon|null + */ + public function last() + { + if ($array = $this->toArray()) { + return $array[count($array) - 1]; + } + } + + /** + * Call given macro. + * + * @param string $name + * @param array $parameters + * + * @return mixed + */ + protected function callMacro($name, $parameters) + { + $macro = static::$macros[$name]; + + $reflection = new ReflectionFunction($macro); + + $reflectionParameters = $reflection->getParameters(); + + $expectedCount = count($reflectionParameters); + $actualCount = count($parameters); + + if ($expectedCount > $actualCount && $reflectionParameters[$expectedCount - 1]->name === 'self') { + for ($i = $actualCount; $i < $expectedCount - 1; $i++) { + $parameters[] = $reflectionParameters[$i]->getDefaultValue(); + } + + $parameters[] = $this; + } + + if ($macro instanceof Closure && method_exists($macro, 'bindTo')) { + $macro = $macro->bindTo($this, get_class($this)); + } + + return call_user_func_array($macro, $parameters); + } + + /** + * Convert the date period into a string. + * + * @return string + */ + public function __toString() + { + return $this->toString(); + } + + /** + * Add aliases for setters. + * + * CarbonPeriod::days(3)->hours(5)->invert() + * ->sinceNow()->until('2010-01-10') + * ->filter(...) + * ->count() + * + * Note: We use magic method to let static and instance aliases with the same names. + * + * @param string $method + * @param array $parameters + * + * @return mixed + */ + public function __call($method, $parameters) + { + if (static::hasMacro($method)) { + return $this->callMacro($method, $parameters); + } + + $first = count($parameters) >= 1 ? $parameters[0] : null; + $second = count($parameters) >= 2 ? $parameters[1] : null; + + switch ($method) { + case 'start': + case 'since': + return $this->setStartDate($first, $second); + + case 'sinceNow': + return $this->setStartDate(new Carbon, $first); + + case 'end': + case 'until': + return $this->setEndDate($first, $second); + + case 'untilNow': + return $this->setEndDate(new Carbon, $first); + + case 'dates': + case 'between': + return $this->setDates($first, $second); + + case 'recurrences': + case 'times': + return $this->setRecurrences($first); + + case 'options': + return $this->setOptions($first); + + case 'toggle': + return $this->toggleOptions($first, $second); + + case 'filter': + case 'push': + return $this->addFilter($first, $second); + + case 'prepend': + return $this->prependFilter($first, $second); + + case 'filters': + return $this->setFilters($first ?: array()); + + case 'interval': + case 'each': + case 'every': + case 'step': + case 'stepBy': + return $this->setDateInterval($first); + + case 'invert': + return $this->invertDateInterval(); + + case 'years': + case 'year': + case 'months': + case 'month': + case 'weeks': + case 'week': + case 'days': + case 'dayz': + case 'day': + case 'hours': + case 'hour': + case 'minutes': + case 'minute': + case 'seconds': + case 'second': + return $this->setDateInterval(call_user_func( + // Override default P1D when instantiating via fluent setters. + array($this->isDefaultInterval ? new CarbonInterval('PT0S') : $this->dateInterval, $method), + count($parameters) === 0 ? 1 : $first + )); + } + + throw new BadMethodCallException("Method $method does not exist."); + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidDateException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidDateException.php new file mode 100644 index 000000000..1b0d47377 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidDateException.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Carbon\Exceptions; + +use Exception; +use InvalidArgumentException; + +class InvalidDateException extends InvalidArgumentException +{ + /** + * The invalid field. + * + * @var string + */ + private $field; + + /** + * The invalid value. + * + * @var mixed + */ + private $value; + + /** + * Constructor. + * + * @param string $field + * @param mixed $value + * @param int $code + * @param \Exception|null $previous + */ + public function __construct($field, $value, $code = 0, Exception $previous = null) + { + $this->field = $field; + $this->value = $value; + parent::__construct($field.' : '.$value.' is not a valid value.', $code, $previous); + } + + /** + * Get the invalid field. + * + * @return string + */ + public function getField() + { + return $this->field; + } + + /** + * Get the invalid value. + * + * @return mixed + */ + public function getValue() + { + return $this->value; + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/af.php b/vendor/nesbot/carbon/src/Carbon/Lang/af.php new file mode 100644 index 000000000..5cf6a8d94 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/af.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count jaar|:count jare', + 'y' => ':count jaar|:count jare', + 'month' => ':count maand|:count maande', + 'm' => ':count maand|:count maande', + 'week' => ':count week|:count weke', + 'w' => ':count week|:count weke', + 'day' => ':count dag|:count dae', + 'd' => ':count dag|:count dae', + 'hour' => ':count uur|:count ure', + 'h' => ':count uur|:count ure', + 'minute' => ':count minuut|:count minute', + 'min' => ':count minuut|:count minute', + 'second' => ':count sekond|:count sekondes', + 's' => ':count sekond|:count sekondes', + 'ago' => ':time terug', + 'from_now' => ':time van nou af', + 'after' => ':time na', + 'before' => ':time voor', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar.php new file mode 100644 index 000000000..de8f6b85b --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => '{0}سنة|{1}سنة|{2}سنتين|[3,10]:count سنوات|[11,Inf]:count سنة', + 'y' => '{0}سنة|{1}سنة|{2}سنتين|[3,10]:count سنوات|[11,Inf]:count سنة', + 'month' => '{0}شهر|{1} شهر|{2}شهرين|[3,10]:count أشهر|[11,Inf]:count شهر', + 'm' => '{0}شهر|{1} شهر|{2}شهرين|[3,10]:count أشهر|[11,Inf]:count شهر', + 'week' => '{0}أسبوع|{1}أسبوع|{2}أسبوعين|[3,10]:count أسابيع|[11,Inf]:count أسبوع', + 'w' => '{0}أسبوع|{1}أسبوع|{2}أسبوعين|[3,10]:count أسابيع|[11,Inf]:count أسبوع', + 'day' => '{0}يوم|{1}يوم|{2}يومين|[3,10]:count أيام|[11,Inf] يوم', + 'd' => '{0}يوم|{1}يوم|{2}يومين|[3,10]:count أيام|[11,Inf] يوم', + 'hour' => '{0}ساعة|{1}ساعة|{2}ساعتين|[3,10]:count ساعات|[11,Inf]:count ساعة', + 'h' => '{0}ساعة|{1}ساعة|{2}ساعتين|[3,10]:count ساعات|[11,Inf]:count ساعة', + 'minute' => '{0}دقيقة|{1}دقيقة|{2}دقيقتين|[3,10]:count دقائق|[11,Inf]:count دقيقة', + 'min' => '{0}دقيقة|{1}دقيقة|{2}دقيقتين|[3,10]:count دقائق|[11,Inf]:count دقيقة', + 'second' => '{0}ثانية|{1}ثانية|{2}ثانيتين|[3,10]:count ثوان|[11,Inf]:count ثانية', + 's' => '{0}ثانية|{1}ثانية|{2}ثانيتين|[3,10]:count ثوان|[11,Inf]:count ثانية', + 'ago' => 'منذ :time', + 'from_now' => ':time من الآن', + 'after' => 'بعد :time', + 'before' => 'قبل :time', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_Shakl.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_Shakl.php new file mode 100644 index 000000000..846ae0247 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_Shakl.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => '[0,1] سَنَة|{2} سَنَتَيْن|[3,10]:count سَنَوَات|[11,Inf]:count سَنَة', + 'y' => '[0,1] سَنَة|{2} سَنَتَيْن|[3,10]:count سَنَوَات|[11,Inf]:count سَنَة', + 'month' => '[0,1] شَهْرَ|{2} شَهْرَيْن|[3,10]:count أَشْهُر|[11,Inf]:count شَهْرَ', + 'm' => '[0,1] شَهْرَ|{2} شَهْرَيْن|[3,10]:count أَشْهُر|[11,Inf]:count شَهْرَ', + 'week' => '[0,1] أُسْبُوع|{2} أُسْبُوعَيْن|[3,10]:count أَسَابِيع|[11,Inf]:count أُسْبُوع', + 'w' => '[0,1] أُسْبُوع|{2} أُسْبُوعَيْن|[3,10]:count أَسَابِيع|[11,Inf]:count أُسْبُوع', + 'day' => '[0,1] يَوْم|{2} يَوْمَيْن|[3,10]:count أَيَّام|[11,Inf] يَوْم', + 'd' => '[0,1] يَوْم|{2} يَوْمَيْن|[3,10]:count أَيَّام|[11,Inf] يَوْم', + 'hour' => '[0,1] سَاعَة|{2} سَاعَتَيْن|[3,10]:count سَاعَات|[11,Inf]:count سَاعَة', + 'h' => '[0,1] سَاعَة|{2} سَاعَتَيْن|[3,10]:count سَاعَات|[11,Inf]:count سَاعَة', + 'minute' => '[0,1] دَقِيقَة|{2} دَقِيقَتَيْن|[3,10]:count دَقَائِق|[11,Inf]:count دَقِيقَة', + 'min' => '[0,1] دَقِيقَة|{2} دَقِيقَتَيْن|[3,10]:count دَقَائِق|[11,Inf]:count دَقِيقَة', + 'second' => '[0,1] ثَانِيَة|{2} ثَانِيَتَيْن|[3,10]:count ثَوَان|[11,Inf]:count ثَانِيَة', + 's' => '[0,1] ثَانِيَة|{2} ثَانِيَتَيْن|[3,10]:count ثَوَان|[11,Inf]:count ثَانِيَة', + 'ago' => 'مُنْذُ :time', + 'from_now' => 'مِنَ الْآن :time', + 'after' => 'بَعْدَ :time', + 'before' => 'قَبْلَ :time', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/az.php b/vendor/nesbot/carbon/src/Carbon/Lang/az.php new file mode 100644 index 000000000..25f5c4a4d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/az.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count il', + 'y' => ':count il', + 'month' => ':count ay', + 'm' => ':count ay', + 'week' => ':count həftə', + 'w' => ':count həftə', + 'day' => ':count gün', + 'd' => ':count gün', + 'hour' => ':count saat', + 'h' => ':count saat', + 'minute' => ':count dəqiqə', + 'min' => ':count dəqiqə', + 'second' => ':count saniyə', + 's' => ':count saniyə', + 'ago' => ':time əvvəl', + 'from_now' => ':time sonra', + 'after' => ':time sonra', + 'before' => ':time əvvəl', + 'diff_now' => 'indi', + 'diff_yesterday' => 'dünən', + 'diff_tomorrow' => 'sabah', + 'diff_before_yesterday' => 'srağagün', + 'diff_after_tomorrow' => 'birisi gün', + 'period_recurrences' => ':count dəfədən bir', + 'period_interval' => 'hər :interval', + 'period_start_date' => ':date tarixindən başlayaraq', + 'period_end_date' => ':date tarixinədək', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bg.php b/vendor/nesbot/carbon/src/Carbon/Lang/bg.php new file mode 100644 index 000000000..d9e510be8 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/bg.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count година|:count години', + 'y' => ':count година|:count години', + 'month' => ':count месец|:count месеца', + 'm' => ':count месец|:count месеца', + 'week' => ':count седмица|:count седмици', + 'w' => ':count седмица|:count седмици', + 'day' => ':count ден|:count дни', + 'd' => ':count ден|:count дни', + 'hour' => ':count час|:count часа', + 'h' => ':count час|:count часа', + 'minute' => ':count минута|:count минути', + 'min' => ':count минута|:count минути', + 'second' => ':count секунда|:count секунди', + 's' => ':count секунда|:count секунди', + 'ago' => 'преди :time', + 'from_now' => ':time от сега', + 'after' => 'след :time', + 'before' => 'преди :time', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bn.php b/vendor/nesbot/carbon/src/Carbon/Lang/bn.php new file mode 100644 index 000000000..5817599c6 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/bn.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => '১ বছর|:count বছর', + 'y' => '১ বছর|:count বছর', + 'month' => '১ মাস|:count মাস', + 'm' => '১ মাস|:count মাস', + 'week' => '১ সপ্তাহ|:count সপ্তাহ', + 'w' => '১ সপ্তাহ|:count সপ্তাহ', + 'day' => '১ দিন|:count দিন', + 'd' => '১ দিন|:count দিন', + 'hour' => '১ ঘন্টা|:count ঘন্টা', + 'h' => '১ ঘন্টা|:count ঘন্টা', + 'minute' => '১ মিনিট|:count মিনিট', + 'min' => '১ মিনিট|:count মিনিট', + 'second' => '১ সেকেন্ড|:count সেকেন্ড', + 's' => '১ সেকেন্ড|:count সেকেন্ড', + 'ago' => ':time পূর্বে', + 'from_now' => 'এখন থেকে :time', + 'after' => ':time পরে', + 'before' => ':time আগে', + 'diff_now' => 'এখন', + 'diff_yesterday' => 'গতকাল', + 'diff_tomorrow' => 'আগামীকাল', + 'period_recurrences' => ':count বার|:count বার', + 'period_interval' => 'প্রতি :interval', + 'period_start_date' => ':date থেকে', + 'period_end_date' => ':date পর্যন্ত', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bs_BA.php b/vendor/nesbot/carbon/src/Carbon/Lang/bs_BA.php new file mode 100644 index 000000000..7a9b05aa4 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/bs_BA.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count godina|:count godine|:count godina', + 'y' => ':count godina|:count godine|:count godina', + 'month' => ':count mjesec|:count mjeseca|:count mjeseci', + 'm' => ':count mjesec|:count mjeseca|:count mjeseci', + 'week' => ':count nedjelja|:count nedjelje|:count nedjelja', + 'w' => ':count nedjelja|:count nedjelje|:count nedjelja', + 'day' => ':count dan|:count dana|:count dana', + 'd' => ':count dan|:count dana|:count dana', + 'hour' => ':count sat|:count sata|:count sati', + 'h' => ':count sat|:count sata|:count sati', + 'minute' => ':count minut|:count minuta|:count minuta', + 'min' => ':count minut|:count minuta|:count minuta', + 'second' => ':count sekund|:count sekunda|:count sekundi', + 's' => ':count sekund|:count sekunda|:count sekundi', + 'ago' => 'prije :time', + 'from_now' => 'za :time', + 'after' => 'nakon :time', + 'before' => ':time ranije', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ca.php b/vendor/nesbot/carbon/src/Carbon/Lang/ca.php new file mode 100644 index 000000000..c854b5a91 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ca.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count any|:count anys', + 'y' => ':count any|:count anys', + 'month' => ':count mes|:count mesos', + 'm' => ':count mes|:count mesos', + 'week' => ':count setmana|:count setmanes', + 'w' => ':count setmana|:count setmanes', + 'day' => ':count dia|:count dies', + 'd' => ':count dia|:count dies', + 'hour' => ':count hora|:count hores', + 'h' => ':count hora|:count hores', + 'minute' => ':count minut|:count minuts', + 'min' => ':count minut|:count minuts', + 'second' => ':count segon|:count segons', + 's' => ':count segon|:count segons', + 'ago' => 'fa :time', + 'from_now' => 'd\'aquí :time', + 'after' => ':time després', + 'before' => ':time abans', + 'diff_now' => 'ara mateix', + 'diff_yesterday' => 'ahir', + 'diff_tomorrow' => 'demà', + 'diff_before_yesterday' => "abans d'ahir", + 'diff_after_tomorrow' => 'demà passat', + 'period_recurrences' => ':count cop|:count cops', + 'period_interval' => 'cada :interval', + 'period_start_date' => 'de :date', + 'period_end_date' => 'fins a :date', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/cs.php b/vendor/nesbot/carbon/src/Carbon/Lang/cs.php new file mode 100644 index 000000000..a447ce298 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/cs.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count rok|:count roky|:count let', + 'y' => ':count rok|:count roky|:count let', + 'month' => ':count měsíc|:count měsíce|:count měsíců', + 'm' => ':count měsíc|:count měsíce|:count měsíců', + 'week' => ':count týden|:count týdny|:count týdnů', + 'w' => ':count týden|:count týdny|:count týdnů', + 'day' => ':count den|:count dny|:count dní', + 'd' => ':count den|:count dny|:count dní', + 'hour' => ':count hodinu|:count hodiny|:count hodin', + 'h' => ':count hodinu|:count hodiny|:count hodin', + 'minute' => ':count minutu|:count minuty|:count minut', + 'min' => ':count minutu|:count minuty|:count minut', + 'second' => ':count sekundu|:count sekundy|:count sekund', + 's' => ':count sekundu|:count sekundy|:count sekund', + 'ago' => ':time nazpět', + 'from_now' => 'za :time', + 'after' => ':time později', + 'before' => ':time předtím', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/cy.php b/vendor/nesbot/carbon/src/Carbon/Lang/cy.php new file mode 100644 index 000000000..c93750e25 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/cy.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +return array( + 'year' => '1 flwyddyn|:count blynedd', + 'y' => ':countbl', + 'month' => '1 mis|:count fis', + 'm' => ':countmi', + 'week' => ':count wythnos', + 'w' => ':countw', + 'day' => ':count diwrnod', + 'd' => ':countd', + 'hour' => ':count awr', + 'h' => ':counth', + 'minute' => ':count munud', + 'min' => ':countm', + 'second' => ':count eiliad', + 's' => ':counts', + 'ago' => ':time yn ôl', + 'from_now' => ':time o hyn ymlaen', + 'after' => ':time ar ôl', + 'before' => ':time o\'r blaen', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/da.php b/vendor/nesbot/carbon/src/Carbon/Lang/da.php new file mode 100644 index 000000000..86507b0fe --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/da.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count år|:count år', + 'y' => ':count år|:count år', + 'month' => ':count måned|:count måneder', + 'm' => ':count måned|:count måneder', + 'week' => ':count uge|:count uger', + 'w' => ':count uge|:count uger', + 'day' => ':count dag|:count dage', + 'd' => ':count dag|:count dage', + 'hour' => ':count time|:count timer', + 'h' => ':count time|:count timer', + 'minute' => ':count minut|:count minutter', + 'min' => ':count minut|:count minutter', + 'second' => ':count sekund|:count sekunder', + 's' => ':count sekund|:count sekunder', + 'ago' => ':time siden', + 'from_now' => 'om :time', + 'after' => ':time efter', + 'before' => ':time før', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/de.php b/vendor/nesbot/carbon/src/Carbon/Lang/de.php new file mode 100644 index 000000000..5ea2a03ad --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/de.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count Jahr|:count Jahre', + 'y' => ':countJ|:countJ', + 'month' => ':count Monat|:count Monate', + 'm' => ':countMon|:countMon', + 'week' => ':count Woche|:count Wochen', + 'w' => ':countWo|:countWo', + 'day' => ':count Tag|:count Tage', + 'd' => ':countTg|:countTg', + 'hour' => ':count Stunde|:count Stunden', + 'h' => ':countStd|:countStd', + 'minute' => ':count Minute|:count Minuten', + 'min' => ':countMin|:countMin', + 'second' => ':count Sekunde|:count Sekunden', + 's' => ':countSek|:countSek', + 'ago' => 'vor :time', + 'from_now' => 'in :time', + 'after' => ':time später', + 'before' => ':time zuvor', + + 'year_from_now' => ':count Jahr|:count Jahren', + 'month_from_now' => ':count Monat|:count Monaten', + 'week_from_now' => ':count Woche|:count Wochen', + 'day_from_now' => ':count Tag|:count Tagen', + 'year_ago' => ':count Jahr|:count Jahren', + 'month_ago' => ':count Monat|:count Monaten', + 'week_ago' => ':count Woche|:count Wochen', + 'day_ago' => ':count Tag|:count Tagen', + + 'diff_now' => 'Gerade eben', + 'diff_yesterday' => 'Gestern', + 'diff_tomorrow' => 'Heute', + 'diff_before_yesterday' => 'Vorgestern', + 'diff_after_tomorrow' => 'Übermorgen', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/dv_MV.php b/vendor/nesbot/carbon/src/Carbon/Lang/dv_MV.php new file mode 100644 index 000000000..e3c50b396 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/dv_MV.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => '{0}އަހަރެއް|[1,Inf]:count އަހަރު', + 'y' => '{0}އަހަރެއް|[1,Inf]:count އަހަރު', + 'month' => '{0}މައްސަރެއް|[1,Inf]:count މަސް', + 'm' => '{0}މައްސަރެއް|[1,Inf]:count މަސް', + 'week' => '{0}ހަފްތާއެއް|[1,Inf]:count ހަފްތާ', + 'w' => '{0}ހަފްތާއެއް|[1,Inf]:count ހަފްތާ', + 'day' => '{0}ދުވަސް|[1,Inf]:count ދުވަސް', + 'd' => '{0}ދުވަސް|[1,Inf]:count ދުވަސް', + 'hour' => '{0}ގަޑިއިރެއް|[1,Inf]:count ގަޑި', + 'h' => '{0}ގަޑިއިރެއް|[1,Inf]:count ގަޑި', + 'minute' => '{0}މިނެޓެއް|[1,Inf]:count މިނެޓް', + 'min' => '{0}މިނެޓެއް|[1,Inf]:count މިނެޓް', + 'second' => '{0}ސިކުންތެއް|[1,Inf]:count ސިކުންތު', + 's' => '{0}ސިކުންތެއް|[1,Inf]:count ސިކުންތު', + 'ago' => ':time ކުރިން', + 'from_now' => ':time ފަހުން', + 'after' => ':time ފަހުން', + 'before' => ':time ކުރި', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/el.php b/vendor/nesbot/carbon/src/Carbon/Lang/el.php new file mode 100644 index 000000000..16b3f4427 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/el.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count χρόνος|:count χρόνια', + 'y' => ':count χρόνος|:count χρόνια', + 'month' => ':count μήνας|:count μήνες', + 'm' => ':count μήνας|:count μήνες', + 'week' => ':count εβδομάδα|:count εβδομάδες', + 'w' => ':count εβδομάδα|:count εβδομάδες', + 'day' => ':count μέρα|:count μέρες', + 'd' => ':count μέρα|:count μέρες', + 'hour' => ':count ώρα|:count ώρες', + 'h' => ':count ώρα|:count ώρες', + 'minute' => ':count λεπτό|:count λεπτά', + 'min' => ':count λεπτό|:count λεπτά', + 'second' => ':count δευτερόλεπτο|:count δευτερόλεπτα', + 's' => ':count δευτερόλεπτο|:count δευτερόλεπτα', + 'ago' => 'πριν από :time', + 'from_now' => 'σε :time από τώρα', + 'after' => ':time μετά', + 'before' => ':time πριν', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en.php b/vendor/nesbot/carbon/src/Carbon/Lang/en.php new file mode 100644 index 000000000..a15c131fb --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/en.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count year|:count years', + 'y' => ':countyr|:countyrs', + 'month' => ':count month|:count months', + 'm' => ':countmo|:countmos', + 'week' => ':count week|:count weeks', + 'w' => ':countw|:countw', + 'day' => ':count day|:count days', + 'd' => ':countd|:countd', + 'hour' => ':count hour|:count hours', + 'h' => ':counth|:counth', + 'minute' => ':count minute|:count minutes', + 'min' => ':countm|:countm', + 'second' => ':count second|:count seconds', + 's' => ':counts|:counts', + 'ago' => ':time ago', + 'from_now' => ':time from now', + 'after' => ':time after', + 'before' => ':time before', + 'diff_now' => 'just now', + 'diff_yesterday' => 'yesterday', + 'diff_tomorrow' => 'tomorrow', + 'diff_before_yesterday' => 'before yesterday', + 'diff_after_tomorrow' => 'after tomorrow', + 'period_recurrences' => 'once|:count times', + 'period_interval' => 'every :interval', + 'period_start_date' => 'from :date', + 'period_end_date' => 'to :date', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/eo.php b/vendor/nesbot/carbon/src/Carbon/Lang/eo.php new file mode 100644 index 000000000..c5b90b3e7 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/eo.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count jaro|:count jaroj', + 'y' => ':count jaro|:count jaroj', + 'month' => ':count monato|:count monatoj', + 'm' => ':count monato|:count monatoj', + 'week' => ':count semajno|:count semajnoj', + 'w' => ':count semajno|:count semajnoj', + 'day' => ':count tago|:count tagoj', + 'd' => ':count tago|:count tagoj', + 'hour' => ':count horo|:count horoj', + 'h' => ':count horo|:count horoj', + 'minute' => ':count minuto|:count minutoj', + 'min' => ':count minuto|:count minutoj', + 'second' => ':count sekundo|:count sekundoj', + 's' => ':count sekundo|:count sekundoj', + 'ago' => 'antaŭ :time', + 'from_now' => 'je :time', + 'after' => ':time poste', + 'before' => ':time antaŭe', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es.php b/vendor/nesbot/carbon/src/Carbon/Lang/es.php new file mode 100644 index 000000000..567a280f3 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/es.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count año|:count años', + 'y' => ':count año|:count años', + 'month' => ':count mes|:count meses', + 'm' => ':count mes|:count meses', + 'week' => ':count semana|:count semanas', + 'w' => ':count semana|:count semanas', + 'day' => ':count día|:count días', + 'd' => ':count día|:count días', + 'hour' => ':count hora|:count horas', + 'h' => ':count hora|:count horas', + 'minute' => ':count minuto|:count minutos', + 'min' => ':count minuto|:count minutos', + 'second' => ':count segundo|:count segundos', + 's' => ':count segundo|:count segundos', + 'ago' => 'hace :time', + 'from_now' => 'dentro de :time', + 'after' => ':time después', + 'before' => ':time antes', + 'diff_now' => 'ahora mismo', + 'diff_yesterday' => 'ayer', + 'diff_tomorrow' => 'mañana', + 'diff_before_yesterday' => 'antier', + 'diff_after_tomorrow' => 'pasado mañana', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/et.php b/vendor/nesbot/carbon/src/Carbon/Lang/et.php new file mode 100644 index 000000000..2d9291e04 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/et.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count aasta|:count aastat', + 'y' => ':count aasta|:count aastat', + 'month' => ':count kuu|:count kuud', + 'm' => ':count kuu|:count kuud', + 'week' => ':count nädal|:count nädalat', + 'w' => ':count nädal|:count nädalat', + 'day' => ':count päev|:count päeva', + 'd' => ':count päev|:count päeva', + 'hour' => ':count tund|:count tundi', + 'h' => ':count tund|:count tundi', + 'minute' => ':count minut|:count minutit', + 'min' => ':count minut|:count minutit', + 'second' => ':count sekund|:count sekundit', + 's' => ':count sekund|:count sekundit', + 'ago' => ':time tagasi', + 'from_now' => ':time pärast', + 'after' => ':time pärast', + 'before' => ':time enne', + 'year_from_now' => ':count aasta', + 'month_from_now' => ':count kuu', + 'week_from_now' => ':count nädala', + 'day_from_now' => ':count päeva', + 'hour_from_now' => ':count tunni', + 'minute_from_now' => ':count minuti', + 'second_from_now' => ':count sekundi', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/eu.php b/vendor/nesbot/carbon/src/Carbon/Lang/eu.php new file mode 100644 index 000000000..1cb6b7cd6 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/eu.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => 'Urte 1|:count urte', + 'y' => 'Urte 1|:count urte', + 'month' => 'Hile 1|:count hile', + 'm' => 'Hile 1|:count hile', + 'week' => 'Aste 1|:count aste', + 'w' => 'Aste 1|:count aste', + 'day' => 'Egun 1|:count egun', + 'd' => 'Egun 1|:count egun', + 'hour' => 'Ordu 1|:count ordu', + 'h' => 'Ordu 1|:count ordu', + 'minute' => 'Minutu 1|:count minutu', + 'min' => 'Minutu 1|:count minutu', + 'second' => 'Segundu 1|:count segundu', + 's' => 'Segundu 1|:count segundu', + 'ago' => 'Orain dela :time', + 'from_now' => ':time barru', + 'after' => ':time geroago', + 'before' => ':time lehenago', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fa.php b/vendor/nesbot/carbon/src/Carbon/Lang/fa.php new file mode 100644 index 000000000..31bec8869 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fa.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count سال', + 'y' => ':count سال', + 'month' => ':count ماه', + 'm' => ':count ماه', + 'week' => ':count هفته', + 'w' => ':count هفته', + 'day' => ':count روز', + 'd' => ':count روز', + 'hour' => ':count ساعت', + 'h' => ':count ساعت', + 'minute' => ':count دقیقه', + 'min' => ':count دقیقه', + 'second' => ':count ثانیه', + 's' => ':count ثانیه', + 'ago' => ':time پیش', + 'from_now' => ':time بعد', + 'after' => ':time پس از', + 'before' => ':time پیش از', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fi.php b/vendor/nesbot/carbon/src/Carbon/Lang/fi.php new file mode 100644 index 000000000..481880444 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fi.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count vuosi|:count vuotta', + 'y' => ':count vuosi|:count vuotta', + 'month' => ':count kuukausi|:count kuukautta', + 'm' => ':count kuukausi|:count kuukautta', + 'week' => ':count viikko|:count viikkoa', + 'w' => ':count viikko|:count viikkoa', + 'day' => ':count päivä|:count päivää', + 'd' => ':count päivä|:count päivää', + 'hour' => ':count tunti|:count tuntia', + 'h' => ':count tunti|:count tuntia', + 'minute' => ':count minuutti|:count minuuttia', + 'min' => ':count minuutti|:count minuuttia', + 'second' => ':count sekunti|:count sekuntia', + 's' => ':count sekunti|:count sekuntia', + 'ago' => ':time sitten', + 'from_now' => ':time tästä hetkestä', + 'after' => ':time sen jälkeen', + 'before' => ':time ennen', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fo.php b/vendor/nesbot/carbon/src/Carbon/Lang/fo.php new file mode 100644 index 000000000..d91104b78 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fo.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count ár|:count ár', + 'y' => ':count ár|:count ár', + 'month' => ':count mánaður|:count mánaðir', + 'm' => ':count mánaður|:count mánaðir', + 'week' => ':count vika|:count vikur', + 'w' => ':count vika|:count vikur', + 'day' => ':count dag|:count dagar', + 'd' => ':count dag|:count dagar', + 'hour' => ':count tími|:count tímar', + 'h' => ':count tími|:count tímar', + 'minute' => ':count minutt|:count minuttir', + 'min' => ':count minutt|:count minuttir', + 'second' => ':count sekund|:count sekundir', + 's' => ':count sekund|:count sekundir', + 'ago' => ':time síðan', + 'from_now' => 'um :time', + 'after' => ':time aftaná', + 'before' => ':time áðrenn', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr.php new file mode 100644 index 000000000..0b20cdd29 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count an|:count ans', + 'y' => ':count an|:count ans', + 'month' => ':count mois', + 'm' => ':count mois', + 'week' => ':count semaine|:count semaines', + 'w' => ':count sem.', + 'day' => ':count jour|:count jours', + 'd' => ':count j.', + 'hour' => ':count heure|:count heures', + 'h' => ':count h.', + 'minute' => ':count minute|:count minutes', + 'min' => ':count min.', + 'second' => ':count seconde|:count secondes', + 's' => ':count sec.', + 'ago' => 'il y a :time', + 'from_now' => 'dans :time', + 'after' => ':time après', + 'before' => ':time avant', + 'diff_now' => "à l'instant", + 'diff_yesterday' => 'hier', + 'diff_tomorrow' => 'demain', + 'diff_before_yesterday' => 'avant-hier', + 'diff_after_tomorrow' => 'après-demain', + 'period_recurrences' => ':count fois', + 'period_interval' => 'tous les :interval', + 'period_start_date' => 'de :date', + 'period_end_date' => 'à :date', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/gl.php b/vendor/nesbot/carbon/src/Carbon/Lang/gl.php new file mode 100644 index 000000000..cd22a31e7 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/gl.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count ano|:count anos', + 'month' => ':count mes|:count meses', + 'week' => ':count semana|:count semanas', + 'day' => ':count día|:count días', + 'hour' => ':count hora|:count horas', + 'minute' => ':count minuto|:count minutos', + 'second' => ':count segundo|:count segundos', + 'ago' => 'fai :time', + 'from_now' => 'dentro de :time', + 'after' => ':time despois', + 'before' => ':time antes', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/gu.php b/vendor/nesbot/carbon/src/Carbon/Lang/gu.php new file mode 100644 index 000000000..7759dfcb5 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/gu.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count વર્ષ|:count વર્ષો', + 'y' => ':countવર્ષ|:countવર્ષો', + 'month' => ':count મહિનો|:count મહિના', + 'm' => ':countમહિનો|:countમહિના', + 'week' => ':count અઠવાડિયું|:count અઠવાડિયા', + 'w' => ':countઅઠ.|:countઅઠ.', + 'day' => ':count દિવસ|:count દિવસો', + 'd' => ':countદિ.|:countદિ.', + 'hour' => ':count કલાક|:count કલાકો', + 'h' => ':countક.|:countક.', + 'minute' => ':count મિનિટ|:count મિનિટ', + 'min' => ':countમિ.|:countમિ.', + 'second' => ':count સેકેન્ડ|:count સેકેન્ડ', + 's' => ':countસે.|:countસે.', + 'ago' => ':time પહેલા', + 'from_now' => ':time અત્યારથી', + 'after' => ':time પછી', + 'before' => ':time પહેલા', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/he.php b/vendor/nesbot/carbon/src/Carbon/Lang/he.php new file mode 100644 index 000000000..2d4f4f880 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/he.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => 'שנה|{2}שנתיים|:count שנים', + 'y' => 'שנה|{2}שנתיים|:count שנים', + 'month' => 'חודש|{2}חודשיים|:count חודשים', + 'm' => 'חודש|{2}חודשיים|:count חודשים', + 'week' => 'שבוע|{2}שבועיים|:count שבועות', + 'w' => 'שבוע|{2}שבועיים|:count שבועות', + 'day' => 'יום|{2}יומיים|:count ימים', + 'd' => 'יום|{2}יומיים|:count ימים', + 'hour' => 'שעה|{2}שעתיים|:count שעות', + 'h' => 'שעה|{2}שעתיים|:count שעות', + 'minute' => 'דקה|{2}דקותיים|:count דקות', + 'min' => 'דקה|{2}דקותיים|:count דקות', + 'second' => 'שניה|:count שניות', + 's' => 'שניה|:count שניות', + 'ago' => 'לפני :time', + 'from_now' => 'בעוד :time', + 'after' => 'אחרי :time', + 'before' => 'לפני :time', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/hi.php b/vendor/nesbot/carbon/src/Carbon/Lang/hi.php new file mode 100644 index 000000000..6c670ee8c --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/hi.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => '1 वर्ष|:count वर्षों', + 'y' => '1 वर्ष|:count वर्षों', + 'month' => '1 माह|:count महीने', + 'm' => '1 माह|:count महीने', + 'week' => '1 सप्ताह|:count सप्ताह', + 'w' => '1 सप्ताह|:count सप्ताह', + 'day' => '1 दिन|:count दिनों', + 'd' => '1 दिन|:count दिनों', + 'hour' => '1 घंटा|:count घंटे', + 'h' => '1 घंटा|:count घंटे', + 'minute' => '1 मिनट|:count मिनटों', + 'min' => '1 मिनट|:count मिनटों', + 'second' => '1 सेकंड|:count सेकंड', + 's' => '1 सेकंड|:count सेकंड', + 'ago' => ':time पूर्व', + 'from_now' => ':time से', + 'after' => ':time के बाद', + 'before' => ':time के पहले', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/hr.php b/vendor/nesbot/carbon/src/Carbon/Lang/hr.php new file mode 100644 index 000000000..1a339de7b --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/hr.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count godinu|:count godine|:count godina', + 'y' => ':count godinu|:count godine|:count godina', + 'month' => ':count mjesec|:count mjeseca|:count mjeseci', + 'm' => ':count mjesec|:count mjeseca|:count mjeseci', + 'week' => ':count tjedan|:count tjedna|:count tjedana', + 'w' => ':count tjedan|:count tjedna|:count tjedana', + 'day' => ':count dan|:count dana|:count dana', + 'd' => ':count dan|:count dana|:count dana', + 'hour' => ':count sat|:count sata|:count sati', + 'h' => ':count sat|:count sata|:count sati', + 'minute' => ':count minutu|:count minute |:count minuta', + 'min' => ':count minutu|:count minute |:count minuta', + 'second' => ':count sekundu|:count sekunde|:count sekundi', + 's' => ':count sekundu|:count sekunde|:count sekundi', + 'ago' => 'prije :time', + 'from_now' => 'za :time', + 'after' => 'za :time', + 'before' => 'prije :time', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/hu.php b/vendor/nesbot/carbon/src/Carbon/Lang/hu.php new file mode 100644 index 000000000..45daf41a1 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/hu.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count év', + 'y' => ':count év', + 'month' => ':count hónap', + 'm' => ':count hónap', + 'week' => ':count hét', + 'w' => ':count hét', + 'day' => ':count nap', + 'd' => ':count nap', + 'hour' => ':count óra', + 'h' => ':count óra', + 'minute' => ':count perc', + 'min' => ':count perc', + 'second' => ':count másodperc', + 's' => ':count másodperc', + 'ago' => ':time', + 'from_now' => ':time múlva', + 'after' => ':time később', + 'before' => ':time korábban', + 'year_ago' => ':count éve', + 'month_ago' => ':count hónapja', + 'week_ago' => ':count hete', + 'day_ago' => ':count napja', + 'hour_ago' => ':count órája', + 'minute_ago' => ':count perce', + 'second_ago' => ':count másodperce', + 'year_after' => ':count évvel', + 'month_after' => ':count hónappal', + 'week_after' => ':count héttel', + 'day_after' => ':count nappal', + 'hour_after' => ':count órával', + 'minute_after' => ':count perccel', + 'second_after' => ':count másodperccel', + 'year_before' => ':count évvel', + 'month_before' => ':count hónappal', + 'week_before' => ':count héttel', + 'day_before' => ':count nappal', + 'hour_before' => ':count órával', + 'minute_before' => ':count perccel', + 'second_before' => ':count másodperccel', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/hy.php b/vendor/nesbot/carbon/src/Carbon/Lang/hy.php new file mode 100644 index 000000000..d2665f27a --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/hy.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count տարի', + 'y' => ':countտ', + 'month' => ':count ամիս', + 'm' => ':countամ', + 'week' => ':count շաբաթ', + 'w' => ':countշ', + 'day' => ':count օր', + 'd' => ':countօր', + 'hour' => ':count ժամ', + 'h' => ':countժ', + 'minute' => ':count րոպե', + 'min' => ':countր', + 'second' => ':count վարկյան', + 's' => ':countվրկ', + 'ago' => ':time առաջ', + 'from_now' => ':time ներկա պահից', + 'after' => ':time հետո', + 'before' => ':time առաջ', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/id.php b/vendor/nesbot/carbon/src/Carbon/Lang/id.php new file mode 100644 index 000000000..7f7114fad --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/id.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count tahun', + 'y' => ':count tahun', + 'month' => ':count bulan', + 'm' => ':count bulan', + 'week' => ':count minggu', + 'w' => ':count minggu', + 'day' => ':count hari', + 'd' => ':count hari', + 'hour' => ':count jam', + 'h' => ':count jam', + 'minute' => ':count menit', + 'min' => ':count menit', + 'second' => ':count detik', + 's' => ':count detik', + 'ago' => ':time yang lalu', + 'from_now' => ':time dari sekarang', + 'after' => ':time setelah', + 'before' => ':time sebelum', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/is.php b/vendor/nesbot/carbon/src/Carbon/Lang/is.php new file mode 100644 index 000000000..94c76a7f2 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/is.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => '1 ár|:count ár', + 'y' => '1 ár|:count ár', + 'month' => '1 mánuður|:count mánuðir', + 'm' => '1 mánuður|:count mánuðir', + 'week' => '1 vika|:count vikur', + 'w' => '1 vika|:count vikur', + 'day' => '1 dagur|:count dagar', + 'd' => '1 dagur|:count dagar', + 'hour' => '1 klukkutími|:count klukkutímar', + 'h' => '1 klukkutími|:count klukkutímar', + 'minute' => '1 mínúta|:count mínútur', + 'min' => '1 mínúta|:count mínútur', + 'second' => '1 sekúnda|:count sekúndur', + 's' => '1 sekúnda|:count sekúndur', + 'ago' => ':time síðan', + 'from_now' => ':time síðan', + 'after' => ':time eftir', + 'before' => ':time fyrir', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/it.php b/vendor/nesbot/carbon/src/Carbon/Lang/it.php new file mode 100644 index 000000000..70bc6d7b3 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/it.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count anno|:count anni', + 'y' => ':count anno|:count anni', + 'month' => ':count mese|:count mesi', + 'm' => ':count mese|:count mesi', + 'week' => ':count settimana|:count settimane', + 'w' => ':count settimana|:count settimane', + 'day' => ':count giorno|:count giorni', + 'd' => ':count giorno|:count giorni', + 'hour' => ':count ora|:count ore', + 'h' => ':count ora|:count ore', + 'minute' => ':count minuto|:count minuti', + 'min' => ':count minuto|:count minuti', + 'second' => ':count secondo|:count secondi', + 's' => ':count secondo|:count secondi', + 'ago' => ':time fa', + 'from_now' => 'tra :time', + 'after' => ':time dopo', + 'before' => ':time prima', + 'diff_now' => 'proprio ora', + 'diff_yesterday' => 'ieri', + 'diff_tomorrow' => 'domani', + 'diff_before_yesterday' => "l'altro ieri", + 'diff_after_tomorrow' => 'dopodomani', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ja.php b/vendor/nesbot/carbon/src/Carbon/Lang/ja.php new file mode 100644 index 000000000..71195475d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ja.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count年', + 'y' => ':count年', + 'month' => ':countヶ月', + 'm' => ':countヶ月', + 'week' => ':count週間', + 'w' => ':count週間', + 'day' => ':count日', + 'd' => ':count日', + 'hour' => ':count時間', + 'h' => ':count時間', + 'minute' => ':count分', + 'min' => ':count分', + 'second' => ':count秒', + 's' => ':count秒', + 'ago' => ':time前', + 'from_now' => '今から:time', + 'after' => ':time後', + 'before' => ':time前', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ka.php b/vendor/nesbot/carbon/src/Carbon/Lang/ka.php new file mode 100644 index 000000000..a8dde7ebc --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ka.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count წლის', + 'y' => ':count წლის', + 'month' => ':count თვის', + 'm' => ':count თვის', + 'week' => ':count კვირის', + 'w' => ':count კვირის', + 'day' => ':count დღის', + 'd' => ':count დღის', + 'hour' => ':count საათის', + 'h' => ':count საათის', + 'minute' => ':count წუთის', + 'min' => ':count წუთის', + 'second' => ':count წამის', + 's' => ':count წამის', + 'ago' => ':time უკან', + 'from_now' => ':time შემდეგ', + 'after' => ':time შემდეგ', + 'before' => ':time უკან', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/kk.php b/vendor/nesbot/carbon/src/Carbon/Lang/kk.php new file mode 100644 index 000000000..8d113afed --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/kk.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +return array( + 'year' => ':count жыл', + 'y' => ':count жыл', + 'month' => ':count ай', + 'm' => ':count ай', + 'week' => ':count апта', + 'w' => ':count апта', + 'day' => ':count күн', + 'd' => ':count күн', + 'hour' => ':count сағат', + 'h' => ':count сағат', + 'minute' => ':count минут', + 'min' => ':count минут', + 'second' => ':count секунд', + 's' => ':count секунд', + 'ago' => ':time бұрын', + 'from_now' => ':time кейін', + 'after' => ':time кейін', + 'before' => ':time бұрын', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/km.php b/vendor/nesbot/carbon/src/Carbon/Lang/km.php new file mode 100644 index 000000000..a104e06eb --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/km.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count ឆ្នាំ', + 'y' => ':count ឆ្នាំ', + 'month' => ':count ខែ', + 'm' => ':count ខែ', + 'week' => ':count សប្ដាហ៍', + 'w' => ':count សប្ដាហ៍', + 'day' => ':count ថ្ងៃ', + 'd' => ':count ថ្ងៃ', + 'hour' => ':count ម៉ោង', + 'h' => ':count ម៉ោង', + 'minute' => ':count នាទី', + 'min' => ':count នាទី', + 'second' => ':count វិនាទី', + 's' => ':count វិនាទី', + 'ago' => ':timeមុន', + 'from_now' => ':timeពី​ឥឡូវ', + 'after' => 'នៅ​ក្រោយ :time', + 'before' => 'នៅ​មុន :time', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ko.php b/vendor/nesbot/carbon/src/Carbon/Lang/ko.php new file mode 100644 index 000000000..020916442 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ko.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count 년', + 'y' => ':count 년', + 'month' => ':count 개월', + 'm' => ':count 개월', + 'week' => ':count 주일', + 'w' => ':count 주일', + 'day' => ':count 일', + 'd' => ':count 일', + 'hour' => ':count 시간', + 'h' => ':count 시간', + 'minute' => ':count 분', + 'min' => ':count 분', + 'second' => ':count 초', + 's' => ':count 초', + 'ago' => ':time 전', + 'from_now' => ':time 후', + 'after' => ':time 이후', + 'before' => ':time 이전', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/lt.php b/vendor/nesbot/carbon/src/Carbon/Lang/lt.php new file mode 100644 index 000000000..3f2fd1ec9 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/lt.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count metus|:count metus|:count metų', + 'y' => ':count metus|:count metus|:count metų', + 'month' => ':count mėnesį|:count mėnesius|:count mėnesių', + 'm' => ':count mėnesį|:count mėnesius|:count mėnesių', + 'week' => ':count savaitę|:count savaites|:count savaičių', + 'w' => ':count savaitę|:count savaites|:count savaičių', + 'day' => ':count dieną|:count dienas|:count dienų', + 'd' => ':count dieną|:count dienas|:count dienų', + 'hour' => ':count valandą|:count valandas|:count valandų', + 'h' => ':count valandą|:count valandas|:count valandų', + 'minute' => ':count minutę|:count minutes|:count minučių', + 'min' => ':count minutę|:count minutes|:count minučių', + 'second' => ':count sekundę|:count sekundes|:count sekundžių', + 's' => ':count sekundę|:count sekundes|:count sekundžių', + 'second_from_now' => ':count sekundės|:count sekundžių|:count sekundžių', + 'minute_from_now' => ':count minutės|:count minučių|:count minučių', + 'hour_from_now' => ':count valandos|:count valandų|:count valandų', + 'day_from_now' => ':count dienos|:count dienų|:count dienų', + 'week_from_now' => ':count savaitės|:count savaičių|:count savaičių', + 'month_from_now' => ':count mėnesio|:count mėnesių|:count mėnesių', + 'year_from_now' => ':count metų', + 'ago' => 'prieš :time', + 'from_now' => 'už :time', + 'after' => 'po :time', + 'before' => ':time nuo dabar', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/lv.php b/vendor/nesbot/carbon/src/Carbon/Lang/lv.php new file mode 100644 index 000000000..363193db9 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/lv.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => '0 gadiem|:count gada|:count gadiem', + 'y' => '0 gadiem|:count gada|:count gadiem', + 'month' => '0 mēnešiem|:count mēneša|:count mēnešiem', + 'm' => '0 mēnešiem|:count mēneša|:count mēnešiem', + 'week' => '0 nedēļām|:count nedēļas|:count nedēļām', + 'w' => '0 nedēļām|:count nedēļas|:count nedēļām', + 'day' => '0 dienām|:count dienas|:count dienām', + 'd' => '0 dienām|:count dienas|:count dienām', + 'hour' => '0 stundām|:count stundas|:count stundām', + 'h' => '0 stundām|:count stundas|:count stundām', + 'minute' => '0 minūtēm|:count minūtes|:count minūtēm', + 'min' => '0 minūtēm|:count minūtes|:count minūtēm', + 'second' => '0 sekundēm|:count sekundes|:count sekundēm', + 's' => '0 sekundēm|:count sekundes|:count sekundēm', + 'ago' => 'pirms :time', + 'from_now' => 'pēc :time', + 'after' => ':time vēlāk', + 'before' => ':time pirms', + + 'year_after' => '0 gadus|:count gadu|:count gadus', + 'month_after' => '0 mēnešus|:count mēnesi|:count mēnešus', + 'week_after' => '0 nedēļas|:count nedēļu|:count nedēļas', + 'day_after' => '0 dienas|:count dienu|:count dienas', + 'hour_after' => '0 stundas|:count stundu|:count stundas', + 'minute_after' => '0 minūtes|:count minūti|:count minūtes', + 'second_after' => '0 sekundes|:count sekundi|:count sekundes', + + 'year_before' => '0 gadus|:count gadu|:count gadus', + 'month_before' => '0 mēnešus|:count mēnesi|:count mēnešus', + 'week_before' => '0 nedēļas|:count nedēļu|:count nedēļas', + 'day_before' => '0 dienas|:count dienu|:count dienas', + 'hour_before' => '0 stundas|:count stundu|:count stundas', + 'minute_before' => '0 minūtes|:count minūti|:count minūtes', + 'second_before' => '0 sekundes|:count sekundi|:count sekundes', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mk.php b/vendor/nesbot/carbon/src/Carbon/Lang/mk.php new file mode 100644 index 000000000..c5ec12df7 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/mk.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count година|:count години', + 'month' => ':count месец|:count месеци', + 'week' => ':count седмица|:count седмици', + 'day' => ':count ден|:count дена', + 'hour' => ':count час|:count часа', + 'minute' => ':count минута|:count минути', + 'second' => ':count секунда|:count секунди', + 'ago' => 'пред :time', + 'from_now' => ':time од сега', + 'after' => 'по :time', + 'before' => 'пред :time', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mn.php b/vendor/nesbot/carbon/src/Carbon/Lang/mn.php new file mode 100644 index 000000000..b26dce557 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/mn.php @@ -0,0 +1,62 @@ + + * + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * @translator Batmandakh Erdenebileg + */ + +return array( + 'year' => ':count жил', + 'y' => ':count жил', + 'month' => ':count сар', + 'm' => ':count сар', + 'week' => ':count долоо хоног', + 'w' => ':count долоо хоног', + 'day' => ':count өдөр', + 'd' => ':count өдөр', + 'hour' => ':count цаг', + 'h' => ':countц', + 'minute' => ':count минут', + 'min' => ':countм', + 'second' => ':count секунд', + 's' => ':countс', + + 'ago' => ':timeн өмнө', + 'year_ago' => ':count жилий', + 'month_ago' => ':count сары', + 'day_ago' => ':count хоногий', + 'hour_ago' => ':count цагий', + 'minute_ago' => ':count минуты', + 'second_ago' => ':count секунды', + + 'from_now' => 'одоогоос :time', + 'year_from_now' => ':count жилийн дараа', + 'month_from_now' => ':count сарын дараа', + 'day_from_now' => ':count хоногийн дараа', + 'hour_from_now' => ':count цагийн дараа', + 'minute_from_now' => ':count минутын дараа', + 'second_from_now' => ':count секундын дараа', + + // Does it required to make translation for before, after as follows? hmm, I think we've made it with ago and from now keywords already. Anyway, I've included it just in case of undesired action... + 'after' => ':timeн дараа', + 'year_after' => ':count жилий', + 'month_after' => ':count сары', + 'day_after' => ':count хоногий', + 'hour_after' => ':count цагий', + 'minute_after' => ':count минуты', + 'second_after' => ':count секунды', + 'before' => ':timeн өмнө', + 'year_before' => ':count жилий', + 'month_before' => ':count сары', + 'day_before' => ':count хоногий', + 'hour_before' => ':count цагий', + 'minute_before' => ':count минуты', + 'second_before' => ':count секунды', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ms.php b/vendor/nesbot/carbon/src/Carbon/Lang/ms.php new file mode 100644 index 000000000..ef574228a --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ms.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count tahun', + 'y' => ':count tahun', + 'month' => ':count bulan', + 'm' => ':count bulan', + 'week' => ':count minggu', + 'w' => ':count minggu', + 'day' => ':count hari', + 'd' => ':count hari', + 'hour' => ':count jam', + 'h' => ':count jam', + 'minute' => ':count minit', + 'min' => ':count minit', + 'second' => ':count saat', + 's' => ':count saat', + 'ago' => ':time yang lalu', + 'from_now' => ':time dari sekarang', + 'after' => ':time selepas', + 'before' => ':time sebelum', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/my.php b/vendor/nesbot/carbon/src/Carbon/Lang/my.php new file mode 100644 index 000000000..e8e491ecf --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/my.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count နှစ်|:count နှစ်', + 'y' => ':count နှစ်|:count နှစ်', + 'month' => ':count လ|:count လ', + 'm' => ':count လ|:count လ', + 'week' => ':count ပတ်|:count ပတ်', + 'w' => ':count ပတ်|:count ပတ်', + 'day' => ':count ရက်|:count ရက်', + 'd' => ':count ရက်|:count ရက်', + 'hour' => ':count နာရီ|:count နာရီ', + 'h' => ':count နာရီ|:count နာရီ', + 'minute' => ':count မိနစ်|:count မိနစ်', + 'min' => ':count မိနစ်|:count မိနစ်', + 'second' => ':count စက္ကန့်|:count စက္ကန့်', + 's' => ':count စက္ကန့်|:count စက္ကန့်', + 'ago' => 'လွန်ခဲ့သော :time က', + 'from_now' => 'ယခုမှစ၍နောက် :time အကြာ', + 'after' => ':time ကြာပြီးနောက်', + 'before' => ':time မတိုင်ခင်', + 'diff_now' => 'အခုလေးတင်', + 'diff_yesterday' => 'မနေ့က', + 'diff_tomorrow' => 'မနက်ဖြန်', + 'diff_before_yesterday' => 'တမြန်နေ့က', + 'diff_after_tomorrow' => 'တဘက်ခါ', + 'period_recurrences' => ':count ကြိမ်', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ne.php b/vendor/nesbot/carbon/src/Carbon/Lang/ne.php new file mode 100644 index 000000000..0b528dfa4 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ne.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count वर्ष', + 'y' => ':count वर्ष', + 'month' => ':count महिना', + 'm' => ':count महिना', + 'week' => ':count हप्ता', + 'w' => ':count हप्ता', + 'day' => ':count दिन', + 'd' => ':count दिन', + 'hour' => ':count घण्टा', + 'h' => ':count घण्टा', + 'minute' => ':count मिनेट', + 'min' => ':count मिनेट', + 'second' => ':count सेकेण्ड', + 's' => ':count सेकेण्ड', + 'ago' => ':time पहिले', + 'from_now' => ':time देखि', + 'after' => ':time पछि', + 'before' => ':time अघि', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nl.php b/vendor/nesbot/carbon/src/Carbon/Lang/nl.php new file mode 100644 index 000000000..ec5a88edf --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/nl.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count jaar', + 'y' => ':count jaar', + 'month' => ':count maand|:count maanden', + 'm' => ':count maand|:count maanden', + 'week' => ':count week|:count weken', + 'w' => ':count week|:count weken', + 'day' => ':count dag|:count dagen', + 'd' => ':count dag|:count dagen', + 'hour' => ':count uur', + 'h' => ':count uur', + 'minute' => ':count minuut|:count minuten', + 'min' => ':count minuut|:count minuten', + 'second' => ':count seconde|:count seconden', + 's' => ':count seconde|:count seconden', + 'ago' => ':time geleden', + 'from_now' => 'over :time', + 'after' => ':time later', + 'before' => ':time eerder', + 'diff_now' => 'nu', + 'diff_yesterday' => 'gisteren', + 'diff_tomorrow' => 'morgen', + 'diff_after_tomorrow' => 'overmorgen', + 'diff_before_yesterday' => 'eergisteren', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/no.php b/vendor/nesbot/carbon/src/Carbon/Lang/no.php new file mode 100644 index 000000000..a6ece06a0 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/no.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count år|:count år', + 'y' => ':count år|:count år', + 'month' => ':count måned|:count måneder', + 'm' => ':count måned|:count måneder', + 'week' => ':count uke|:count uker', + 'w' => ':count uke|:count uker', + 'day' => ':count dag|:count dager', + 'd' => ':count dag|:count dager', + 'hour' => ':count time|:count timer', + 'h' => ':count time|:count timer', + 'minute' => ':count minutt|:count minutter', + 'min' => ':count minutt|:count minutter', + 'second' => ':count sekund|:count sekunder', + 's' => ':count sekund|:count sekunder', + 'ago' => ':time siden', + 'from_now' => 'om :time', + 'after' => ':time etter', + 'before' => ':time før', + 'diff_now' => 'akkurat nå', + 'diff_yesterday' => 'i går', + 'diff_tomorrow' => 'i morgen', + 'diff_before_yesterday' => 'i forgårs', + 'diff_after_tomorrow' => 'i overmorgen', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/oc.php b/vendor/nesbot/carbon/src/Carbon/Lang/oc.php new file mode 100644 index 000000000..e89e94c3b --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/oc.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +\Symfony\Component\Translation\PluralizationRules::set(function ($number) { + return $number == 1 ? 0 : 1; +}, 'oc'); + +return array( + 'year' => ':count an|:count ans', + 'y' => ':count an|:count ans', + 'month' => ':count mes|:count meses', + 'm' => ':count mes|:count meses', + 'week' => ':count setmana|:count setmanas', + 'w' => ':count setmana|:count setmanas', + 'day' => ':count jorn|:count jorns', + 'd' => ':count jorn|:count jorns', + 'hour' => ':count ora|:count oras', + 'h' => ':count ora|:count oras', + 'minute' => ':count minuta|:count minutas', + 'min' => ':count minuta|:count minutas', + 'second' => ':count segonda|:count segondas', + 's' => ':count segonda|:count segondas', + 'ago' => 'fa :time', + 'from_now' => 'dins :time', + 'after' => ':time aprèp', + 'before' => ':time abans', + 'diff_now' => 'ara meteis', + 'diff_yesterday' => 'ièr', + 'diff_tomorrow' => 'deman', + 'diff_before_yesterday' => 'ièr delà', + 'diff_after_tomorrow' => 'deman passat', + 'period_recurrences' => ':count còp|:count còps', + 'period_interval' => 'cada :interval', + 'period_start_date' => 'de :date', + 'period_end_date' => 'fins a :date', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pl.php b/vendor/nesbot/carbon/src/Carbon/Lang/pl.php new file mode 100644 index 000000000..2308af2d0 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/pl.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count rok|:count lata|:count lat', + 'y' => ':countr|:countl', + 'month' => ':count miesiąc|:count miesiące|:count miesięcy', + 'm' => ':countmies', + 'week' => ':count tydzień|:count tygodnie|:count tygodni', + 'w' => ':counttyg', + 'day' => ':count dzień|:count dni|:count dni', + 'd' => ':countd', + 'hour' => ':count godzina|:count godziny|:count godzin', + 'h' => ':countg', + 'minute' => ':count minuta|:count minuty|:count minut', + 'min' => ':countm', + 'second' => ':count sekunda|:count sekundy|:count sekund', + 's' => ':counts', + 'ago' => ':time temu', + 'from_now' => ':time od teraz', + 'after' => ':time po', + 'before' => ':time przed', + 'diff_now' => 'przed chwilą', + 'diff_yesterday' => 'wczoraj', + 'diff_tomorrow' => 'jutro', + 'diff_before_yesterday' => 'przedwczoraj', + 'diff_after_tomorrow' => 'pojutrze', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ps.php b/vendor/nesbot/carbon/src/Carbon/Lang/ps.php new file mode 100644 index 000000000..15c3296e2 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ps.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count کال|:count کاله', + 'y' => ':countکال|:countکاله', + 'month' => ':count مياشت|:count مياشتي', + 'm' => ':countمياشت|:countمياشتي', + 'week' => ':count اونۍ|:count اونۍ', + 'w' => ':countاونۍ|:countاونۍ', + 'day' => ':count ورځ|:count ورځي', + 'd' => ':countورځ|:countورځي', + 'hour' => ':count ساعت|:count ساعته', + 'h' => ':countساعت|:countساعته', + 'minute' => ':count دقيقه|:count دقيقې', + 'min' => ':countدقيقه|:countدقيقې', + 'second' => ':count ثانيه|:count ثانيې', + 's' => ':countثانيه|:countثانيې', + 'ago' => ':time دمخه', + 'from_now' => ':time له اوس څخه', + 'after' => ':time وروسته', + 'before' => ':time دمخه', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pt.php b/vendor/nesbot/carbon/src/Carbon/Lang/pt.php new file mode 100644 index 000000000..392b12159 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/pt.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count ano|:count anos', + 'y' => ':count ano|:count anos', + 'month' => ':count mês|:count meses', + 'm' => ':count mês|:count meses', + 'week' => ':count semana|:count semanas', + 'w' => ':count semana|:count semanas', + 'day' => ':count dia|:count dias', + 'd' => ':count dia|:count dias', + 'hour' => ':count hora|:count horas', + 'h' => ':count hora|:count horas', + 'minute' => ':count minuto|:count minutos', + 'min' => ':count minuto|:count minutos', + 'second' => ':count segundo|:count segundos', + 's' => ':count segundo|:count segundos', + 'ago' => ':time atrás', + 'from_now' => 'em :time', + 'after' => ':time depois', + 'before' => ':time antes', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pt_BR.php b/vendor/nesbot/carbon/src/Carbon/Lang/pt_BR.php new file mode 100644 index 000000000..1f84eac5f --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/pt_BR.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count ano|:count anos', + 'y' => ':counta|:counta', + 'month' => ':count mês|:count meses', + 'm' => ':countm|:countm', + 'week' => ':count semana|:count semanas', + 'w' => ':countsem|:countsem', + 'day' => ':count dia|:count dias', + 'd' => ':countd|:countd', + 'hour' => ':count hora|:count horas', + 'h' => ':counth|:counth', + 'minute' => ':count minuto|:count minutos', + 'min' => ':countmin|:countmin', + 'second' => ':count segundo|:count segundos', + 's' => ':counts|:counts', + 'ago' => 'há :time', + 'from_now' => 'em :time', + 'after' => 'após :time', + 'before' => ':time atrás', + 'diff_now' => 'agora', + 'diff_yesterday' => 'ontem', + 'diff_tomorrow' => 'amanhã', + 'diff_before_yesterday' => 'anteontem', + 'diff_after_tomorrow' => 'depois de amanhã', + 'period_recurrences' => 'uma|:count vez', + 'period_interval' => 'toda :interval', + 'period_start_date' => 'de :date', + 'period_end_date' => 'até :date', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ro.php b/vendor/nesbot/carbon/src/Carbon/Lang/ro.php new file mode 100644 index 000000000..cc1672406 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ro.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => 'un an|:count ani|:count ani', + 'y' => 'un an|:count ani|:count ani', + 'month' => 'o lună|:count luni|:count luni', + 'm' => 'o lună|:count luni|:count luni', + 'week' => 'o săptămână|:count săptămâni|:count săptămâni', + 'w' => 'o săptămână|:count săptămâni|:count săptămâni', + 'day' => 'o zi|:count zile|:count zile', + 'd' => 'o zi|:count zile|:count zile', + 'hour' => 'o oră|:count ore|:count ore', + 'h' => 'o oră|:count ore|:count ore', + 'minute' => 'un minut|:count minute|:count minute', + 'min' => 'un minut|:count minute|:count minute', + 'second' => 'o secundă|:count secunde|:count secunde', + 's' => 'o secundă|:count secunde|:count secunde', + 'ago' => 'acum :time', + 'from_now' => ':time de acum', + 'after' => 'peste :time', + 'before' => 'acum :time', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ru.php b/vendor/nesbot/carbon/src/Carbon/Lang/ru.php new file mode 100644 index 000000000..6a83fb131 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ru.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count год|:count года|:count лет', + 'y' => ':count г|:count г|:count л', + 'month' => ':count месяц|:count месяца|:count месяцев', + 'm' => ':count м|:count м|:count м', + 'week' => ':count неделю|:count недели|:count недель', + 'w' => ':count н|:count н|:count н', + 'day' => ':count день|:count дня|:count дней', + 'd' => ':count д|:count д|:count д', + 'hour' => ':count час|:count часа|:count часов', + 'h' => ':count ч|:count ч|:count ч', + 'minute' => ':count минуту|:count минуты|:count минут', + 'min' => ':count мин|:count мин|:count мин', + 'second' => ':count секунду|:count секунды|:count секунд', + 's' => ':count с|:count с|:count с', + 'ago' => ':time назад', + 'from_now' => 'через :time', + 'after' => ':time после', + 'before' => ':time до', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sh.php b/vendor/nesbot/carbon/src/Carbon/Lang/sh.php new file mode 100644 index 000000000..57f287a7c --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sh.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +\Symfony\Component\Translation\PluralizationRules::set(function ($number) { + return ((1 == $number % 10) && (11 != $number % 100)) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2); +}, 'sh'); + +return array( + 'year' => ':count godina|:count godine|:count godina', + 'y' => ':count godina|:count godine|:count godina', + 'month' => ':count mesec|:count meseca|:count meseci', + 'm' => ':count mesec|:count meseca|:count meseci', + 'week' => ':count nedelja|:count nedelje|:count nedelja', + 'w' => ':count nedelja|:count nedelje|:count nedelja', + 'day' => ':count dan|:count dana|:count dana', + 'd' => ':count dan|:count dana|:count dana', + 'hour' => ':count čas|:count časa|:count časova', + 'h' => ':count čas|:count časa|:count časova', + 'minute' => ':count minut|:count minuta|:count minuta', + 'min' => ':count minut|:count minuta|:count minuta', + 'second' => ':count sekund|:count sekunda|:count sekundi', + 's' => ':count sekund|:count sekunda|:count sekundi', + 'ago' => 'pre :time', + 'from_now' => 'za :time', + 'after' => 'nakon :time', + 'before' => ':time raniјe', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sk.php b/vendor/nesbot/carbon/src/Carbon/Lang/sk.php new file mode 100644 index 000000000..61013448d --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sk.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => 'rok|:count roky|:count rokov', + 'y' => 'rok|:count roky|:count rokov', + 'month' => 'mesiac|:count mesiace|:count mesiacov', + 'm' => 'mesiac|:count mesiace|:count mesiacov', + 'week' => 'týždeň|:count týždne|:count týždňov', + 'w' => 'týždeň|:count týždne|:count týždňov', + 'day' => 'deň|:count dni|:count dní', + 'd' => 'deň|:count dni|:count dní', + 'hour' => 'hodinu|:count hodiny|:count hodín', + 'h' => 'hodinu|:count hodiny|:count hodín', + 'minute' => 'minútu|:count minúty|:count minút', + 'min' => 'minútu|:count minúty|:count minút', + 'second' => 'sekundu|:count sekundy|:count sekúnd', + 's' => 'sekundu|:count sekundy|:count sekúnd', + 'ago' => 'pred :time', + 'from_now' => 'za :time', + 'after' => 'o :time neskôr', + 'before' => ':time predtým', + 'year_ago' => 'rokom|:count rokmi|:count rokmi', + 'month_ago' => 'mesiacom|:count mesiacmi|:count mesiacmi', + 'week_ago' => 'týždňom|:count týždňami|:count týždňami', + 'day_ago' => 'dňom|:count dňami|:count dňami', + 'hour_ago' => 'hodinou|:count hodinami|:count hodinami', + 'minute_ago' => 'minútou|:count minútami|:count minútami', + 'second_ago' => 'sekundou|:count sekundami|:count sekundami', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sl.php b/vendor/nesbot/carbon/src/Carbon/Lang/sl.php new file mode 100644 index 000000000..06686d135 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sl.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count leto|:count leti|:count leta|:count let', + 'y' => ':count leto|:count leti|:count leta|:count let', + 'month' => ':count mesec|:count meseca|:count mesece|:count mesecev', + 'm' => ':count mesec|:count meseca|:count mesece|:count mesecev', + 'week' => ':count teden|:count tedna|:count tedne|:count tednov', + 'w' => ':count teden|:count tedna|:count tedne|:count tednov', + 'day' => ':count dan|:count dni|:count dni|:count dni', + 'd' => ':count dan|:count dni|:count dni|:count dni', + 'hour' => ':count uro|:count uri|:count ure|:count ur', + 'h' => ':count uro|:count uri|:count ure|:count ur', + 'minute' => ':count minuto|:count minuti|:count minute|:count minut', + 'min' => ':count minuto|:count minuti|:count minute|:count minut', + 'second' => ':count sekundo|:count sekundi|:count sekunde|:count sekund', + 's' => ':count sekundo|:count sekundi|:count sekunde|:count sekund', + 'year_ago' => ':count letom|:count leti|:count leti|:count leti', + 'month_ago' => ':count mesecem|:count meseci|:count meseci|:count meseci', + 'week_ago' => ':count tednom|:count tednoma|:count tedni|:count tedni', + 'day_ago' => ':count dnem|:count dnevoma|:count dnevi|:count dnevi', + 'hour_ago' => ':count uro|:count urama|:count urami|:count urami', + 'minute_ago' => ':count minuto|:count minutama|:count minutami|:count minutami', + 'second_ago' => ':count sekundo|:count sekundama|:count sekundami|:count sekundami', + 'ago' => 'pred :time', + 'from_now' => 'čez :time', + 'after' => 'čez :time', + 'before' => 'pred :time', + 'diff_now' => 'ravnokar', + 'diff_yesterday' => 'včeraj', + 'diff_tomorrow' => 'jutri', + 'diff_before_yesterday' => 'predvčerajšnjim', + 'diff_after_tomorrow' => 'pojutrišnjem', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sq.php b/vendor/nesbot/carbon/src/Carbon/Lang/sq.php new file mode 100644 index 000000000..6e138a041 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sq.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count vit|:count vjet', + 'y' => ':count vit|:count vjet', + 'month' => ':count muaj|:count muaj', + 'm' => ':count muaj|:count muaj', + 'week' => ':count javë|:count javë', + 'w' => ':count javë|:count javë', + 'day' => ':count ditë|:count ditë', + 'd' => ':count ditë|:count ditë', + 'hour' => ':count orë|:count orë', + 'h' => ':count orë|:count orë', + 'minute' => ':count minutë|:count minuta', + 'min' => ':count minutë|:count minuta', + 'second' => ':count sekondë|:count sekonda', + 's' => ':count sekondë|:count sekonda', + 'ago' => ':time më parë', + 'from_now' => ':time nga tani', + 'after' => ':time pas', + 'before' => ':time para', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sr.php b/vendor/nesbot/carbon/src/Carbon/Lang/sr.php new file mode 100644 index 000000000..5a1064212 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sr.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count godina|:count godine|:count godina', + 'y' => ':count godina|:count godine|:count godina', + 'month' => ':count mesec|:count meseca|:count meseci', + 'm' => ':count mesec|:count meseca|:count meseci', + 'week' => ':count nedelja|:count nedelje|:count nedelja', + 'w' => ':count nedelja|:count nedelje|:count nedelja', + 'day' => ':count dan|:count dana|:count dana', + 'd' => ':count dan|:count dana|:count dana', + 'hour' => ':count sat|:count sata|:count sati', + 'h' => ':count sat|:count sata|:count sati', + 'minute' => ':count minut|:count minuta |:count minuta', + 'min' => ':count minut|:count minuta |:count minuta', + 'second' => ':count sekund|:count sekunde|:count sekunde', + 's' => ':count sekund|:count sekunde|:count sekunde', + 'ago' => 'pre :time', + 'from_now' => ':time od sada', + 'after' => 'nakon :time', + 'before' => 'pre :time', + + 'year_from_now' => '{1,21,31,41,51} :count godinu|{2,3,4,22,23,24,32,33,34,42,43,44,52,53,54} :count godine|[5,Inf[ :count godina', + 'year_ago' => '{1,21,31,41,51} :count godinu|{2,3,4,22,23,24,32,33,34,42,43,44,52,53,54} :count godine|[5,Inf[ :count godina', + + 'week_from_now' => '{1} :count nedelju|{2,3,4} :count nedelje|[5,Inf[ :count nedelja', + 'week_ago' => '{1} :count nedelju|{2,3,4} :count nedelje|[5,Inf[ :count nedelja', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sr_Cyrl.php b/vendor/nesbot/carbon/src/Carbon/Lang/sr_Cyrl.php new file mode 100644 index 000000000..2db83edd9 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sr_Cyrl.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => '{2,3,4,22,23,24,32,33,34,42,43,44,52,53,54}:count године|[0,Inf[ :count година', + 'y' => ':count г.', + 'month' => '{1} :count месец|{2,3,4}:count месеца|[5,Inf[ :count месеци', + 'm' => ':count м.', + 'week' => '{1} :count недеља|{2,3,4}:count недеље|[5,Inf[ :count недеља', + 'w' => ':count нед.', + 'day' => '{1,21,31} :count дан|[2,Inf[ :count дана', + 'd' => ':count д.', + 'hour' => '{1,21} :count сат|{2,3,4,22,23,24}:count сата|[5,Inf[ :count сати', + 'h' => ':count ч.', + 'minute' => '{1,21,31,41,51} :count минут|[2,Inf[ :count минута', + 'min' => ':count мин.', + 'second' => '{1,21,31,41,51} :count секунд|{2,3,4,22,23,24,32,33,34,42,43,44,52,53,54}:count секунде|[5,Inf[:count секунди', + 's' => ':count сек.', + 'ago' => 'пре :time', + 'from_now' => 'за :time', + 'after' => ':time након', + 'before' => ':time пре', + + 'year_from_now' => '{1,21,31,41,51} :count годину|{2,3,4,22,23,24,32,33,34,42,43,44,52,53,54} :count године|[5,Inf[ :count година', + 'year_ago' => '{1,21,31,41,51} :count годину|{2,3,4,22,23,24,32,33,34,42,43,44,52,53,54} :count године|[5,Inf[ :count година', + + 'week_from_now' => '{1} :count недељу|{2,3,4} :count недеље|[5,Inf[ :count недеља', + 'week_ago' => '{1} :count недељу|{2,3,4} :count недеље|[5,Inf[ :count недеља', + + 'diff_now' => 'управо сада', + 'diff_yesterday' => 'јуче', + 'diff_tomorrow' => 'сутра', + 'diff_before_yesterday' => 'прекјуче', + 'diff_after_tomorrow' => 'прекосутра', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sr_Cyrl_ME.php b/vendor/nesbot/carbon/src/Carbon/Lang/sr_Cyrl_ME.php new file mode 100644 index 000000000..18214c44c --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sr_Cyrl_ME.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => '{2,3,4,22,23,24,32,33,34,42,43,44,52,53,54}:count године|[0,Inf[ :count година', + 'y' => ':count г.', + 'month' => '{1} :count мјесец|{2,3,4}:count мјесеца|[5,Inf[ :count мјесеци', + 'm' => ':count мј.', + 'week' => '{1} :count недјеља|{2,3,4}:count недјеље|[5,Inf[ :count недјеља', + 'w' => ':count нед.', + 'day' => '{1,21,31} :count дан|[2,Inf[ :count дана', + 'd' => ':count д.', + 'hour' => '{1,21} :count сат|{2,3,4,22,23,24}:count сата|[5,Inf[ :count сати', + 'h' => ':count ч.', + 'minute' => '{1,21,31,41,51} :count минут|[2,Inf[ :count минута', + 'min' => ':count мин.', + 'second' => '{1,21,31,41,51} :count секунд|{2,3,4,22,23,24,32,33,34,42,43,44,52,53,54}:count секунде|[5,Inf[:count секунди', + 's' => ':count сек.', + 'ago' => 'прије :time', + 'from_now' => 'за :time', + 'after' => ':time након', + 'before' => ':time прије', + + 'year_from_now' => '{1,21,31,41,51} :count годину|{2,3,4,22,23,24,32,33,34,42,43,44,52,53,54} :count године|[5,Inf[ :count година', + 'year_ago' => '{1,21,31,41,51} :count годину|{2,3,4,22,23,24,32,33,34,42,43,44,52,53,54} :count године|[5,Inf[ :count година', + + 'week_from_now' => '{1} :count недјељу|{2,3,4} :count недјеље|[5,Inf[ :count недјеља', + 'week_ago' => '{1} :count недјељу|{2,3,4} :count недјеље|[5,Inf[ :count недјеља', + + 'diff_now' => 'управо сада', + 'diff_yesterday' => 'јуче', + 'diff_tomorrow' => 'сутра', + 'diff_before_yesterday' => 'прекјуче', + 'diff_after_tomorrow' => 'прекосјутра', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sr_Latn_ME.php b/vendor/nesbot/carbon/src/Carbon/Lang/sr_Latn_ME.php new file mode 100644 index 000000000..2d2e28814 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sr_Latn_ME.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => '{2,3,4,22,23,24,32,33,34,42,43,44,52,53,54}:count godine|[0,Inf[ :count godina', + 'y' => ':count g.', + 'month' => '{1} :count mjesec|{2,3,4}:count mjeseca|[5,Inf[ :count mjeseci', + 'm' => ':count mj.', + 'week' => '{1} :count nedjelja|{2,3,4}:count nedjelje|[5,Inf[ :count nedjelja', + 'w' => ':count ned.', + 'day' => '{1,21,31} :count dan|[2,Inf[ :count dana', + 'd' => ':count d.', + 'hour' => '{1,21} :count sat|{2,3,4,22,23,24}:count sata|[5,Inf[ :count sati', + 'h' => ':count č.', + 'minute' => '{1,21,31,41,51} :count minut|[2,Inf[ :count minuta', + 'min' => ':count min.', + 'second' => '{1,21,31,41,51} :count sekund|{2,3,4,22,23,24,32,33,34,42,43,44,52,53,54}:count sekunde|[5,Inf[:count sekundi', + 's' => ':count sek.', + 'ago' => 'prije :time', + 'from_now' => 'za :time', + 'after' => ':time nakon', + 'before' => ':time prije', + + 'year_from_now' => '{1,21,31,41,51} :count godinu|{2,3,4,22,23,24,32,33,34,42,43,44,52,53,54} :count godine|[5,Inf[ :count godina', + 'year_ago' => '{1,21,31,41,51} :count godinu|{2,3,4,22,23,24,32,33,34,42,43,44,52,53,54} :count godine|[5,Inf[ :count godina', + + 'week_from_now' => '{1} :count nedjelju|{2,3,4} :count nedjelje|[5,Inf[ :count nedjelja', + 'week_ago' => '{1} :count nedjelju|{2,3,4} :count nedjelje|[5,Inf[ :count nedjelja', + + 'diff_now' => 'upravo sada', + 'diff_yesterday' => 'juče', + 'diff_tomorrow' => 'sutra', + 'diff_before_yesterday' => 'prekjuče', + 'diff_after_tomorrow' => 'preksutra', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sr_ME.php b/vendor/nesbot/carbon/src/Carbon/Lang/sr_ME.php new file mode 100644 index 000000000..7ebf6f041 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sr_ME.php @@ -0,0 +1,12 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return require __DIR__.'/sr_Latn_ME.php'; diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sv.php b/vendor/nesbot/carbon/src/Carbon/Lang/sv.php new file mode 100644 index 000000000..89a03b432 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sv.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count år|:count år', + 'y' => ':count år|:count år', + 'month' => ':count månad|:count månader', + 'm' => ':count månad|:count månader', + 'week' => ':count vecka|:count veckor', + 'w' => ':count vecka|:count veckor', + 'day' => ':count dag|:count dagar', + 'd' => ':count dag|:count dagar', + 'hour' => ':count timme|:count timmar', + 'h' => ':count timme|:count timmar', + 'minute' => ':count minut|:count minuter', + 'min' => ':count minut|:count minuter', + 'second' => ':count sekund|:count sekunder', + 's' => ':count sekund|:count sekunder', + 'ago' => ':time sedan', + 'from_now' => 'om :time', + 'after' => ':time efter', + 'before' => ':time före', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sw.php b/vendor/nesbot/carbon/src/Carbon/Lang/sw.php new file mode 100644 index 000000000..52f03429e --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/sw.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => 'mwaka 1|miaka :count', + 'y' => 'mwaka 1|miaka :count', + 'month' => 'mwezi 1|miezi :count', + 'm' => 'mwezi 1|miezi :count', + 'week' => 'wiki 1|wiki :count', + 'w' => 'wiki 1|wiki :count', + 'day' => 'siku 1|siku :count', + 'd' => 'siku 1|siku :count', + 'hour' => 'saa 1|masaa :count', + 'h' => 'saa 1|masaa :count', + 'minute' => 'dakika 1|dakika :count', + 'min' => 'dakika 1|dakika :count', + 'second' => 'sekunde 1|sekunde :count', + 's' => 'sekunde 1|sekunde :count', + 'ago' => ':time ziliyopita', + 'from_now' => ':time kwanzia sasa', + 'after' => ':time baada', + 'before' => ':time kabla', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/th.php b/vendor/nesbot/carbon/src/Carbon/Lang/th.php new file mode 100644 index 000000000..88bb4ac40 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/th.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count ปี', + 'y' => ':count ปี', + 'month' => ':count เดือน', + 'm' => ':count เดือน', + 'week' => ':count สัปดาห์', + 'w' => ':count สัปดาห์', + 'day' => ':count วัน', + 'd' => ':count วัน', + 'hour' => ':count ชั่วโมง', + 'h' => ':count ชั่วโมง', + 'minute' => ':count นาที', + 'min' => ':count นาที', + 'second' => ':count วินาที', + 's' => ':count วินาที', + 'ago' => ':timeที่แล้ว', + 'from_now' => ':timeต่อจากนี้', + 'after' => ':timeหลังจากนี้', + 'before' => ':timeก่อน', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tr.php b/vendor/nesbot/carbon/src/Carbon/Lang/tr.php new file mode 100644 index 000000000..6a9dfed87 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/tr.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count yıl', + 'y' => ':count yıl', + 'month' => ':count ay', + 'm' => ':count ay', + 'week' => ':count hafta', + 'w' => ':count hafta', + 'day' => ':count gün', + 'd' => ':count gün', + 'hour' => ':count saat', + 'h' => ':count saat', + 'minute' => ':count dakika', + 'min' => ':count dakika', + 'second' => ':count saniye', + 's' => ':count saniye', + 'ago' => ':time önce', + 'from_now' => ':time sonra', + 'after' => ':time sonra', + 'before' => ':time önce', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/uk.php b/vendor/nesbot/carbon/src/Carbon/Lang/uk.php new file mode 100644 index 000000000..8d08eaa4e --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/uk.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count рік|:count роки|:count років', + 'y' => ':count рік|:count роки|:count років', + 'month' => ':count місяць|:count місяці|:count місяців', + 'm' => ':count місяць|:count місяці|:count місяців', + 'week' => ':count тиждень|:count тижні|:count тижнів', + 'w' => ':count тиждень|:count тижні|:count тижнів', + 'day' => ':count день|:count дні|:count днів', + 'd' => ':count день|:count дні|:count днів', + 'hour' => ':count година|:count години|:count годин', + 'h' => ':count година|:count години|:count годин', + 'minute' => ':count хвилину|:count хвилини|:count хвилин', + 'min' => ':count хвилину|:count хвилини|:count хвилин', + 'second' => ':count секунду|:count секунди|:count секунд', + 's' => ':count секунду|:count секунди|:count секунд', + 'ago' => ':time тому', + 'from_now' => 'через :time', + 'after' => ':time після', + 'before' => ':time до', + 'diff_now' => 'щойно', + 'diff_yesterday' => 'вчора', + 'diff_tomorrow' => 'завтра', + 'diff_before_yesterday' => 'позавчора', + 'diff_after_tomorrow' => 'післязавтра', + 'period_recurrences' => 'один раз|:count рази|:count разів', + 'period_interval' => 'кожні :interval', + 'period_start_date' => 'з :date', + 'period_end_date' => 'до :date', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ur.php b/vendor/nesbot/carbon/src/Carbon/Lang/ur.php new file mode 100644 index 000000000..3c5f7ed91 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/ur.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count سال', + 'month' => ':count ماه', + 'week' => ':count ہفتے', + 'day' => ':count روز', + 'hour' => ':count گھنٹے', + 'minute' => ':count منٹ', + 'second' => ':count سیکنڈ', + 'ago' => ':time پہلے', + 'from_now' => ':time بعد', + 'after' => ':time بعد', + 'before' => ':time پہلے', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/uz.php b/vendor/nesbot/carbon/src/Carbon/Lang/uz.php new file mode 100644 index 000000000..1cb6f713e --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/uz.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count yil', + 'y' => ':count yil', + 'month' => ':count oy', + 'm' => ':count oy', + 'week' => ':count hafta', + 'w' => ':count hafta', + 'day' => ':count kun', + 'd' => ':count kun', + 'hour' => ':count soat', + 'h' => ':count soat', + 'minute' => ':count daqiqa', + 'min' => ':count daq', + 'second' => ':count soniya', + 's' => ':count s', + 'ago' => ':time avval', + 'from_now' => ':time dan keyin', + 'after' => ':time keyin', + 'before' => ':time oldin', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/vi.php b/vendor/nesbot/carbon/src/Carbon/Lang/vi.php new file mode 100644 index 000000000..3f9838d66 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/vi.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count năm', + 'y' => ':count năm', + 'month' => ':count tháng', + 'm' => ':count tháng', + 'week' => ':count tuần', + 'w' => ':count tuần', + 'day' => ':count ngày', + 'd' => ':count ngày', + 'hour' => ':count giờ', + 'h' => ':count giờ', + 'minute' => ':count phút', + 'min' => ':count phút', + 'second' => ':count giây', + 's' => ':count giây', + 'ago' => ':time trước', + 'from_now' => ':time từ bây giờ', + 'after' => ':time sau', + 'before' => ':time trước', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/zh.php b/vendor/nesbot/carbon/src/Carbon/Lang/zh.php new file mode 100644 index 000000000..9e1f6cad5 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/zh.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count年', + 'y' => ':count年', + 'month' => ':count个月', + 'm' => ':count个月', + 'week' => ':count周', + 'w' => ':count周', + 'day' => ':count天', + 'd' => ':count天', + 'hour' => ':count小时', + 'h' => ':count小时', + 'minute' => ':count分钟', + 'min' => ':count分钟', + 'second' => ':count秒', + 's' => ':count秒', + 'ago' => ':time前', + 'from_now' => '距现在:time', + 'after' => ':time后', + 'before' => ':time前', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/zh_TW.php b/vendor/nesbot/carbon/src/Carbon/Lang/zh_TW.php new file mode 100644 index 000000000..c848723bc --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Lang/zh_TW.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + 'year' => ':count年', + 'y' => ':count年', + 'month' => ':count月', + 'm' => ':count月', + 'week' => ':count週', + 'w' => ':count週', + 'day' => ':count天', + 'd' => ':count天', + 'hour' => ':count小時', + 'h' => ':count小時', + 'minute' => ':count分鐘', + 'min' => ':count分鐘', + 'second' => ':count秒', + 's' => ':count秒', + 'ago' => ':time前', + 'from_now' => '距現在:time', + 'after' => ':time後', + 'before' => ':time前', +); diff --git a/vendor/nesbot/carbon/src/Carbon/Laravel/ServiceProvider.php b/vendor/nesbot/carbon/src/Carbon/Laravel/ServiceProvider.php new file mode 100644 index 000000000..4d83b0c65 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Laravel/ServiceProvider.php @@ -0,0 +1,37 @@ +app['events']; + if ($events instanceof EventDispatcher || $events instanceof Dispatcher) { + $events->listen(class_exists('Illuminate\Foundation\Events\LocaleUpdated') ? 'Illuminate\Foundation\Events\LocaleUpdated' : 'locale.changed', function () use ($service) { + $service->updateLocale(); + }); + $service->updateLocale(); + } + } + + public function updateLocale() + { + $translator = $this->app['translator']; + if ($translator instanceof Translator || $translator instanceof IlluminateTranslator) { + Carbon::setLocale($translator->getLocale()); + } + } + + public function register() + { + // Needed for Laravel < 5.3 compatibility + } +} diff --git a/vendor/nesbot/carbon/src/Carbon/Translator.php b/vendor/nesbot/carbon/src/Carbon/Translator.php new file mode 100644 index 000000000..12115b008 --- /dev/null +++ b/vendor/nesbot/carbon/src/Carbon/Translator.php @@ -0,0 +1,143 @@ +addLoader('array', new Translation\Loader\ArrayLoader()); + parent::__construct($locale, $formatter, $cacheDir, $debug); + } + + /** + * Reset messages of a locale (all locale if no locale passed). + * Remove custom messages and reload initial messages from matching + * file in Lang directory. + * + * @param string|null $locale + * + * @return bool + */ + public function resetMessages($locale = null) + { + if ($locale === null) { + static::$messages = array(); + + return true; + } + + if (file_exists($filename = __DIR__.'/Lang/'.$locale.'.php')) { + static::$messages[$locale] = require $filename; + $this->addResource('array', static::$messages[$locale], $locale); + + return true; + } + + return false; + } + + /** + * Init messages language from matching file in Lang directory. + * + * @param string $locale + * + * @return bool + */ + protected function loadMessagesFromFile($locale) + { + if (isset(static::$messages[$locale])) { + return true; + } + + return $this->resetMessages($locale); + } + + /** + * Set messages of a locale and take file first if present. + * + * @param string $locale + * @param array $messages + * + * @return $this + */ + public function setMessages($locale, $messages) + { + $this->loadMessagesFromFile($locale); + $this->addResource('array', $messages, $locale); + static::$messages[$locale] = array_merge( + isset(static::$messages[$locale]) ? static::$messages[$locale] : array(), + $messages + ); + + return $this; + } + + /** + * Get messages of a locale, if none given, return all the + * languages. + * + * @param string|null $locale + * + * @return array + */ + public function getMessages($locale = null) + { + return $locale === null ? static::$messages : static::$messages[$locale]; + } + + /** + * Set the current translator locale and indicate if the source locale file exists + * + * @param string $locale locale ex. en + * + * @return bool + */ + public function setLocale($locale) + { + $locale = preg_replace_callback('/[-_]([a-z]{2,})/', function ($matches) { + // _2-letters is a region, _3+-letters is a variant + return '_'.call_user_func(strlen($matches[1]) > 2 ? 'ucfirst' : 'strtoupper', $matches[1]); + }, strtolower($locale)); + + if ($this->loadMessagesFromFile($locale)) { + parent::setLocale($locale); + + return true; + } + + return false; + } +} diff --git a/vendor/nesbot/carbon/src/JsonSerializable.php b/vendor/nesbot/carbon/src/JsonSerializable.php new file mode 100644 index 000000000..d34060b44 --- /dev/null +++ b/vendor/nesbot/carbon/src/JsonSerializable.php @@ -0,0 +1,18 @@ +json_encode, + * which is a value of any type other than a resource. + * + * @since 5.4.0 + */ + public function jsonSerialize(); + } +} diff --git a/vendor/symfony/polyfill-mbstring/LICENSE b/vendor/symfony/polyfill-mbstring/LICENSE new file mode 100644 index 000000000..24fa32c2e --- /dev/null +++ b/vendor/symfony/polyfill-mbstring/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2015-2018 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/symfony/polyfill-mbstring/Mbstring.php b/vendor/symfony/polyfill-mbstring/Mbstring.php new file mode 100644 index 000000000..a5e4a8fde --- /dev/null +++ b/vendor/symfony/polyfill-mbstring/Mbstring.php @@ -0,0 +1,800 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Polyfill\Mbstring; + +/** + * Partial mbstring implementation in PHP, iconv based, UTF-8 centric. + * + * Implemented: + * - mb_chr - Returns a specific character from its Unicode code point + * - mb_convert_encoding - Convert character encoding + * - mb_convert_variables - Convert character code in variable(s) + * - mb_decode_mimeheader - Decode string in MIME header field + * - mb_encode_mimeheader - Encode string for MIME header XXX NATIVE IMPLEMENTATION IS REALLY BUGGED + * - mb_decode_numericentity - Decode HTML numeric string reference to character + * - mb_encode_numericentity - Encode character to HTML numeric string reference + * - mb_convert_case - Perform case folding on a string + * - mb_detect_encoding - Detect character encoding + * - mb_get_info - Get internal settings of mbstring + * - mb_http_input - Detect HTTP input character encoding + * - mb_http_output - Set/Get HTTP output character encoding + * - mb_internal_encoding - Set/Get internal character encoding + * - mb_list_encodings - Returns an array of all supported encodings + * - mb_ord - Returns the Unicode code point of a character + * - mb_output_handler - Callback function converts character encoding in output buffer + * - mb_scrub - Replaces ill-formed byte sequences with substitute characters + * - mb_strlen - Get string length + * - mb_strpos - Find position of first occurrence of string in a string + * - mb_strrpos - Find position of last occurrence of a string in a string + * - mb_strtolower - Make a string lowercase + * - mb_strtoupper - Make a string uppercase + * - mb_substitute_character - Set/Get substitution character + * - mb_substr - Get part of string + * - mb_stripos - Finds position of first occurrence of a string within another, case insensitive + * - mb_stristr - Finds first occurrence of a string within another, case insensitive + * - mb_strrchr - Finds the last occurrence of a character in a string within another + * - mb_strrichr - Finds the last occurrence of a character in a string within another, case insensitive + * - mb_strripos - Finds position of last occurrence of a string within another, case insensitive + * - mb_strstr - Finds first occurrence of a string within another + * - mb_strwidth - Return width of string + * - mb_substr_count - Count the number of substring occurrences + * + * Not implemented: + * - mb_convert_kana - Convert "kana" one from another ("zen-kaku", "han-kaku" and more) + * - mb_ereg_* - Regular expression with multibyte support + * - mb_parse_str - Parse GET/POST/COOKIE data and set global variable + * - mb_preferred_mime_name - Get MIME charset string + * - mb_regex_encoding - Returns current encoding for multibyte regex as string + * - mb_regex_set_options - Set/Get the default options for mbregex functions + * - mb_send_mail - Send encoded mail + * - mb_split - Split multibyte string using regular expression + * - mb_strcut - Get part of string + * - mb_strimwidth - Get truncated string with specified width + * + * @author Nicolas Grekas + * + * @internal + */ +final class Mbstring +{ + const MB_CASE_FOLD = PHP_INT_MAX; + + private static $encodingList = array('ASCII', 'UTF-8'); + private static $language = 'neutral'; + private static $internalEncoding = 'UTF-8'; + private static $caseFold = array( + array('µ', 'ſ', "\xCD\x85", 'ς', "\xCF\x90", "\xCF\x91", "\xCF\x95", "\xCF\x96", "\xCF\xB0", "\xCF\xB1", "\xCF\xB5", "\xE1\xBA\x9B", "\xE1\xBE\xBE"), + array('μ', 's', 'ι', 'σ', 'β', 'θ', 'φ', 'π', 'κ', 'ρ', 'ε', "\xE1\xB9\xA1", 'ι'), + ); + + public static function mb_convert_encoding($s, $toEncoding, $fromEncoding = null) + { + if (\is_array($fromEncoding) || false !== strpos($fromEncoding, ',')) { + $fromEncoding = self::mb_detect_encoding($s, $fromEncoding); + } else { + $fromEncoding = self::getEncoding($fromEncoding); + } + + $toEncoding = self::getEncoding($toEncoding); + + if ('BASE64' === $fromEncoding) { + $s = base64_decode($s); + $fromEncoding = $toEncoding; + } + + if ('BASE64' === $toEncoding) { + return base64_encode($s); + } + + if ('HTML-ENTITIES' === $toEncoding || 'HTML' === $toEncoding) { + if ('HTML-ENTITIES' === $fromEncoding || 'HTML' === $fromEncoding) { + $fromEncoding = 'Windows-1252'; + } + if ('UTF-8' !== $fromEncoding) { + $s = iconv($fromEncoding, 'UTF-8//IGNORE', $s); + } + + return preg_replace_callback('/[\x80-\xFF]+/', array(__CLASS__, 'html_encoding_callback'), $s); + } + + if ('HTML-ENTITIES' === $fromEncoding) { + $s = html_entity_decode($s, ENT_COMPAT, 'UTF-8'); + $fromEncoding = 'UTF-8'; + } + + return iconv($fromEncoding, $toEncoding.'//IGNORE', $s); + } + + public static function mb_convert_variables($toEncoding, $fromEncoding, &$a = null, &$b = null, &$c = null, &$d = null, &$e = null, &$f = null) + { + $vars = array(&$a, &$b, &$c, &$d, &$e, &$f); + + $ok = true; + array_walk_recursive($vars, function (&$v) use (&$ok, $toEncoding, $fromEncoding) { + if (false === $v = Mbstring::mb_convert_encoding($v, $toEncoding, $fromEncoding)) { + $ok = false; + } + }); + + return $ok ? $fromEncoding : false; + } + + public static function mb_decode_mimeheader($s) + { + return iconv_mime_decode($s, 2, self::$internalEncoding); + } + + public static function mb_encode_mimeheader($s, $charset = null, $transferEncoding = null, $linefeed = null, $indent = null) + { + trigger_error('mb_encode_mimeheader() is bugged. Please use iconv_mime_encode() instead', E_USER_WARNING); + } + + public static function mb_decode_numericentity($s, $convmap, $encoding = null) + { + if (null !== $s && !\is_scalar($s) && !(\is_object($s) && \method_exists($s, '__toString'))) { + trigger_error('mb_decode_numericentity() expects parameter 1 to be string, '.\gettype($s).' given', E_USER_WARNING); + + return null; + } + + if (!\is_array($convmap) || !$convmap) { + return false; + } + + if (null !== $encoding && !\is_scalar($encoding)) { + trigger_error('mb_decode_numericentity() expects parameter 3 to be string, '.\gettype($s).' given', E_USER_WARNING); + + return ''; // Instead of null (cf. mb_encode_numericentity). + } + + $s = (string) $s; + if ('' === $s) { + return ''; + } + + $encoding = self::getEncoding($encoding); + + if ('UTF-8' === $encoding) { + $encoding = null; + if (!preg_match('//u', $s)) { + $s = @iconv('UTF-8', 'UTF-8//IGNORE', $s); + } + } else { + $s = iconv($encoding, 'UTF-8//IGNORE', $s); + } + + $cnt = floor(\count($convmap) / 4) * 4; + + for ($i = 0; $i < $cnt; $i += 4) { + // collector_decode_htmlnumericentity ignores $convmap[$i + 3] + $convmap[$i] += $convmap[$i + 2]; + $convmap[$i + 1] += $convmap[$i + 2]; + } + + $s = preg_replace_callback('/&#(?:0*([0-9]+)|x0*([0-9a-fA-F]+))(?!&);?/', function (array $m) use ($cnt, $convmap) { + $c = isset($m[2]) ? (int) hexdec($m[2]) : $m[1]; + for ($i = 0; $i < $cnt; $i += 4) { + if ($c >= $convmap[$i] && $c <= $convmap[$i + 1]) { + return Mbstring::mb_chr($c - $convmap[$i + 2]); + } + } + + return $m[0]; + }, $s); + + if (null === $encoding) { + return $s; + } + + return iconv('UTF-8', $encoding.'//IGNORE', $s); + } + + public static function mb_encode_numericentity($s, $convmap, $encoding = null, $is_hex = false) + { + if (null !== $s && !\is_scalar($s) && !(\is_object($s) && \method_exists($s, '__toString'))) { + trigger_error('mb_encode_numericentity() expects parameter 1 to be string, '.\gettype($s).' given', E_USER_WARNING); + + return null; + } + + if (!\is_array($convmap) || !$convmap) { + return false; + } + + if (null !== $encoding && !\is_scalar($encoding)) { + trigger_error('mb_encode_numericentity() expects parameter 3 to be string, '.\gettype($s).' given', E_USER_WARNING); + + return null; // Instead of '' (cf. mb_decode_numericentity). + } + + if (null !== $is_hex && !\is_scalar($is_hex)) { + trigger_error('mb_encode_numericentity() expects parameter 4 to be boolean, '.\gettype($s).' given', E_USER_WARNING); + + return null; + } + + $s = (string) $s; + if ('' === $s) { + return ''; + } + + $encoding = self::getEncoding($encoding); + + if ('UTF-8' === $encoding) { + $encoding = null; + if (!preg_match('//u', $s)) { + $s = @iconv('UTF-8', 'UTF-8//IGNORE', $s); + } + } else { + $s = iconv($encoding, 'UTF-8//IGNORE', $s); + } + + static $ulenMask = array("\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4); + + $cnt = floor(\count($convmap) / 4) * 4; + $i = 0; + $len = \strlen($s); + $result = ''; + + while ($i < $len) { + $ulen = $s[$i] < "\x80" ? 1 : $ulenMask[$s[$i] & "\xF0"]; + $uchr = substr($s, $i, $ulen); + $i += $ulen; + $c = self::mb_ord($uchr); + + for ($j = 0; $j < $cnt; $j += 4) { + if ($c >= $convmap[$j] && $c <= $convmap[$j + 1]) { + $cOffset = ($c + $convmap[$j + 2]) & $convmap[$j + 3]; + $result .= $is_hex ? sprintf('&#x%X;', $cOffset) : '&#'.$cOffset.';'; + continue 2; + } + } + $result .= $uchr; + } + + if (null === $encoding) { + return $result; + } + + return iconv('UTF-8', $encoding.'//IGNORE', $result); + } + + public static function mb_convert_case($s, $mode, $encoding = null) + { + $s = (string) $s; + if ('' === $s) { + return ''; + } + + $encoding = self::getEncoding($encoding); + + if ('UTF-8' === $encoding) { + $encoding = null; + if (!preg_match('//u', $s)) { + $s = @iconv('UTF-8', 'UTF-8//IGNORE', $s); + } + } else { + $s = iconv($encoding, 'UTF-8//IGNORE', $s); + } + + if (MB_CASE_TITLE == $mode) { + static $titleRegexp = null; + if (null === $titleRegexp) { + $titleRegexp = self::getData('titleCaseRegexp'); + } + $s = preg_replace_callback($titleRegexp, array(__CLASS__, 'title_case'), $s); + } else { + if (MB_CASE_UPPER == $mode) { + static $upper = null; + if (null === $upper) { + $upper = self::getData('upperCase'); + } + $map = $upper; + } else { + if (self::MB_CASE_FOLD === $mode) { + $s = str_replace(self::$caseFold[0], self::$caseFold[1], $s); + } + + static $lower = null; + if (null === $lower) { + $lower = self::getData('lowerCase'); + } + $map = $lower; + } + + static $ulenMask = array("\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4); + + $i = 0; + $len = \strlen($s); + + while ($i < $len) { + $ulen = $s[$i] < "\x80" ? 1 : $ulenMask[$s[$i] & "\xF0"]; + $uchr = substr($s, $i, $ulen); + $i += $ulen; + + if (isset($map[$uchr])) { + $uchr = $map[$uchr]; + $nlen = \strlen($uchr); + + if ($nlen == $ulen) { + $nlen = $i; + do { + $s[--$nlen] = $uchr[--$ulen]; + } while ($ulen); + } else { + $s = substr_replace($s, $uchr, $i - $ulen, $ulen); + $len += $nlen - $ulen; + $i += $nlen - $ulen; + } + } + } + } + + if (null === $encoding) { + return $s; + } + + return iconv('UTF-8', $encoding.'//IGNORE', $s); + } + + public static function mb_internal_encoding($encoding = null) + { + if (null === $encoding) { + return self::$internalEncoding; + } + + $encoding = self::getEncoding($encoding); + + if ('UTF-8' === $encoding || false !== @iconv($encoding, $encoding, ' ')) { + self::$internalEncoding = $encoding; + + return true; + } + + return false; + } + + public static function mb_language($lang = null) + { + if (null === $lang) { + return self::$language; + } + + switch ($lang = strtolower($lang)) { + case 'uni': + case 'neutral': + self::$language = $lang; + + return true; + } + + return false; + } + + public static function mb_list_encodings() + { + return array('UTF-8'); + } + + public static function mb_encoding_aliases($encoding) + { + switch (strtoupper($encoding)) { + case 'UTF8': + case 'UTF-8': + return array('utf8'); + } + + return false; + } + + public static function mb_check_encoding($var = null, $encoding = null) + { + if (null === $encoding) { + if (null === $var) { + return false; + } + $encoding = self::$internalEncoding; + } + + return self::mb_detect_encoding($var, array($encoding)) || false !== @iconv($encoding, $encoding, $var); + } + + public static function mb_detect_encoding($str, $encodingList = null, $strict = false) + { + if (null === $encodingList) { + $encodingList = self::$encodingList; + } else { + if (!\is_array($encodingList)) { + $encodingList = array_map('trim', explode(',', $encodingList)); + } + $encodingList = array_map('strtoupper', $encodingList); + } + + foreach ($encodingList as $enc) { + switch ($enc) { + case 'ASCII': + if (!preg_match('/[\x80-\xFF]/', $str)) { + return $enc; + } + break; + + case 'UTF8': + case 'UTF-8': + if (preg_match('//u', $str)) { + return 'UTF-8'; + } + break; + + default: + if (0 === strncmp($enc, 'ISO-8859-', 9)) { + return $enc; + } + } + } + + return false; + } + + public static function mb_detect_order($encodingList = null) + { + if (null === $encodingList) { + return self::$encodingList; + } + + if (!\is_array($encodingList)) { + $encodingList = array_map('trim', explode(',', $encodingList)); + } + $encodingList = array_map('strtoupper', $encodingList); + + foreach ($encodingList as $enc) { + switch ($enc) { + default: + if (strncmp($enc, 'ISO-8859-', 9)) { + return false; + } + // no break + case 'ASCII': + case 'UTF8': + case 'UTF-8': + } + } + + self::$encodingList = $encodingList; + + return true; + } + + public static function mb_strlen($s, $encoding = null) + { + $encoding = self::getEncoding($encoding); + if ('CP850' === $encoding || 'ASCII' === $encoding) { + return \strlen($s); + } + + return @iconv_strlen($s, $encoding); + } + + public static function mb_strpos($haystack, $needle, $offset = 0, $encoding = null) + { + $encoding = self::getEncoding($encoding); + if ('CP850' === $encoding || 'ASCII' === $encoding) { + return strpos($haystack, $needle, $offset); + } + + $needle = (string) $needle; + if ('' === $needle) { + trigger_error(__METHOD__.': Empty delimiter', E_USER_WARNING); + + return false; + } + + return iconv_strpos($haystack, $needle, $offset, $encoding); + } + + public static function mb_strrpos($haystack, $needle, $offset = 0, $encoding = null) + { + $encoding = self::getEncoding($encoding); + if ('CP850' === $encoding || 'ASCII' === $encoding) { + return strrpos($haystack, $needle, $offset); + } + + if ($offset != (int) $offset) { + $offset = 0; + } elseif ($offset = (int) $offset) { + if ($offset < 0) { + $haystack = self::mb_substr($haystack, 0, $offset, $encoding); + $offset = 0; + } else { + $haystack = self::mb_substr($haystack, $offset, 2147483647, $encoding); + } + } + + $pos = iconv_strrpos($haystack, $needle, $encoding); + + return false !== $pos ? $offset + $pos : false; + } + + public static function mb_strtolower($s, $encoding = null) + { + return self::mb_convert_case($s, MB_CASE_LOWER, $encoding); + } + + public static function mb_strtoupper($s, $encoding = null) + { + return self::mb_convert_case($s, MB_CASE_UPPER, $encoding); + } + + public static function mb_substitute_character($c = null) + { + if (0 === strcasecmp($c, 'none')) { + return true; + } + + return null !== $c ? false : 'none'; + } + + public static function mb_substr($s, $start, $length = null, $encoding = null) + { + $encoding = self::getEncoding($encoding); + if ('CP850' === $encoding || 'ASCII' === $encoding) { + return substr($s, $start, null === $length ? 2147483647 : $length); + } + + if ($start < 0) { + $start = iconv_strlen($s, $encoding) + $start; + if ($start < 0) { + $start = 0; + } + } + + if (null === $length) { + $length = 2147483647; + } elseif ($length < 0) { + $length = iconv_strlen($s, $encoding) + $length - $start; + if ($length < 0) { + return ''; + } + } + + return (string) iconv_substr($s, $start, $length, $encoding); + } + + public static function mb_stripos($haystack, $needle, $offset = 0, $encoding = null) + { + $haystack = self::mb_convert_case($haystack, self::MB_CASE_FOLD, $encoding); + $needle = self::mb_convert_case($needle, self::MB_CASE_FOLD, $encoding); + + return self::mb_strpos($haystack, $needle, $offset, $encoding); + } + + public static function mb_stristr($haystack, $needle, $part = false, $encoding = null) + { + $pos = self::mb_stripos($haystack, $needle, 0, $encoding); + + return self::getSubpart($pos, $part, $haystack, $encoding); + } + + public static function mb_strrchr($haystack, $needle, $part = false, $encoding = null) + { + $encoding = self::getEncoding($encoding); + if ('CP850' === $encoding || 'ASCII' === $encoding) { + return strrchr($haystack, $needle, $part); + } + $needle = self::mb_substr($needle, 0, 1, $encoding); + $pos = iconv_strrpos($haystack, $needle, $encoding); + + return self::getSubpart($pos, $part, $haystack, $encoding); + } + + public static function mb_strrichr($haystack, $needle, $part = false, $encoding = null) + { + $needle = self::mb_substr($needle, 0, 1, $encoding); + $pos = self::mb_strripos($haystack, $needle, $encoding); + + return self::getSubpart($pos, $part, $haystack, $encoding); + } + + public static function mb_strripos($haystack, $needle, $offset = 0, $encoding = null) + { + $haystack = self::mb_convert_case($haystack, self::MB_CASE_FOLD, $encoding); + $needle = self::mb_convert_case($needle, self::MB_CASE_FOLD, $encoding); + + return self::mb_strrpos($haystack, $needle, $offset, $encoding); + } + + public static function mb_strstr($haystack, $needle, $part = false, $encoding = null) + { + $pos = strpos($haystack, $needle); + if (false === $pos) { + return false; + } + if ($part) { + return substr($haystack, 0, $pos); + } + + return substr($haystack, $pos); + } + + public static function mb_get_info($type = 'all') + { + $info = array( + 'internal_encoding' => self::$internalEncoding, + 'http_output' => 'pass', + 'http_output_conv_mimetypes' => '^(text/|application/xhtml\+xml)', + 'func_overload' => 0, + 'func_overload_list' => 'no overload', + 'mail_charset' => 'UTF-8', + 'mail_header_encoding' => 'BASE64', + 'mail_body_encoding' => 'BASE64', + 'illegal_chars' => 0, + 'encoding_translation' => 'Off', + 'language' => self::$language, + 'detect_order' => self::$encodingList, + 'substitute_character' => 'none', + 'strict_detection' => 'Off', + ); + + if ('all' === $type) { + return $info; + } + if (isset($info[$type])) { + return $info[$type]; + } + + return false; + } + + public static function mb_http_input($type = '') + { + return false; + } + + public static function mb_http_output($encoding = null) + { + return null !== $encoding ? 'pass' === $encoding : 'pass'; + } + + public static function mb_strwidth($s, $encoding = null) + { + $encoding = self::getEncoding($encoding); + + if ('UTF-8' !== $encoding) { + $s = iconv($encoding, 'UTF-8//IGNORE', $s); + } + + $s = preg_replace('/[\x{1100}-\x{115F}\x{2329}\x{232A}\x{2E80}-\x{303E}\x{3040}-\x{A4CF}\x{AC00}-\x{D7A3}\x{F900}-\x{FAFF}\x{FE10}-\x{FE19}\x{FE30}-\x{FE6F}\x{FF00}-\x{FF60}\x{FFE0}-\x{FFE6}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}]/u', '', $s, -1, $wide); + + return ($wide << 1) + iconv_strlen($s, 'UTF-8'); + } + + public static function mb_substr_count($haystack, $needle, $encoding = null) + { + return substr_count($haystack, $needle); + } + + public static function mb_output_handler($contents, $status) + { + return $contents; + } + + public static function mb_chr($code, $encoding = null) + { + if (0x80 > $code %= 0x200000) { + $s = \chr($code); + } elseif (0x800 > $code) { + $s = \chr(0xC0 | $code >> 6).\chr(0x80 | $code & 0x3F); + } elseif (0x10000 > $code) { + $s = \chr(0xE0 | $code >> 12).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F); + } else { + $s = \chr(0xF0 | $code >> 18).\chr(0x80 | $code >> 12 & 0x3F).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F); + } + + if ('UTF-8' !== $encoding = self::getEncoding($encoding)) { + $s = mb_convert_encoding($s, $encoding, 'UTF-8'); + } + + return $s; + } + + public static function mb_ord($s, $encoding = null) + { + if ('UTF-8' !== $encoding = self::getEncoding($encoding)) { + $s = mb_convert_encoding($s, 'UTF-8', $encoding); + } + + if (1 === \strlen($s)) { + return \ord($s); + } + + $code = ($s = unpack('C*', substr($s, 0, 4))) ? $s[1] : 0; + if (0xF0 <= $code) { + return (($code - 0xF0) << 18) + (($s[2] - 0x80) << 12) + (($s[3] - 0x80) << 6) + $s[4] - 0x80; + } + if (0xE0 <= $code) { + return (($code - 0xE0) << 12) + (($s[2] - 0x80) << 6) + $s[3] - 0x80; + } + if (0xC0 <= $code) { + return (($code - 0xC0) << 6) + $s[2] - 0x80; + } + + return $code; + } + + private static function getSubpart($pos, $part, $haystack, $encoding) + { + if (false === $pos) { + return false; + } + if ($part) { + return self::mb_substr($haystack, 0, $pos, $encoding); + } + + return self::mb_substr($haystack, $pos, null, $encoding); + } + + private static function html_encoding_callback(array $m) + { + $i = 1; + $entities = ''; + $m = unpack('C*', htmlentities($m[0], ENT_COMPAT, 'UTF-8')); + + while (isset($m[$i])) { + if (0x80 > $m[$i]) { + $entities .= \chr($m[$i++]); + continue; + } + if (0xF0 <= $m[$i]) { + $c = (($m[$i++] - 0xF0) << 18) + (($m[$i++] - 0x80) << 12) + (($m[$i++] - 0x80) << 6) + $m[$i++] - 0x80; + } elseif (0xE0 <= $m[$i]) { + $c = (($m[$i++] - 0xE0) << 12) + (($m[$i++] - 0x80) << 6) + $m[$i++] - 0x80; + } else { + $c = (($m[$i++] - 0xC0) << 6) + $m[$i++] - 0x80; + } + + $entities .= '&#'.$c.';'; + } + + return $entities; + } + + private static function title_case(array $s) + { + return self::mb_convert_case($s[1], MB_CASE_UPPER, 'UTF-8').self::mb_convert_case($s[2], MB_CASE_LOWER, 'UTF-8'); + } + + private static function getData($file) + { + if (file_exists($file = __DIR__.'/Resources/unidata/'.$file.'.php')) { + return require $file; + } + + return false; + } + + private static function getEncoding($encoding) + { + if (null === $encoding) { + return self::$internalEncoding; + } + + $encoding = strtoupper($encoding); + + if ('8BIT' === $encoding || 'BINARY' === $encoding) { + return 'CP850'; + } + if ('UTF8' === $encoding) { + return 'UTF-8'; + } + + return $encoding; + } +} diff --git a/vendor/symfony/polyfill-mbstring/README.md b/vendor/symfony/polyfill-mbstring/README.md new file mode 100644 index 000000000..342e8286d --- /dev/null +++ b/vendor/symfony/polyfill-mbstring/README.md @@ -0,0 +1,13 @@ +Symfony Polyfill / Mbstring +=========================== + +This component provides a partial, native PHP implementation for the +[Mbstring](http://php.net/mbstring) extension. + +More information can be found in the +[main Polyfill README](https://github.com/symfony/polyfill/blob/master/README.md). + +License +======= + +This library is released under the [MIT license](LICENSE). diff --git a/vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php b/vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php new file mode 100644 index 000000000..e6fbfa64e --- /dev/null +++ b/vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php @@ -0,0 +1,1096 @@ + '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 000000000..2a8f6e73b --- /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', + '𐐨' => '𐐀', + '𐐩' => '𐐁', + '𐐪' => '𐐂', + '𐐫' => '𐐃', + '𐐬' => '𐐄', + '𐐭' => '𐐅', + '𐐮' => '𐐆', + '𐐯' => '𐐇', + '𐐰' => '𐐈', + '𐐱' => '𐐉', + '𐐲' => '𐐊', + '𐐳' => '𐐋', + '𐐴' => '𐐌', + '𐐵' => '𐐍', + '𐐶' => '𐐎', + '𐐷' => '𐐏', + '𐐸' => '𐐐', + '𐐹' => '𐐑', + '𐐺' => '𐐒', + '𐐻' => '𐐓', + '𐐼' => '𐐔', + '𐐽' => '𐐕', + '𐐾' => '𐐖', + '𐐿' => '𐐗', + '𐑀' => '𐐘', + '𐑁' => '𐐙', + '𐑂' => '𐐚', + '𐑃' => '𐐛', + '𐑄' => '𐐜', + '𐑅' => '𐐝', + '𐑆' => '𐐞', + '𐑇' => '𐐟', + '𐑈' => '𐐠', + '𐑉' => '𐐡', + '𐑊' => '𐐢', + '𐑋' => '𐐣', + '𐑌' => '𐐤', + '𐑍' => '𐐥', + '𐑎' => '𐐦', + '𐑏' => '𐐧', + '𑣀' => '𑢠', + '𑣁' => '𑢡', + '𑣂' => '𑢢', + '𑣃' => '𑢣', + '𑣄' => '𑢤', + '𑣅' => '𑢥', + '𑣆' => '𑢦', + '𑣇' => '𑢧', + '𑣈' => '𑢨', + '𑣉' => '𑢩', + '𑣊' => '𑢪', + '𑣋' => '𑢫', + '𑣌' => '𑢬', + '𑣍' => '𑢭', + '𑣎' => '𑢮', + '𑣏' => '𑢯', + '𑣐' => '𑢰', + '𑣑' => '𑢱', + '𑣒' => '𑢲', + '𑣓' => '𑢳', + '𑣔' => '𑢴', + '𑣕' => '𑢵', + '𑣖' => '𑢶', + '𑣗' => '𑢷', + '𑣘' => '𑢸', + '𑣙' => '𑢹', + '𑣚' => '𑢺', + '𑣛' => '𑢻', + '𑣜' => '𑢼', + '𑣝' => '𑢽', + '𑣞' => '𑢾', + '𑣟' => '𑢿', +); diff --git a/vendor/symfony/polyfill-mbstring/bootstrap.php b/vendor/symfony/polyfill-mbstring/bootstrap.php new file mode 100644 index 000000000..2fdcc5a6f --- /dev/null +++ b/vendor/symfony/polyfill-mbstring/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\Mbstring as p; + +if (!function_exists('mb_strlen')) { + define('MB_CASE_UPPER', 0); + define('MB_CASE_LOWER', 1); + define('MB_CASE_TITLE', 2); + + function mb_convert_encoding($s, $to, $from = null) { return p\Mbstring::mb_convert_encoding($s, $to, $from); } + function mb_decode_mimeheader($s) { return p\Mbstring::mb_decode_mimeheader($s); } + function mb_encode_mimeheader($s, $charset = null, $transferEnc = null, $lf = null, $indent = null) { return p\Mbstring::mb_encode_mimeheader($s, $charset, $transferEnc, $lf, $indent); } + function mb_decode_numericentity($s, $convmap, $enc = null) { return p\Mbstring::mb_decode_numericentity($s, $convmap, $enc); } + function mb_encode_numericentity($s, $convmap, $enc = null, $is_hex = false) { return p\Mbstring::mb_encode_numericentity($s, $convmap, $enc, $is_hex); } + function mb_convert_case($s, $mode, $enc = null) { return p\Mbstring::mb_convert_case($s, $mode, $enc); } + function mb_internal_encoding($enc = null) { return p\Mbstring::mb_internal_encoding($enc); } + function mb_language($lang = null) { return p\Mbstring::mb_language($lang); } + function mb_list_encodings() { return p\Mbstring::mb_list_encodings(); } + function mb_encoding_aliases($encoding) { return p\Mbstring::mb_encoding_aliases($encoding); } + function mb_check_encoding($var = null, $encoding = null) { return p\Mbstring::mb_check_encoding($var, $encoding); } + function mb_detect_encoding($str, $encodingList = null, $strict = false) { return p\Mbstring::mb_detect_encoding($str, $encodingList, $strict); } + function mb_detect_order($encodingList = null) { return p\Mbstring::mb_detect_order($encodingList); } + function mb_parse_str($s, &$result = array()) { parse_str($s, $result); } + function mb_strlen($s, $enc = null) { return p\Mbstring::mb_strlen($s, $enc); } + function mb_strpos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_strpos($s, $needle, $offset, $enc); } + function mb_strtolower($s, $enc = null) { return p\Mbstring::mb_strtolower($s, $enc); } + function mb_strtoupper($s, $enc = null) { return p\Mbstring::mb_strtoupper($s, $enc); } + function mb_substitute_character($char = null) { return p\Mbstring::mb_substitute_character($char); } + function mb_substr($s, $start, $length = 2147483647, $enc = null) { return p\Mbstring::mb_substr($s, $start, $length, $enc); } + function mb_stripos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_stripos($s, $needle, $offset, $enc); } + function mb_stristr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_stristr($s, $needle, $part, $enc); } + function mb_strrchr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_strrchr($s, $needle, $part, $enc); } + function mb_strrichr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_strrichr($s, $needle, $part, $enc); } + function mb_strripos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_strripos($s, $needle, $offset, $enc); } + function mb_strrpos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_strrpos($s, $needle, $offset, $enc); } + function mb_strstr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_strstr($s, $needle, $part, $enc); } + function mb_get_info($type = 'all') { return p\Mbstring::mb_get_info($type); } + function mb_http_output($enc = null) { return p\Mbstring::mb_http_output($enc); } + function mb_strwidth($s, $enc = null) { return p\Mbstring::mb_strwidth($s, $enc); } + function mb_substr_count($haystack, $needle, $enc = null) { return p\Mbstring::mb_substr_count($haystack, $needle, $enc); } + function mb_output_handler($contents, $status) { return p\Mbstring::mb_output_handler($contents, $status); } + function mb_http_input($type = '') { return p\Mbstring::mb_http_input($type); } + function mb_convert_variables($toEncoding, $fromEncoding, &$a = null, &$b = null, &$c = null, &$d = null, &$e = null, &$f = null) { return p\Mbstring::mb_convert_variables($toEncoding, $fromEncoding, $a, $b, $c, $d, $e, $f); } +} +if (!function_exists('mb_chr')) { + function mb_ord($s, $enc = null) { return p\Mbstring::mb_ord($s, $enc); } + function mb_chr($code, $enc = null) { return p\Mbstring::mb_chr($code, $enc); } + function mb_scrub($s, $enc = null) { $enc = null === $enc ? mb_internal_encoding() : $enc; return mb_convert_encoding($s, $enc, $enc); } +} diff --git a/vendor/symfony/polyfill-mbstring/composer.json b/vendor/symfony/polyfill-mbstring/composer.json new file mode 100644 index 000000000..50ea12f1b --- /dev/null +++ b/vendor/symfony/polyfill-mbstring/composer.json @@ -0,0 +1,34 @@ +{ + "name": "symfony/polyfill-mbstring", + "type": "library", + "description": "Symfony polyfill for the Mbstring extension", + "keywords": ["polyfill", "shim", "compatibility", "portable", "mbstring"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.3.3" + }, + "autoload": { + "psr-4": { "Symfony\\Polyfill\\Mbstring\\": "" }, + "files": [ "bootstrap.php" ] + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "1.9-dev" + } + } +} diff --git a/vendor/symfony/translation/.gitignore b/vendor/symfony/translation/.gitignore new file mode 100644 index 000000000..c49a5d8df --- /dev/null +++ b/vendor/symfony/translation/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/vendor/symfony/translation/CHANGELOG.md b/vendor/symfony/translation/CHANGELOG.md new file mode 100644 index 000000000..527b80495 --- /dev/null +++ b/vendor/symfony/translation/CHANGELOG.md @@ -0,0 +1,108 @@ +CHANGELOG +========= + +4.1.0 +----- + + * The `FileDumper::setBackup()` method is deprecated. + * The `TranslationWriter::disableBackup()` method is deprecated. + * The `XliffFileDumper` will write "name" on the "unit" node when dumping XLIFF 2.0. + +4.0.0 +----- + + * removed the backup feature of the `FileDumper` class + * removed `TranslationWriter::writeTranslations()` method + * removed support for passing `MessageSelector` instances to the constructor of the `Translator` class + +3.4.0 +----- + + * Added `TranslationDumperPass` + * Added `TranslationExtractorPass` + * Added `TranslatorPass` + * Added `TranslationReader` and `TranslationReaderInterface` + * Added `` section to the Xliff 2.0 dumper. + * Improved Xliff 2.0 loader to load `` section. + * Added `TranslationWriterInterface` + * Deprecated `TranslationWriter::writeTranslations` in favor of `TranslationWriter::write` + * added support for adding custom message formatter and decoupling the default one. + * Added `PhpExtractor` + * Added `PhpStringTokenParser` + +3.2.0 +----- + + * Added support for escaping `|` in plural translations with double pipe. + +3.1.0 +----- + + * Deprecated the backup feature of the file dumper classes. + +3.0.0 +----- + + * removed `FileDumper::format()` method. + * Changed the visibility of the locale property in `Translator` from protected to private. + +2.8.0 +----- + + * deprecated FileDumper::format(), overwrite FileDumper::formatCatalogue() instead. + * deprecated Translator::getMessages(), rely on TranslatorBagInterface::getCatalogue() instead. + * added `FileDumper::formatCatalogue` which allows format the catalogue without dumping it into file. + * added option `json_encoding` to JsonFileDumper + * added options `as_tree`, `inline` to YamlFileDumper + * added support for XLIFF 2.0. + * added support for XLIFF target and tool attributes. + * added message parameters to DataCollectorTranslator. + * [DEPRECATION] The `DiffOperation` class has been deprecated and + will be removed in Symfony 3.0, since its operation has nothing to do with 'diff', + so the class name is misleading. The `TargetOperation` class should be used for + this use-case instead. + +2.7.0 +----- + + * added DataCollectorTranslator for collecting the translated messages. + +2.6.0 +----- + + * added possibility to cache catalogues + * added TranslatorBagInterface + * added LoggingTranslator + * added Translator::getMessages() for retrieving the message catalogue as an array + +2.5.0 +----- + + * added relative file path template to the file dumpers + * added optional backup to the file dumpers + * changed IcuResFileDumper to extend FileDumper + +2.3.0 +----- + + * added classes to make operations on catalogues (like making a diff or a merge on 2 catalogues) + * added Translator::getFallbackLocales() + * deprecated Translator::setFallbackLocale() in favor of the new Translator::setFallbackLocales() method + +2.2.0 +----- + + * QtTranslationsLoader class renamed to QtFileLoader. QtTranslationsLoader is deprecated and will be removed in 2.3. + * [BC BREAK] uniformized the exception thrown by the load() method when an error occurs. The load() method now + throws Symfony\Component\Translation\Exception\NotFoundResourceException when a resource cannot be found + and Symfony\Component\Translation\Exception\InvalidResourceException when a resource is invalid. + * changed the exception class thrown by some load() methods from \RuntimeException to \InvalidArgumentException + (IcuDatFileLoader, IcuResFileLoader and QtFileLoader) + +2.1.0 +----- + + * added support for more than one fallback locale + * added support for extracting translation messages from templates (Twig and PHP) + * added dumpers for translation catalogs + * added support for QT, gettext, and ResourceBundles diff --git a/vendor/symfony/translation/Catalogue/AbstractOperation.php b/vendor/symfony/translation/Catalogue/AbstractOperation.php new file mode 100644 index 000000000..a0765ddd6 --- /dev/null +++ b/vendor/symfony/translation/Catalogue/AbstractOperation.php @@ -0,0 +1,157 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Catalogue; + +use Symfony\Component\Translation\Exception\InvalidArgumentException; +use Symfony\Component\Translation\Exception\LogicException; +use Symfony\Component\Translation\MessageCatalogue; +use Symfony\Component\Translation\MessageCatalogueInterface; + +/** + * Base catalogues binary operation class. + * + * A catalogue binary operation performs operation on + * source (the left argument) and target (the right argument) catalogues. + * + * @author Jean-François Simon + */ +abstract class AbstractOperation implements OperationInterface +{ + protected $source; + protected $target; + protected $result; + + /** + * @var array|null The domains affected by this operation + */ + private $domains; + + /** + * This array stores 'all', 'new' and 'obsolete' messages for all valid domains. + * + * The data structure of this array is as follows: + * + * array( + * 'domain 1' => array( + * 'all' => array(...), + * 'new' => array(...), + * 'obsolete' => array(...) + * ), + * 'domain 2' => array( + * 'all' => array(...), + * 'new' => array(...), + * 'obsolete' => array(...) + * ), + * ... + * ) + * + * @var array The array that stores 'all', 'new' and 'obsolete' messages + */ + protected $messages; + + /** + * @throws LogicException + */ + public function __construct(MessageCatalogueInterface $source, MessageCatalogueInterface $target) + { + if ($source->getLocale() !== $target->getLocale()) { + throw new LogicException('Operated catalogues must belong to the same locale.'); + } + + $this->source = $source; + $this->target = $target; + $this->result = new MessageCatalogue($source->getLocale()); + $this->messages = array(); + } + + /** + * {@inheritdoc} + */ + public function getDomains() + { + if (null === $this->domains) { + $this->domains = array_values(array_unique(array_merge($this->source->getDomains(), $this->target->getDomains()))); + } + + return $this->domains; + } + + /** + * {@inheritdoc} + */ + public function getMessages($domain) + { + if (!\in_array($domain, $this->getDomains())) { + throw new InvalidArgumentException(sprintf('Invalid domain: %s.', $domain)); + } + + if (!isset($this->messages[$domain]['all'])) { + $this->processDomain($domain); + } + + return $this->messages[$domain]['all']; + } + + /** + * {@inheritdoc} + */ + public function getNewMessages($domain) + { + if (!\in_array($domain, $this->getDomains())) { + throw new InvalidArgumentException(sprintf('Invalid domain: %s.', $domain)); + } + + if (!isset($this->messages[$domain]['new'])) { + $this->processDomain($domain); + } + + return $this->messages[$domain]['new']; + } + + /** + * {@inheritdoc} + */ + public function getObsoleteMessages($domain) + { + if (!\in_array($domain, $this->getDomains())) { + throw new InvalidArgumentException(sprintf('Invalid domain: %s.', $domain)); + } + + if (!isset($this->messages[$domain]['obsolete'])) { + $this->processDomain($domain); + } + + return $this->messages[$domain]['obsolete']; + } + + /** + * {@inheritdoc} + */ + public function getResult() + { + foreach ($this->getDomains() as $domain) { + if (!isset($this->messages[$domain])) { + $this->processDomain($domain); + } + } + + return $this->result; + } + + /** + * Performs operation on source and target catalogues for the given domain and + * stores the results. + * + * @param string $domain The domain which the operation will be performed for + */ + abstract protected function processDomain($domain); +} diff --git a/vendor/symfony/translation/Catalogue/MergeOperation.php b/vendor/symfony/translation/Catalogue/MergeOperation.php new file mode 100644 index 000000000..6db3f801f --- /dev/null +++ b/vendor/symfony/translation/Catalogue/MergeOperation.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\Translation\Catalogue; + +/** + * Merge operation between two catalogues as follows: + * all = source ∪ target = {x: x ∈ source ∨ x ∈ target} + * new = all ∖ source = {x: x ∈ target ∧ x ∉ source} + * obsolete = source ∖ all = {x: x ∈ source ∧ x ∉ source ∧ x ∉ target} = ∅ + * Basically, the result contains messages from both catalogues. + * + * @author Jean-François Simon + */ +class MergeOperation extends AbstractOperation +{ + /** + * {@inheritdoc} + */ + protected function processDomain($domain) + { + $this->messages[$domain] = array( + 'all' => array(), + 'new' => array(), + 'obsolete' => array(), + ); + + foreach ($this->source->all($domain) as $id => $message) { + $this->messages[$domain]['all'][$id] = $message; + $this->result->add(array($id => $message), $domain); + if (null !== $keyMetadata = $this->source->getMetadata($id, $domain)) { + $this->result->setMetadata($id, $keyMetadata, $domain); + } + } + + foreach ($this->target->all($domain) as $id => $message) { + if (!$this->source->has($id, $domain)) { + $this->messages[$domain]['all'][$id] = $message; + $this->messages[$domain]['new'][$id] = $message; + $this->result->add(array($id => $message), $domain); + if (null !== $keyMetadata = $this->target->getMetadata($id, $domain)) { + $this->result->setMetadata($id, $keyMetadata, $domain); + } + } + } + } +} diff --git a/vendor/symfony/translation/Catalogue/OperationInterface.php b/vendor/symfony/translation/Catalogue/OperationInterface.php new file mode 100644 index 000000000..87d888efb --- /dev/null +++ b/vendor/symfony/translation/Catalogue/OperationInterface.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Catalogue; + +use Symfony\Component\Translation\MessageCatalogueInterface; + +/** + * Represents an operation on catalogue(s). + * + * An instance of this interface performs an operation on one or more catalogues and + * stores intermediate and final results of the operation. + * + * The first catalogue in its argument(s) is called the 'source catalogue' or 'source' and + * the following results are stored: + * + * Messages: also called 'all', are valid messages for the given domain after the operation is performed. + * + * New Messages: also called 'new' (new = all ∖ source = {x: x ∈ all ∧ x ∉ source}). + * + * Obsolete Messages: also called 'obsolete' (obsolete = source ∖ all = {x: x ∈ source ∧ x ∉ all}). + * + * Result: also called 'result', is the resulting catalogue for the given domain that holds the same messages as 'all'. + * + * @author Jean-François Simon + */ +interface OperationInterface +{ + /** + * Returns domains affected by operation. + * + * @return array + */ + public function getDomains(); + + /** + * Returns all valid messages ('all') after operation. + * + * @param string $domain + * + * @return array + */ + public function getMessages($domain); + + /** + * Returns new messages ('new') after operation. + * + * @param string $domain + * + * @return array + */ + public function getNewMessages($domain); + + /** + * Returns obsolete messages ('obsolete') after operation. + * + * @param string $domain + * + * @return array + */ + public function getObsoleteMessages($domain); + + /** + * Returns resulting catalogue ('result'). + * + * @return MessageCatalogueInterface + */ + public function getResult(); +} diff --git a/vendor/symfony/translation/Catalogue/TargetOperation.php b/vendor/symfony/translation/Catalogue/TargetOperation.php new file mode 100644 index 000000000..f3b0a29df --- /dev/null +++ b/vendor/symfony/translation/Catalogue/TargetOperation.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Catalogue; + +/** + * Target operation between two catalogues: + * intersection = source ∩ target = {x: x ∈ source ∧ x ∈ target} + * all = intersection ∪ (target ∖ intersection) = target + * new = all ∖ source = {x: x ∈ target ∧ x ∉ source} + * obsolete = source ∖ all = source ∖ target = {x: x ∈ source ∧ x ∉ target} + * Basically, the result contains messages from the target catalogue. + * + * @author Michael Lee + */ +class TargetOperation extends AbstractOperation +{ + /** + * {@inheritdoc} + */ + protected function processDomain($domain) + { + $this->messages[$domain] = array( + 'all' => array(), + 'new' => array(), + 'obsolete' => array(), + ); + + // For 'all' messages, the code can't be simplified as ``$this->messages[$domain]['all'] = $target->all($domain);``, + // because doing so will drop messages like {x: x ∈ source ∧ x ∉ target.all ∧ x ∈ target.fallback} + // + // For 'new' messages, the code can't be simplied as ``array_diff_assoc($this->target->all($domain), $this->source->all($domain));`` + // because doing so will not exclude messages like {x: x ∈ target ∧ x ∉ source.all ∧ x ∈ source.fallback} + // + // For 'obsolete' messages, the code can't be simplifed as ``array_diff_assoc($this->source->all($domain), $this->target->all($domain))`` + // because doing so will not exclude messages like {x: x ∈ source ∧ x ∉ target.all ∧ x ∈ target.fallback} + + foreach ($this->source->all($domain) as $id => $message) { + if ($this->target->has($id, $domain)) { + $this->messages[$domain]['all'][$id] = $message; + $this->result->add(array($id => $message), $domain); + if (null !== $keyMetadata = $this->source->getMetadata($id, $domain)) { + $this->result->setMetadata($id, $keyMetadata, $domain); + } + } else { + $this->messages[$domain]['obsolete'][$id] = $message; + } + } + + foreach ($this->target->all($domain) as $id => $message) { + if (!$this->source->has($id, $domain)) { + $this->messages[$domain]['all'][$id] = $message; + $this->messages[$domain]['new'][$id] = $message; + $this->result->add(array($id => $message), $domain); + if (null !== $keyMetadata = $this->target->getMetadata($id, $domain)) { + $this->result->setMetadata($id, $keyMetadata, $domain); + } + } + } + } +} diff --git a/vendor/symfony/translation/Command/XliffLintCommand.php b/vendor/symfony/translation/Command/XliffLintCommand.php new file mode 100644 index 000000000..378788f5b --- /dev/null +++ b/vendor/symfony/translation/Command/XliffLintCommand.php @@ -0,0 +1,270 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Command; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Exception\RuntimeException; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +/** + * Validates XLIFF files syntax and outputs encountered errors. + * + * @author Grégoire Pineau + * @author Robin Chalas + * @author Javier Eguiluz + */ +class XliffLintCommand extends Command +{ + protected static $defaultName = 'lint:xliff'; + + private $format; + private $displayCorrectFiles; + private $directoryIteratorProvider; + private $isReadableProvider; + + public function __construct(string $name = null, callable $directoryIteratorProvider = null, callable $isReadableProvider = null) + { + parent::__construct($name); + + $this->directoryIteratorProvider = $directoryIteratorProvider; + $this->isReadableProvider = $isReadableProvider; + } + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setDescription('Lints a XLIFF file and outputs encountered errors') + ->addArgument('filename', null, 'A file or a directory or STDIN') + ->addOption('format', null, InputOption::VALUE_REQUIRED, 'The output format', 'txt') + ->setHelp(<<%command.name% command lints a XLIFF file and outputs to STDOUT +the first encountered syntax error. + +You can validates XLIFF contents passed from STDIN: + + cat filename | php %command.full_name% + +You can also validate the syntax of a file: + + php %command.full_name% filename + +Or of a whole directory: + + php %command.full_name% dirname + php %command.full_name% dirname --format=json + +EOF + ) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $io = new SymfonyStyle($input, $output); + $filename = $input->getArgument('filename'); + $this->format = $input->getOption('format'); + $this->displayCorrectFiles = $output->isVerbose(); + + if (!$filename) { + if (!$stdin = $this->getStdin()) { + throw new RuntimeException('Please provide a filename or pipe file content to STDIN.'); + } + + return $this->display($io, array($this->validate($stdin))); + } + + if (!$this->isReadable($filename)) { + throw new RuntimeException(sprintf('File or directory "%s" is not readable.', $filename)); + } + + $filesInfo = array(); + foreach ($this->getFiles($filename) as $file) { + $filesInfo[] = $this->validate(file_get_contents($file), $file); + } + + return $this->display($io, $filesInfo); + } + + private function validate($content, $file = null) + { + $errors = array(); + + // Avoid: Warning DOMDocument::loadXML(): Empty string supplied as input + if ('' === trim($content)) { + return array('file' => $file, 'valid' => true); + } + + libxml_use_internal_errors(true); + + $document = new \DOMDocument(); + $document->loadXML($content); + + if (null !== $targetLanguage = $this->getTargetLanguageFromFile($document)) { + $expectedFileExtension = sprintf('%s.xlf', str_replace('-', '_', $targetLanguage)); + $realFileExtension = explode('.', basename($file), 2)[1] ?? ''; + + if ($expectedFileExtension !== $realFileExtension) { + $errors[] = array( + 'line' => -1, + 'column' => -1, + 'message' => sprintf('There is a mismatch between the file extension ("%s") and the "%s" value used in the "target-language" attribute of the file.', $realFileExtension, $targetLanguage), + ); + } + } + + $document->schemaValidate(__DIR__.'/../Resources/schemas/xliff-core-1.2-strict.xsd'); + foreach (libxml_get_errors() as $xmlError) { + $errors[] = array( + 'line' => $xmlError->line, + 'column' => $xmlError->column, + 'message' => trim($xmlError->message), + ); + } + + libxml_clear_errors(); + libxml_use_internal_errors(false); + + return array('file' => $file, 'valid' => 0 === \count($errors), 'messages' => $errors); + } + + private function display(SymfonyStyle $io, array $files) + { + switch ($this->format) { + case 'txt': + return $this->displayTxt($io, $files); + case 'json': + return $this->displayJson($io, $files); + default: + throw new InvalidArgumentException(sprintf('The format "%s" is not supported.', $this->format)); + } + } + + private function displayTxt(SymfonyStyle $io, array $filesInfo) + { + $countFiles = \count($filesInfo); + $erroredFiles = 0; + + foreach ($filesInfo as $info) { + if ($info['valid'] && $this->displayCorrectFiles) { + $io->comment('OK'.($info['file'] ? sprintf(' in %s', $info['file']) : '')); + } elseif (!$info['valid']) { + ++$erroredFiles; + $io->text(' ERROR '.($info['file'] ? sprintf(' in %s', $info['file']) : '')); + $io->listing(array_map(function ($error) { + // general document errors have a '-1' line number + return -1 === $error['line'] ? $error['message'] : sprintf('Line %d, Column %d: %s', $error['line'], $error['column'], $error['message']); + }, $info['messages'])); + } + } + + if (0 === $erroredFiles) { + $io->success(sprintf('All %d XLIFF files contain valid syntax.', $countFiles)); + } else { + $io->warning(sprintf('%d XLIFF files have valid syntax and %d contain errors.', $countFiles - $erroredFiles, $erroredFiles)); + } + + return min($erroredFiles, 1); + } + + private function displayJson(SymfonyStyle $io, array $filesInfo) + { + $errors = 0; + + array_walk($filesInfo, function (&$v) use (&$errors) { + $v['file'] = (string) $v['file']; + if (!$v['valid']) { + ++$errors; + } + }); + + $io->writeln(json_encode($filesInfo, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); + + return min($errors, 1); + } + + private function getFiles($fileOrDirectory) + { + if (is_file($fileOrDirectory)) { + yield new \SplFileInfo($fileOrDirectory); + + return; + } + + foreach ($this->getDirectoryIterator($fileOrDirectory) as $file) { + if (!\in_array($file->getExtension(), array('xlf', 'xliff'))) { + continue; + } + + yield $file; + } + } + + private function getStdin() + { + if (0 !== ftell(STDIN)) { + return; + } + + $inputs = ''; + while (!feof(STDIN)) { + $inputs .= fread(STDIN, 1024); + } + + return $inputs; + } + + private function getDirectoryIterator($directory) + { + $default = function ($directory) { + return new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($directory, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS), + \RecursiveIteratorIterator::LEAVES_ONLY + ); + }; + + if (null !== $this->directoryIteratorProvider) { + return \call_user_func($this->directoryIteratorProvider, $directory, $default); + } + + return $default($directory); + } + + private function isReadable($fileOrDirectory) + { + $default = function ($fileOrDirectory) { + return is_readable($fileOrDirectory); + }; + + if (null !== $this->isReadableProvider) { + return \call_user_func($this->isReadableProvider, $fileOrDirectory, $default); + } + + return $default($fileOrDirectory); + } + + private function getTargetLanguageFromFile(\DOMDocument $xliffContents): ?string + { + foreach ($xliffContents->getElementsByTagName('file')[0]->attributes ?? array() as $attribute) { + if ('target-language' === $attribute->nodeName) { + return $attribute->nodeValue; + } + } + + return null; + } +} diff --git a/vendor/symfony/translation/DataCollector/TranslationDataCollector.php b/vendor/symfony/translation/DataCollector/TranslationDataCollector.php new file mode 100644 index 000000000..edd712dd1 --- /dev/null +++ b/vendor/symfony/translation/DataCollector/TranslationDataCollector.php @@ -0,0 +1,167 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\DataCollector; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\DataCollector\DataCollector; +use Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface; +use Symfony\Component\Translation\DataCollectorTranslator; + +/** + * @author Abdellatif Ait boudad + */ +class TranslationDataCollector extends DataCollector implements LateDataCollectorInterface +{ + private $translator; + + public function __construct(DataCollectorTranslator $translator) + { + $this->translator = $translator; + } + + /** + * {@inheritdoc} + */ + public function lateCollect() + { + $messages = $this->sanitizeCollectedMessages($this->translator->getCollectedMessages()); + + $this->data = $this->computeCount($messages); + $this->data['messages'] = $messages; + + $this->data['locale'] = $this->translator->getLocale(); + $this->data['fallback_locales'] = $this->translator->getFallbackLocales(); + + $this->data = $this->cloneVar($this->data); + } + + /** + * {@inheritdoc} + */ + public function collect(Request $request, Response $response, \Exception $exception = null) + { + } + + /** + * {@inheritdoc} + */ + public function reset() + { + $this->data = array(); + } + + /** + * @return array + */ + public function getMessages() + { + return isset($this->data['messages']) ? $this->data['messages'] : array(); + } + + /** + * @return int + */ + public function getCountMissings() + { + return isset($this->data[DataCollectorTranslator::MESSAGE_MISSING]) ? $this->data[DataCollectorTranslator::MESSAGE_MISSING] : 0; + } + + /** + * @return int + */ + public function getCountFallbacks() + { + return isset($this->data[DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK]) ? $this->data[DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK] : 0; + } + + /** + * @return int + */ + public function getCountDefines() + { + return isset($this->data[DataCollectorTranslator::MESSAGE_DEFINED]) ? $this->data[DataCollectorTranslator::MESSAGE_DEFINED] : 0; + } + + public function getLocale() + { + return !empty($this->data['locale']) ? $this->data['locale'] : null; + } + + public function getFallbackLocales() + { + return (isset($this->data['fallback_locales']) && \count($this->data['fallback_locales']) > 0) ? $this->data['fallback_locales'] : array(); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'translation'; + } + + private function sanitizeCollectedMessages($messages) + { + $result = array(); + foreach ($messages as $key => $message) { + $messageId = $message['locale'].$message['domain'].$message['id']; + + if (!isset($result[$messageId])) { + $message['count'] = 1; + $message['parameters'] = !empty($message['parameters']) ? array($message['parameters']) : array(); + $messages[$key]['translation'] = $this->sanitizeString($message['translation']); + $result[$messageId] = $message; + } else { + if (!empty($message['parameters'])) { + $result[$messageId]['parameters'][] = $message['parameters']; + } + + ++$result[$messageId]['count']; + } + + unset($messages[$key]); + } + + return $result; + } + + private function computeCount($messages) + { + $count = array( + DataCollectorTranslator::MESSAGE_DEFINED => 0, + DataCollectorTranslator::MESSAGE_MISSING => 0, + DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK => 0, + ); + + foreach ($messages as $message) { + ++$count[$message['state']]; + } + + return $count; + } + + private function sanitizeString($string, $length = 80) + { + $string = trim(preg_replace('/\s+/', ' ', $string)); + + if (false !== $encoding = mb_detect_encoding($string, null, true)) { + if (mb_strlen($string, $encoding) > $length) { + return mb_substr($string, 0, $length - 3, $encoding).'...'; + } + } elseif (\strlen($string) > $length) { + return substr($string, 0, $length - 3).'...'; + } + + return $string; + } +} diff --git a/vendor/symfony/translation/DataCollectorTranslator.php b/vendor/symfony/translation/DataCollectorTranslator.php new file mode 100644 index 000000000..5d4d819e0 --- /dev/null +++ b/vendor/symfony/translation/DataCollectorTranslator.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\Translation; + +use Symfony\Component\Translation\Exception\InvalidArgumentException; + +/** + * @author Abdellatif Ait boudad + */ +class DataCollectorTranslator implements TranslatorInterface, TranslatorBagInterface +{ + const MESSAGE_DEFINED = 0; + const MESSAGE_MISSING = 1; + const MESSAGE_EQUALS_FALLBACK = 2; + + /** + * @var TranslatorInterface|TranslatorBagInterface + */ + private $translator; + + private $messages = array(); + + /** + * @param TranslatorInterface $translator The translator must implement TranslatorBagInterface + */ + public function __construct(TranslatorInterface $translator) + { + if (!$translator instanceof TranslatorBagInterface) { + throw new InvalidArgumentException(sprintf('The Translator "%s" must implement TranslatorInterface and TranslatorBagInterface.', \get_class($translator))); + } + + $this->translator = $translator; + } + + /** + * {@inheritdoc} + */ + public function trans($id, array $parameters = array(), $domain = null, $locale = null) + { + $trans = $this->translator->trans($id, $parameters, $domain, $locale); + $this->collectMessage($locale, $domain, $id, $trans, $parameters); + + return $trans; + } + + /** + * {@inheritdoc} + */ + public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null) + { + $trans = $this->translator->transChoice($id, $number, $parameters, $domain, $locale); + $this->collectMessage($locale, $domain, $id, $trans, $parameters, $number); + + return $trans; + } + + /** + * {@inheritdoc} + */ + public function setLocale($locale) + { + $this->translator->setLocale($locale); + } + + /** + * {@inheritdoc} + */ + public function getLocale() + { + return $this->translator->getLocale(); + } + + /** + * {@inheritdoc} + */ + public function getCatalogue($locale = null) + { + return $this->translator->getCatalogue($locale); + } + + /** + * Gets the fallback locales. + * + * @return array $locales The fallback locales + */ + public function getFallbackLocales() + { + if ($this->translator instanceof Translator || method_exists($this->translator, 'getFallbackLocales')) { + return $this->translator->getFallbackLocales(); + } + + return array(); + } + + /** + * Passes through all unknown calls onto the translator object. + */ + public function __call($method, $args) + { + return \call_user_func_array(array($this->translator, $method), $args); + } + + /** + * @return array + */ + public function getCollectedMessages() + { + return $this->messages; + } + + /** + * @param string|null $locale + * @param string|null $domain + * @param string $id + * @param string $translation + * @param array|null $parameters + * @param int|null $number + */ + private function collectMessage($locale, $domain, $id, $translation, $parameters = array(), $number = null) + { + if (null === $domain) { + $domain = 'messages'; + } + + $id = (string) $id; + $catalogue = $this->translator->getCatalogue($locale); + $locale = $catalogue->getLocale(); + if ($catalogue->defines($id, $domain)) { + $state = self::MESSAGE_DEFINED; + } elseif ($catalogue->has($id, $domain)) { + $state = self::MESSAGE_EQUALS_FALLBACK; + + $fallbackCatalogue = $catalogue->getFallbackCatalogue(); + while ($fallbackCatalogue) { + if ($fallbackCatalogue->defines($id, $domain)) { + $locale = $fallbackCatalogue->getLocale(); + break; + } + + $fallbackCatalogue = $fallbackCatalogue->getFallbackCatalogue(); + } + } else { + $state = self::MESSAGE_MISSING; + } + + $this->messages[] = array( + 'locale' => $locale, + 'domain' => $domain, + 'id' => $id, + 'translation' => $translation, + 'parameters' => $parameters, + 'transChoiceNumber' => $number, + 'state' => $state, + ); + } +} diff --git a/vendor/symfony/translation/DependencyInjection/TranslationDumperPass.php b/vendor/symfony/translation/DependencyInjection/TranslationDumperPass.php new file mode 100644 index 000000000..31d791dc2 --- /dev/null +++ b/vendor/symfony/translation/DependencyInjection/TranslationDumperPass.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\DependencyInjection; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Adds tagged translation.formatter services to translation writer. + */ +class TranslationDumperPass implements CompilerPassInterface +{ + private $writerServiceId; + private $dumperTag; + + public function __construct(string $writerServiceId = 'translation.writer', string $dumperTag = 'translation.dumper') + { + $this->writerServiceId = $writerServiceId; + $this->dumperTag = $dumperTag; + } + + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition($this->writerServiceId)) { + return; + } + + $definition = $container->getDefinition($this->writerServiceId); + + foreach ($container->findTaggedServiceIds($this->dumperTag, true) as $id => $attributes) { + $definition->addMethodCall('addDumper', array($attributes[0]['alias'], new Reference($id))); + } + } +} diff --git a/vendor/symfony/translation/DependencyInjection/TranslationExtractorPass.php b/vendor/symfony/translation/DependencyInjection/TranslationExtractorPass.php new file mode 100644 index 000000000..b7e6f7345 --- /dev/null +++ b/vendor/symfony/translation/DependencyInjection/TranslationExtractorPass.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\DependencyInjection; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Adds tagged translation.extractor services to translation extractor. + */ +class TranslationExtractorPass implements CompilerPassInterface +{ + private $extractorServiceId; + private $extractorTag; + + public function __construct(string $extractorServiceId = 'translation.extractor', string $extractorTag = 'translation.extractor') + { + $this->extractorServiceId = $extractorServiceId; + $this->extractorTag = $extractorTag; + } + + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition($this->extractorServiceId)) { + return; + } + + $definition = $container->getDefinition($this->extractorServiceId); + + foreach ($container->findTaggedServiceIds($this->extractorTag, true) as $id => $attributes) { + if (!isset($attributes[0]['alias'])) { + throw new RuntimeException(sprintf('The alias for the tag "translation.extractor" of service "%s" must be set.', $id)); + } + + $definition->addMethodCall('addExtractor', array($attributes[0]['alias'], new Reference($id))); + } + } +} diff --git a/vendor/symfony/translation/DependencyInjection/TranslatorPass.php b/vendor/symfony/translation/DependencyInjection/TranslatorPass.php new file mode 100644 index 000000000..cbaa43db9 --- /dev/null +++ b/vendor/symfony/translation/DependencyInjection/TranslatorPass.php @@ -0,0 +1,79 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\DependencyInjection; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +class TranslatorPass implements CompilerPassInterface +{ + private $translatorServiceId; + private $readerServiceId; + private $loaderTag; + private $debugCommandServiceId; + private $updateCommandServiceId; + + public function __construct(string $translatorServiceId = 'translator.default', string $readerServiceId = 'translation.reader', string $loaderTag = 'translation.loader', string $debugCommandServiceId = 'console.command.translation_debug', string $updateCommandServiceId = 'console.command.translation_update') + { + $this->translatorServiceId = $translatorServiceId; + $this->readerServiceId = $readerServiceId; + $this->loaderTag = $loaderTag; + $this->debugCommandServiceId = $debugCommandServiceId; + $this->updateCommandServiceId = $updateCommandServiceId; + } + + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition($this->translatorServiceId)) { + return; + } + + $loaders = array(); + $loaderRefs = array(); + foreach ($container->findTaggedServiceIds($this->loaderTag, true) as $id => $attributes) { + $loaderRefs[$id] = new Reference($id); + $loaders[$id][] = $attributes[0]['alias']; + if (isset($attributes[0]['legacy-alias'])) { + $loaders[$id][] = $attributes[0]['legacy-alias']; + } + } + + if ($container->hasDefinition($this->readerServiceId)) { + $definition = $container->getDefinition($this->readerServiceId); + foreach ($loaders as $id => $formats) { + foreach ($formats as $format) { + $definition->addMethodCall('addLoader', array($format, $loaderRefs[$id])); + } + } + } + + $container + ->findDefinition($this->translatorServiceId) + ->replaceArgument(0, ServiceLocatorTagPass::register($container, $loaderRefs)) + ->replaceArgument(3, $loaders) + ; + + if (!$container->hasParameter('twig.default_path')) { + return; + } + + if ($container->hasDefinition($this->debugCommandServiceId)) { + $container->getDefinition($this->debugCommandServiceId)->replaceArgument(4, $container->getParameter('twig.default_path')); + } + + if ($container->hasDefinition($this->updateCommandServiceId)) { + $container->getDefinition($this->updateCommandServiceId)->replaceArgument(5, $container->getParameter('twig.default_path')); + } + } +} diff --git a/vendor/symfony/translation/Dumper/CsvFileDumper.php b/vendor/symfony/translation/Dumper/CsvFileDumper.php new file mode 100644 index 000000000..0880528dc --- /dev/null +++ b/vendor/symfony/translation/Dumper/CsvFileDumper.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Dumper; + +use Symfony\Component\Translation\MessageCatalogue; + +/** + * CsvFileDumper generates a csv formatted string representation of a message catalogue. + * + * @author Stealth35 + */ +class CsvFileDumper extends FileDumper +{ + private $delimiter = ';'; + private $enclosure = '"'; + + /** + * {@inheritdoc} + */ + public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array()) + { + $handle = fopen('php://memory', 'r+b'); + + foreach ($messages->all($domain) as $source => $target) { + fputcsv($handle, array($source, $target), $this->delimiter, $this->enclosure); + } + + rewind($handle); + $output = stream_get_contents($handle); + fclose($handle); + + return $output; + } + + /** + * Sets the delimiter and escape character for CSV. + * + * @param string $delimiter Delimiter character + * @param string $enclosure Enclosure character + */ + public function setCsvControl($delimiter = ';', $enclosure = '"') + { + $this->delimiter = $delimiter; + $this->enclosure = $enclosure; + } + + /** + * {@inheritdoc} + */ + protected function getExtension() + { + return 'csv'; + } +} diff --git a/vendor/symfony/translation/Dumper/DumperInterface.php b/vendor/symfony/translation/Dumper/DumperInterface.php new file mode 100644 index 000000000..cebc65ed8 --- /dev/null +++ b/vendor/symfony/translation/Dumper/DumperInterface.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Dumper; + +use Symfony\Component\Translation\MessageCatalogue; + +/** + * DumperInterface is the interface implemented by all translation dumpers. + * There is no common option. + * + * @author Michel Salib + */ +interface DumperInterface +{ + /** + * Dumps the message catalogue. + * + * @param MessageCatalogue $messages The message catalogue + * @param array $options Options that are used by the dumper + */ + public function dump(MessageCatalogue $messages, $options = array()); +} diff --git a/vendor/symfony/translation/Dumper/FileDumper.php b/vendor/symfony/translation/Dumper/FileDumper.php new file mode 100644 index 000000000..5b10e4520 --- /dev/null +++ b/vendor/symfony/translation/Dumper/FileDumper.php @@ -0,0 +1,113 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Dumper; + +use Symfony\Component\Translation\Exception\InvalidArgumentException; +use Symfony\Component\Translation\Exception\RuntimeException; +use Symfony\Component\Translation\MessageCatalogue; + +/** + * FileDumper is an implementation of DumperInterface that dump a message catalogue to file(s). + * + * Options: + * - path (mandatory): the directory where the files should be saved + * + * @author Michel Salib + */ +abstract class FileDumper implements DumperInterface +{ + /** + * A template for the relative paths to files. + * + * @var string + */ + protected $relativePathTemplate = '%domain%.%locale%.%extension%'; + + /** + * Sets the template for the relative paths to files. + * + * @param string $relativePathTemplate A template for the relative paths to files + */ + public function setRelativePathTemplate($relativePathTemplate) + { + $this->relativePathTemplate = $relativePathTemplate; + } + + /** + * Sets backup flag. + * + * @param bool + * + * @deprecated since Symfony 4.1 + */ + public function setBackup($backup) + { + @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1.', __METHOD__), E_USER_DEPRECATED); + + if (false !== $backup) { + throw new \LogicException('The backup feature is no longer supported.'); + } + } + + /** + * {@inheritdoc} + */ + public function dump(MessageCatalogue $messages, $options = array()) + { + if (!array_key_exists('path', $options)) { + throw new InvalidArgumentException('The file dumper needs a path option.'); + } + + // save a file for each domain + foreach ($messages->getDomains() as $domain) { + $fullpath = $options['path'].'/'.$this->getRelativePath($domain, $messages->getLocale()); + if (!file_exists($fullpath)) { + $directory = \dirname($fullpath); + if (!file_exists($directory) && !@mkdir($directory, 0777, true)) { + throw new RuntimeException(sprintf('Unable to create directory "%s".', $directory)); + } + } + // save file + file_put_contents($fullpath, $this->formatCatalogue($messages, $domain, $options)); + } + } + + /** + * Transforms a domain of a message catalogue to its string representation. + * + * @param MessageCatalogue $messages + * @param string $domain + * @param array $options + * + * @return string representation + */ + abstract public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array()); + + /** + * Gets the file extension of the dumper. + * + * @return string file extension + */ + abstract protected function getExtension(); + + /** + * Gets the relative file path using the template. + */ + private function getRelativePath(string $domain, string $locale): string + { + return strtr($this->relativePathTemplate, array( + '%domain%' => $domain, + '%locale%' => $locale, + '%extension%' => $this->getExtension(), + )); + } +} diff --git a/vendor/symfony/translation/Dumper/IcuResFileDumper.php b/vendor/symfony/translation/Dumper/IcuResFileDumper.php new file mode 100644 index 000000000..efa9d7fee --- /dev/null +++ b/vendor/symfony/translation/Dumper/IcuResFileDumper.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\Translation\Dumper; + +use Symfony\Component\Translation\MessageCatalogue; + +/** + * IcuResDumper generates an ICU ResourceBundle formatted string representation of a message catalogue. + * + * @author Stealth35 + */ +class IcuResFileDumper extends FileDumper +{ + /** + * {@inheritdoc} + */ + protected $relativePathTemplate = '%domain%/%locale%.%extension%'; + + /** + * {@inheritdoc} + */ + public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array()) + { + $data = $indexes = $resources = ''; + + foreach ($messages->all($domain) as $source => $target) { + $indexes .= pack('v', \strlen($data) + 28); + $data .= $source."\0"; + } + + $data .= $this->writePadding($data); + + $keyTop = $this->getPosition($data); + + foreach ($messages->all($domain) as $source => $target) { + $resources .= pack('V', $this->getPosition($data)); + + $data .= pack('V', \strlen($target)) + .mb_convert_encoding($target."\0", 'UTF-16LE', 'UTF-8') + .$this->writePadding($data) + ; + } + + $resOffset = $this->getPosition($data); + + $data .= pack('v', \count($messages->all($domain))) + .$indexes + .$this->writePadding($data) + .$resources + ; + + $bundleTop = $this->getPosition($data); + + $root = pack('V7', + $resOffset + (2 << 28), // Resource Offset + Resource Type + 6, // Index length + $keyTop, // Index keys top + $bundleTop, // Index resources top + $bundleTop, // Index bundle top + \count($messages->all($domain)), // Index max table length + 0 // Index attributes + ); + + $header = pack('vC2v4C12@32', + 32, // Header size + 0xDA, 0x27, // Magic number 1 and 2 + 20, 0, 0, 2, // Rest of the header, ..., Size of a char + 0x52, 0x65, 0x73, 0x42, // Data format identifier + 1, 2, 0, 0, // Data version + 1, 4, 0, 0 // Unicode version + ); + + return $header.$root.$data; + } + + private function writePadding($data) + { + $padding = \strlen($data) % 4; + + if ($padding) { + return str_repeat("\xAA", 4 - $padding); + } + } + + private function getPosition($data) + { + return (\strlen($data) + 28) / 4; + } + + /** + * {@inheritdoc} + */ + protected function getExtension() + { + return 'res'; + } +} diff --git a/vendor/symfony/translation/Dumper/IniFileDumper.php b/vendor/symfony/translation/Dumper/IniFileDumper.php new file mode 100644 index 000000000..9ed375403 --- /dev/null +++ b/vendor/symfony/translation/Dumper/IniFileDumper.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Dumper; + +use Symfony\Component\Translation\MessageCatalogue; + +/** + * IniFileDumper generates an ini formatted string representation of a message catalogue. + * + * @author Stealth35 + */ +class IniFileDumper extends FileDumper +{ + /** + * {@inheritdoc} + */ + public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array()) + { + $output = ''; + + foreach ($messages->all($domain) as $source => $target) { + $escapeTarget = str_replace('"', '\"', $target); + $output .= $source.'="'.$escapeTarget."\"\n"; + } + + return $output; + } + + /** + * {@inheritdoc} + */ + protected function getExtension() + { + return 'ini'; + } +} diff --git a/vendor/symfony/translation/Dumper/JsonFileDumper.php b/vendor/symfony/translation/Dumper/JsonFileDumper.php new file mode 100644 index 000000000..a4db4d6ab --- /dev/null +++ b/vendor/symfony/translation/Dumper/JsonFileDumper.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Dumper; + +use Symfony\Component\Translation\MessageCatalogue; + +/** + * JsonFileDumper generates an json formatted string representation of a message catalogue. + * + * @author singles + */ +class JsonFileDumper extends FileDumper +{ + /** + * {@inheritdoc} + */ + public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array()) + { + $flags = $options['json_encoding'] ?? JSON_PRETTY_PRINT; + + return json_encode($messages->all($domain), $flags); + } + + /** + * {@inheritdoc} + */ + protected function getExtension() + { + return 'json'; + } +} diff --git a/vendor/symfony/translation/Dumper/MoFileDumper.php b/vendor/symfony/translation/Dumper/MoFileDumper.php new file mode 100644 index 000000000..32ed0ac26 --- /dev/null +++ b/vendor/symfony/translation/Dumper/MoFileDumper.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Dumper; + +use Symfony\Component\Translation\Loader\MoFileLoader; +use Symfony\Component\Translation\MessageCatalogue; + +/** + * MoFileDumper generates a gettext formatted string representation of a message catalogue. + * + * @author Stealth35 + */ +class MoFileDumper extends FileDumper +{ + /** + * {@inheritdoc} + */ + public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array()) + { + $sources = $targets = $sourceOffsets = $targetOffsets = ''; + $offsets = array(); + $size = 0; + + foreach ($messages->all($domain) as $source => $target) { + $offsets[] = array_map('strlen', array($sources, $source, $targets, $target)); + $sources .= "\0".$source; + $targets .= "\0".$target; + ++$size; + } + + $header = array( + 'magicNumber' => MoFileLoader::MO_LITTLE_ENDIAN_MAGIC, + 'formatRevision' => 0, + 'count' => $size, + 'offsetId' => MoFileLoader::MO_HEADER_SIZE, + 'offsetTranslated' => MoFileLoader::MO_HEADER_SIZE + (8 * $size), + 'sizeHashes' => 0, + 'offsetHashes' => MoFileLoader::MO_HEADER_SIZE + (16 * $size), + ); + + $sourcesSize = \strlen($sources); + $sourcesStart = $header['offsetHashes'] + 1; + + foreach ($offsets as $offset) { + $sourceOffsets .= $this->writeLong($offset[1]) + .$this->writeLong($offset[0] + $sourcesStart); + $targetOffsets .= $this->writeLong($offset[3]) + .$this->writeLong($offset[2] + $sourcesStart + $sourcesSize); + } + + $output = implode('', array_map(array($this, 'writeLong'), $header)) + .$sourceOffsets + .$targetOffsets + .$sources + .$targets + ; + + return $output; + } + + /** + * {@inheritdoc} + */ + protected function getExtension() + { + return 'mo'; + } + + private function writeLong($str) + { + return pack('V*', $str); + } +} diff --git a/vendor/symfony/translation/Dumper/PhpFileDumper.php b/vendor/symfony/translation/Dumper/PhpFileDumper.php new file mode 100644 index 000000000..c7c37aac9 --- /dev/null +++ b/vendor/symfony/translation/Dumper/PhpFileDumper.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Dumper; + +use Symfony\Component\Translation\MessageCatalogue; + +/** + * PhpFileDumper generates PHP files from a message catalogue. + * + * @author Michel Salib + */ +class PhpFileDumper extends FileDumper +{ + /** + * {@inheritdoc} + */ + public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array()) + { + return "all($domain), true).";\n"; + } + + /** + * {@inheritdoc} + */ + protected function getExtension() + { + return 'php'; + } +} diff --git a/vendor/symfony/translation/Dumper/PoFileDumper.php b/vendor/symfony/translation/Dumper/PoFileDumper.php new file mode 100644 index 000000000..ed4418b14 --- /dev/null +++ b/vendor/symfony/translation/Dumper/PoFileDumper.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Dumper; + +use Symfony\Component\Translation\MessageCatalogue; + +/** + * PoFileDumper generates a gettext formatted string representation of a message catalogue. + * + * @author Stealth35 + */ +class PoFileDumper extends FileDumper +{ + /** + * {@inheritdoc} + */ + public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array()) + { + $output = 'msgid ""'."\n"; + $output .= 'msgstr ""'."\n"; + $output .= '"Content-Type: text/plain; charset=UTF-8\n"'."\n"; + $output .= '"Content-Transfer-Encoding: 8bit\n"'."\n"; + $output .= '"Language: '.$messages->getLocale().'\n"'."\n"; + $output .= "\n"; + + $newLine = false; + foreach ($messages->all($domain) as $source => $target) { + if ($newLine) { + $output .= "\n"; + } else { + $newLine = true; + } + $output .= sprintf('msgid "%s"'."\n", $this->escape($source)); + $output .= sprintf('msgstr "%s"', $this->escape($target)); + } + + return $output; + } + + /** + * {@inheritdoc} + */ + protected function getExtension() + { + return 'po'; + } + + private function escape($str) + { + return addcslashes($str, "\0..\37\42\134"); + } +} diff --git a/vendor/symfony/translation/Dumper/QtFileDumper.php b/vendor/symfony/translation/Dumper/QtFileDumper.php new file mode 100644 index 000000000..a9073f26d --- /dev/null +++ b/vendor/symfony/translation/Dumper/QtFileDumper.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\Translation\Dumper; + +use Symfony\Component\Translation\MessageCatalogue; + +/** + * QtFileDumper generates ts files from a message catalogue. + * + * @author Benjamin Eberlei + */ +class QtFileDumper extends FileDumper +{ + /** + * {@inheritdoc} + */ + public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array()) + { + $dom = new \DOMDocument('1.0', 'utf-8'); + $dom->formatOutput = true; + $ts = $dom->appendChild($dom->createElement('TS')); + $context = $ts->appendChild($dom->createElement('context')); + $context->appendChild($dom->createElement('name', $domain)); + + foreach ($messages->all($domain) as $source => $target) { + $message = $context->appendChild($dom->createElement('message')); + $message->appendChild($dom->createElement('source', $source)); + $message->appendChild($dom->createElement('translation', $target)); + } + + return $dom->saveXML(); + } + + /** + * {@inheritdoc} + */ + protected function getExtension() + { + return 'ts'; + } +} diff --git a/vendor/symfony/translation/Dumper/XliffFileDumper.php b/vendor/symfony/translation/Dumper/XliffFileDumper.php new file mode 100644 index 000000000..c12454cd8 --- /dev/null +++ b/vendor/symfony/translation/Dumper/XliffFileDumper.php @@ -0,0 +1,205 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Dumper; + +use Symfony\Component\Translation\Exception\InvalidArgumentException; +use Symfony\Component\Translation\MessageCatalogue; + +/** + * XliffFileDumper generates xliff files from a message catalogue. + * + * @author Michel Salib + */ +class XliffFileDumper extends FileDumper +{ + /** + * {@inheritdoc} + */ + public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array()) + { + $xliffVersion = '1.2'; + if (array_key_exists('xliff_version', $options)) { + $xliffVersion = $options['xliff_version']; + } + + if (array_key_exists('default_locale', $options)) { + $defaultLocale = $options['default_locale']; + } else { + $defaultLocale = \Locale::getDefault(); + } + + if ('1.2' === $xliffVersion) { + return $this->dumpXliff1($defaultLocale, $messages, $domain, $options); + } + if ('2.0' === $xliffVersion) { + return $this->dumpXliff2($defaultLocale, $messages, $domain, $options); + } + + throw new InvalidArgumentException(sprintf('No support implemented for dumping XLIFF version "%s".', $xliffVersion)); + } + + /** + * {@inheritdoc} + */ + protected function getExtension() + { + return 'xlf'; + } + + private function dumpXliff1($defaultLocale, MessageCatalogue $messages, $domain, array $options = array()) + { + $toolInfo = array('tool-id' => 'symfony', 'tool-name' => 'Symfony'); + if (array_key_exists('tool_info', $options)) { + $toolInfo = array_merge($toolInfo, $options['tool_info']); + } + + $dom = new \DOMDocument('1.0', 'utf-8'); + $dom->formatOutput = true; + + $xliff = $dom->appendChild($dom->createElement('xliff')); + $xliff->setAttribute('version', '1.2'); + $xliff->setAttribute('xmlns', 'urn:oasis:names:tc:xliff:document:1.2'); + + $xliffFile = $xliff->appendChild($dom->createElement('file')); + $xliffFile->setAttribute('source-language', str_replace('_', '-', $defaultLocale)); + $xliffFile->setAttribute('target-language', str_replace('_', '-', $messages->getLocale())); + $xliffFile->setAttribute('datatype', 'plaintext'); + $xliffFile->setAttribute('original', 'file.ext'); + + $xliffHead = $xliffFile->appendChild($dom->createElement('header')); + $xliffTool = $xliffHead->appendChild($dom->createElement('tool')); + foreach ($toolInfo as $id => $value) { + $xliffTool->setAttribute($id, $value); + } + + $xliffBody = $xliffFile->appendChild($dom->createElement('body')); + foreach ($messages->all($domain) as $source => $target) { + $translation = $dom->createElement('trans-unit'); + + $translation->setAttribute('id', strtr(substr(base64_encode(hash('sha256', $source, true)), 0, 7), '/+', '._')); + $translation->setAttribute('resname', $source); + + $s = $translation->appendChild($dom->createElement('source')); + $s->appendChild($dom->createTextNode($source)); + + // Does the target contain characters requiring a CDATA section? + $text = 1 === preg_match('/[&<>]/', $target) ? $dom->createCDATASection($target) : $dom->createTextNode($target); + + $targetElement = $dom->createElement('target'); + $metadata = $messages->getMetadata($source, $domain); + if ($this->hasMetadataArrayInfo('target-attributes', $metadata)) { + foreach ($metadata['target-attributes'] as $name => $value) { + $targetElement->setAttribute($name, $value); + } + } + $t = $translation->appendChild($targetElement); + $t->appendChild($text); + + if ($this->hasMetadataArrayInfo('notes', $metadata)) { + foreach ($metadata['notes'] as $note) { + if (!isset($note['content'])) { + continue; + } + + $n = $translation->appendChild($dom->createElement('note')); + $n->appendChild($dom->createTextNode($note['content'])); + + if (isset($note['priority'])) { + $n->setAttribute('priority', $note['priority']); + } + + if (isset($note['from'])) { + $n->setAttribute('from', $note['from']); + } + } + } + + $xliffBody->appendChild($translation); + } + + return $dom->saveXML(); + } + + private function dumpXliff2($defaultLocale, MessageCatalogue $messages, $domain, array $options = array()) + { + $dom = new \DOMDocument('1.0', 'utf-8'); + $dom->formatOutput = true; + + $xliff = $dom->appendChild($dom->createElement('xliff')); + $xliff->setAttribute('xmlns', 'urn:oasis:names:tc:xliff:document:2.0'); + $xliff->setAttribute('version', '2.0'); + $xliff->setAttribute('srcLang', str_replace('_', '-', $defaultLocale)); + $xliff->setAttribute('trgLang', str_replace('_', '-', $messages->getLocale())); + + $xliffFile = $xliff->appendChild($dom->createElement('file')); + $xliffFile->setAttribute('id', $domain.'.'.$messages->getLocale()); + + foreach ($messages->all($domain) as $source => $target) { + $translation = $dom->createElement('unit'); + $translation->setAttribute('id', strtr(substr(base64_encode(hash('sha256', $source, true)), 0, 7), '/+', '._')); + $name = $source; + if (\strlen($source) > 80) { + $name = substr(md5($source), -7); + } + $translation->setAttribute('name', $name); + $metadata = $messages->getMetadata($source, $domain); + + // Add notes section + if ($this->hasMetadataArrayInfo('notes', $metadata)) { + $notesElement = $dom->createElement('notes'); + foreach ($metadata['notes'] as $note) { + $n = $dom->createElement('note'); + $n->appendChild($dom->createTextNode(isset($note['content']) ? $note['content'] : '')); + unset($note['content']); + + foreach ($note as $name => $value) { + $n->setAttribute($name, $value); + } + $notesElement->appendChild($n); + } + $translation->appendChild($notesElement); + } + + $segment = $translation->appendChild($dom->createElement('segment')); + + $s = $segment->appendChild($dom->createElement('source')); + $s->appendChild($dom->createTextNode($source)); + + // Does the target contain characters requiring a CDATA section? + $text = 1 === preg_match('/[&<>]/', $target) ? $dom->createCDATASection($target) : $dom->createTextNode($target); + + $targetElement = $dom->createElement('target'); + if ($this->hasMetadataArrayInfo('target-attributes', $metadata)) { + foreach ($metadata['target-attributes'] as $name => $value) { + $targetElement->setAttribute($name, $value); + } + } + $t = $segment->appendChild($targetElement); + $t->appendChild($text); + + $xliffFile->appendChild($translation); + } + + return $dom->saveXML(); + } + + /** + * @param string $key + * @param array|null $metadata + * + * @return bool + */ + private function hasMetadataArrayInfo($key, $metadata = null) + { + return null !== $metadata && array_key_exists($key, $metadata) && ($metadata[$key] instanceof \Traversable || \is_array($metadata[$key])); + } +} diff --git a/vendor/symfony/translation/Dumper/YamlFileDumper.php b/vendor/symfony/translation/Dumper/YamlFileDumper.php new file mode 100644 index 000000000..b19b9ae1f --- /dev/null +++ b/vendor/symfony/translation/Dumper/YamlFileDumper.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Dumper; + +use Symfony\Component\Translation\Exception\LogicException; +use Symfony\Component\Translation\MessageCatalogue; +use Symfony\Component\Translation\Util\ArrayConverter; +use Symfony\Component\Yaml\Yaml; + +/** + * YamlFileDumper generates yaml files from a message catalogue. + * + * @author Michel Salib + */ +class YamlFileDumper extends FileDumper +{ + private $extension; + + public function __construct(string $extension = 'yml') + { + $this->extension = $extension; + } + + /** + * {@inheritdoc} + */ + public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array()) + { + if (!class_exists('Symfony\Component\Yaml\Yaml')) { + throw new LogicException('Dumping translations in the YAML format requires the Symfony Yaml component.'); + } + + $data = $messages->all($domain); + + if (isset($options['as_tree']) && $options['as_tree']) { + $data = ArrayConverter::expandToTree($data); + } + + if (isset($options['inline']) && ($inline = (int) $options['inline']) > 0) { + return Yaml::dump($data, $inline); + } + + return Yaml::dump($data); + } + + /** + * {@inheritdoc} + */ + protected function getExtension() + { + return $this->extension; + } +} diff --git a/vendor/symfony/translation/Exception/ExceptionInterface.php b/vendor/symfony/translation/Exception/ExceptionInterface.php new file mode 100644 index 000000000..c85fb93ca --- /dev/null +++ b/vendor/symfony/translation/Exception/ExceptionInterface.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Exception; + +/** + * Exception interface for all exceptions thrown by the component. + * + * @author Fabien Potencier + */ +interface ExceptionInterface +{ +} diff --git a/vendor/symfony/translation/Exception/InvalidArgumentException.php b/vendor/symfony/translation/Exception/InvalidArgumentException.php new file mode 100644 index 000000000..90d06690f --- /dev/null +++ b/vendor/symfony/translation/Exception/InvalidArgumentException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Exception; + +/** + * Base InvalidArgumentException for the Translation component. + * + * @author Abdellatif Ait boudad + */ +class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/translation/Exception/InvalidResourceException.php b/vendor/symfony/translation/Exception/InvalidResourceException.php new file mode 100644 index 000000000..cf079432c --- /dev/null +++ b/vendor/symfony/translation/Exception/InvalidResourceException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Exception; + +/** + * Thrown when a resource cannot be loaded. + * + * @author Fabien Potencier + */ +class InvalidResourceException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/translation/Exception/LogicException.php b/vendor/symfony/translation/Exception/LogicException.php new file mode 100644 index 000000000..9019c7e7b --- /dev/null +++ b/vendor/symfony/translation/Exception/LogicException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Exception; + +/** + * Base LogicException for Translation component. + * + * @author Abdellatif Ait boudad + */ +class LogicException extends \LogicException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/translation/Exception/NotFoundResourceException.php b/vendor/symfony/translation/Exception/NotFoundResourceException.php new file mode 100644 index 000000000..cff73ae30 --- /dev/null +++ b/vendor/symfony/translation/Exception/NotFoundResourceException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Exception; + +/** + * Thrown when a resource does not exist. + * + * @author Fabien Potencier + */ +class NotFoundResourceException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/translation/Exception/RuntimeException.php b/vendor/symfony/translation/Exception/RuntimeException.php new file mode 100644 index 000000000..dcd794082 --- /dev/null +++ b/vendor/symfony/translation/Exception/RuntimeException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Exception; + +/** + * Base RuntimeException for the Translation component. + * + * @author Abdellatif Ait boudad + */ +class RuntimeException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/translation/Extractor/AbstractFileExtractor.php b/vendor/symfony/translation/Extractor/AbstractFileExtractor.php new file mode 100644 index 000000000..bebd6d916 --- /dev/null +++ b/vendor/symfony/translation/Extractor/AbstractFileExtractor.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Extractor; + +use Symfony\Component\Translation\Exception\InvalidArgumentException; + +/** + * Base class used by classes that extract translation messages from files. + * + * @author Marcos D. Sánchez + */ +abstract class AbstractFileExtractor +{ + /** + * @param string|array $resource Files, a file or a directory + * + * @return array + */ + protected function extractFiles($resource) + { + if (\is_array($resource) || $resource instanceof \Traversable) { + $files = array(); + foreach ($resource as $file) { + if ($this->canBeExtracted($file)) { + $files[] = $this->toSplFileInfo($file); + } + } + } elseif (is_file($resource)) { + $files = $this->canBeExtracted($resource) ? array($this->toSplFileInfo($resource)) : array(); + } else { + $files = $this->extractFromDirectory($resource); + } + + return $files; + } + + private function toSplFileInfo(string $file): \SplFileInfo + { + return new \SplFileInfo($file); + } + + /** + * @param string $file + * + * @return bool + * + * @throws InvalidArgumentException + */ + protected function isFile($file) + { + if (!is_file($file)) { + throw new InvalidArgumentException(sprintf('The "%s" file does not exist.', $file)); + } + + return true; + } + + /** + * @param string $file + * + * @return bool + */ + abstract protected function canBeExtracted($file); + + /** + * @param string|array $resource Files, a file or a directory + * + * @return array files to be extracted + */ + abstract protected function extractFromDirectory($resource); +} diff --git a/vendor/symfony/translation/Extractor/ChainExtractor.php b/vendor/symfony/translation/Extractor/ChainExtractor.php new file mode 100644 index 000000000..50e3c8457 --- /dev/null +++ b/vendor/symfony/translation/Extractor/ChainExtractor.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Extractor; + +use Symfony\Component\Translation\MessageCatalogue; + +/** + * ChainExtractor extracts translation messages from template files. + * + * @author Michel Salib + */ +class ChainExtractor implements ExtractorInterface +{ + /** + * The extractors. + * + * @var ExtractorInterface[] + */ + private $extractors = array(); + + /** + * Adds a loader to the translation extractor. + * + * @param string $format The format of the loader + * @param ExtractorInterface $extractor The loader + */ + public function addExtractor($format, ExtractorInterface $extractor) + { + $this->extractors[$format] = $extractor; + } + + /** + * {@inheritdoc} + */ + public function setPrefix($prefix) + { + foreach ($this->extractors as $extractor) { + $extractor->setPrefix($prefix); + } + } + + /** + * {@inheritdoc} + */ + public function extract($directory, MessageCatalogue $catalogue) + { + foreach ($this->extractors as $extractor) { + $extractor->extract($directory, $catalogue); + } + } +} diff --git a/vendor/symfony/translation/Extractor/ExtractorInterface.php b/vendor/symfony/translation/Extractor/ExtractorInterface.php new file mode 100644 index 000000000..b4534fae7 --- /dev/null +++ b/vendor/symfony/translation/Extractor/ExtractorInterface.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Extractor; + +use Symfony\Component\Translation\MessageCatalogue; + +/** + * Extracts translation messages from a directory or files to the catalogue. + * New found messages are injected to the catalogue using the prefix. + * + * @author Michel Salib + */ +interface ExtractorInterface +{ + /** + * Extracts translation messages from files, a file or a directory to the catalogue. + * + * @param string|array $resource Files, a file or a directory + * @param MessageCatalogue $catalogue The catalogue + */ + public function extract($resource, MessageCatalogue $catalogue); + + /** + * Sets the prefix that should be used for new found messages. + * + * @param string $prefix The prefix + */ + public function setPrefix($prefix); +} diff --git a/vendor/symfony/translation/Extractor/PhpExtractor.php b/vendor/symfony/translation/Extractor/PhpExtractor.php new file mode 100644 index 000000000..b4b32361f --- /dev/null +++ b/vendor/symfony/translation/Extractor/PhpExtractor.php @@ -0,0 +1,256 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Extractor; + +use Symfony\Component\Finder\Finder; +use Symfony\Component\Translation\MessageCatalogue; + +/** + * PhpExtractor extracts translation messages from a PHP template. + * + * @author Michel Salib + */ +class PhpExtractor extends AbstractFileExtractor implements ExtractorInterface +{ + const MESSAGE_TOKEN = 300; + const METHOD_ARGUMENTS_TOKEN = 1000; + const DOMAIN_TOKEN = 1001; + + /** + * Prefix for new found message. + * + * @var string + */ + private $prefix = ''; + + /** + * The sequence that captures translation messages. + * + * @var array + */ + protected $sequences = array( + array( + '->', + 'trans', + '(', + self::MESSAGE_TOKEN, + ',', + self::METHOD_ARGUMENTS_TOKEN, + ',', + self::DOMAIN_TOKEN, + ), + array( + '->', + 'transChoice', + '(', + self::MESSAGE_TOKEN, + ',', + self::METHOD_ARGUMENTS_TOKEN, + ',', + self::METHOD_ARGUMENTS_TOKEN, + ',', + self::DOMAIN_TOKEN, + ), + array( + '->', + 'trans', + '(', + self::MESSAGE_TOKEN, + ), + array( + '->', + 'transChoice', + '(', + self::MESSAGE_TOKEN, + ), + ); + + /** + * {@inheritdoc} + */ + public function extract($resource, MessageCatalogue $catalog) + { + $files = $this->extractFiles($resource); + foreach ($files as $file) { + $this->parseTokens(token_get_all(file_get_contents($file)), $catalog); + + // PHP 7 memory manager will not release after token_get_all(), see https://bugs.php.net/70098 + gc_mem_caches(); + } + } + + /** + * {@inheritdoc} + */ + public function setPrefix($prefix) + { + $this->prefix = $prefix; + } + + /** + * Normalizes a token. + * + * @param mixed $token + * + * @return string + */ + protected function normalizeToken($token) + { + if (isset($token[1]) && 'b"' !== $token) { + return $token[1]; + } + + return $token; + } + + /** + * Seeks to a non-whitespace token. + */ + private function seekToNextRelevantToken(\Iterator $tokenIterator) + { + for (; $tokenIterator->valid(); $tokenIterator->next()) { + $t = $tokenIterator->current(); + if (T_WHITESPACE !== $t[0]) { + break; + } + } + } + + private function skipMethodArgument(\Iterator $tokenIterator) + { + $openBraces = 0; + + for (; $tokenIterator->valid(); $tokenIterator->next()) { + $t = $tokenIterator->current(); + + if ('[' === $t[0] || '(' === $t[0]) { + ++$openBraces; + } + + if (']' === $t[0] || ')' === $t[0]) { + --$openBraces; + } + + if ((0 === $openBraces && ',' === $t[0]) || (-1 === $openBraces && ')' === $t[0])) { + break; + } + } + } + + /** + * Extracts the message from the iterator while the tokens + * match allowed message tokens. + */ + private function getValue(\Iterator $tokenIterator) + { + $message = ''; + $docToken = ''; + + for (; $tokenIterator->valid(); $tokenIterator->next()) { + $t = $tokenIterator->current(); + if (!isset($t[1])) { + break; + } + + switch ($t[0]) { + case T_START_HEREDOC: + $docToken = $t[1]; + break; + case T_ENCAPSED_AND_WHITESPACE: + case T_CONSTANT_ENCAPSED_STRING: + $message .= $t[1]; + break; + case T_END_HEREDOC: + return PhpStringTokenParser::parseDocString($docToken, $message); + default: + break 2; + } + } + + if ($message) { + $message = PhpStringTokenParser::parse($message); + } + + return $message; + } + + /** + * Extracts trans message from PHP tokens. + * + * @param array $tokens + * @param MessageCatalogue $catalog + */ + protected function parseTokens($tokens, MessageCatalogue $catalog) + { + $tokenIterator = new \ArrayIterator($tokens); + + for ($key = 0; $key < $tokenIterator->count(); ++$key) { + foreach ($this->sequences as $sequence) { + $message = ''; + $domain = 'messages'; + $tokenIterator->seek($key); + + foreach ($sequence as $sequenceKey => $item) { + $this->seekToNextRelevantToken($tokenIterator); + + if ($this->normalizeToken($tokenIterator->current()) === $item) { + $tokenIterator->next(); + continue; + } elseif (self::MESSAGE_TOKEN === $item) { + $message = $this->getValue($tokenIterator); + + if (\count($sequence) === ($sequenceKey + 1)) { + break; + } + } elseif (self::METHOD_ARGUMENTS_TOKEN === $item) { + $this->skipMethodArgument($tokenIterator); + } elseif (self::DOMAIN_TOKEN === $item) { + $domain = $this->getValue($tokenIterator); + + break; + } else { + break; + } + } + + if ($message) { + $catalog->set($message, $this->prefix.$message, $domain); + break; + } + } + } + } + + /** + * @param string $file + * + * @return bool + * + * @throws \InvalidArgumentException + */ + protected function canBeExtracted($file) + { + return $this->isFile($file) && 'php' === pathinfo($file, PATHINFO_EXTENSION); + } + + /** + * @param string|array $directory + * + * @return array + */ + protected function extractFromDirectory($directory) + { + $finder = new Finder(); + + return $finder->files()->name('*.php')->in($directory); + } +} diff --git a/vendor/symfony/translation/Extractor/PhpStringTokenParser.php b/vendor/symfony/translation/Extractor/PhpStringTokenParser.php new file mode 100644 index 000000000..e8094e6c0 --- /dev/null +++ b/vendor/symfony/translation/Extractor/PhpStringTokenParser.php @@ -0,0 +1,142 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Extractor; + +/* + * The following is derived from code at http://github.com/nikic/PHP-Parser + * + * Copyright (c) 2011 by Nikita Popov + * + * Some rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * * The names of the contributors may not be used to endorse or + * promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +class PhpStringTokenParser +{ + protected static $replacements = array( + '\\' => '\\', + '$' => '$', + 'n' => "\n", + 'r' => "\r", + 't' => "\t", + 'f' => "\f", + 'v' => "\v", + 'e' => "\x1B", + ); + + /** + * Parses a string token. + * + * @param string $str String token content + * + * @return string The parsed string + */ + public static function parse($str) + { + $bLength = 0; + if ('b' === $str[0]) { + $bLength = 1; + } + + if ('\'' === $str[$bLength]) { + return str_replace( + array('\\\\', '\\\''), + array('\\', '\''), + substr($str, $bLength + 1, -1) + ); + } else { + return self::parseEscapeSequences(substr($str, $bLength + 1, -1), '"'); + } + } + + /** + * Parses escape sequences in strings (all string types apart from single quoted). + * + * @param string $str String without quotes + * @param string|null $quote Quote type + * + * @return string String with escape sequences parsed + */ + public static function parseEscapeSequences($str, $quote) + { + if (null !== $quote) { + $str = str_replace('\\'.$quote, $quote, $str); + } + + return preg_replace_callback( + '~\\\\([\\\\$nrtfve]|[xX][0-9a-fA-F]{1,2}|[0-7]{1,3})~', + array(__CLASS__, 'parseCallback'), + $str + ); + } + + private static function parseCallback($matches) + { + $str = $matches[1]; + + if (isset(self::$replacements[$str])) { + return self::$replacements[$str]; + } elseif ('x' === $str[0] || 'X' === $str[0]) { + return \chr(hexdec($str)); + } else { + return \chr(octdec($str)); + } + } + + /** + * Parses a constant doc string. + * + * @param string $startToken Doc string start token content (<< + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Formatter; + +/** + * @author Abdellatif Ait boudad + */ +interface ChoiceMessageFormatterInterface +{ + /** + * Formats a localized message pattern with given arguments. + * + * @param string $message The message (may also be an object that can be cast to string) + * @param int $number The number to use to find the indice of the message + * @param string $locale The message locale + * @param array $parameters An array of parameters for the message + * + * @return string + */ + public function choiceFormat($message, $number, $locale, array $parameters = array()); +} diff --git a/vendor/symfony/translation/Formatter/MessageFormatter.php b/vendor/symfony/translation/Formatter/MessageFormatter.php new file mode 100644 index 000000000..e174be36c --- /dev/null +++ b/vendor/symfony/translation/Formatter/MessageFormatter.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\Translation\Formatter; + +use Symfony\Component\Translation\MessageSelector; + +/** + * @author Abdellatif Ait boudad + */ +class MessageFormatter implements MessageFormatterInterface, ChoiceMessageFormatterInterface +{ + private $selector; + + /** + * @param MessageSelector|null $selector The message selector for pluralization + */ + public function __construct(MessageSelector $selector = null) + { + $this->selector = $selector ?: new MessageSelector(); + } + + /** + * {@inheritdoc} + */ + public function format($message, $locale, array $parameters = array()) + { + return strtr($message, $parameters); + } + + /** + * {@inheritdoc} + */ + public function choiceFormat($message, $number, $locale, array $parameters = array()) + { + $parameters = array_merge(array('%count%' => $number), $parameters); + + return $this->format($this->selector->choose($message, (int) $number, $locale), $locale, $parameters); + } +} diff --git a/vendor/symfony/translation/Formatter/MessageFormatterInterface.php b/vendor/symfony/translation/Formatter/MessageFormatterInterface.php new file mode 100644 index 000000000..86937fb2f --- /dev/null +++ b/vendor/symfony/translation/Formatter/MessageFormatterInterface.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Formatter; + +/** + * @author Guilherme Blanco + * @author Abdellatif Ait boudad + */ +interface MessageFormatterInterface +{ + /** + * Formats a localized message pattern with given arguments. + * + * @param string $message The message (may also be an object that can be cast to string) + * @param string $locale The message locale + * @param array $parameters An array of parameters for the message + * + * @return string + */ + public function format($message, $locale, array $parameters = array()); +} diff --git a/vendor/symfony/translation/IdentityTranslator.php b/vendor/symfony/translation/IdentityTranslator.php new file mode 100644 index 000000000..82b247015 --- /dev/null +++ b/vendor/symfony/translation/IdentityTranslator.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation; + +/** + * IdentityTranslator does not translate anything. + * + * @author Fabien Potencier + */ +class IdentityTranslator implements TranslatorInterface +{ + private $selector; + private $locale; + + /** + * @param MessageSelector|null $selector The message selector for pluralization + */ + public function __construct(MessageSelector $selector = null) + { + $this->selector = $selector ?: new MessageSelector(); + } + + /** + * {@inheritdoc} + */ + public function setLocale($locale) + { + $this->locale = $locale; + } + + /** + * {@inheritdoc} + */ + public function getLocale() + { + return $this->locale ?: \Locale::getDefault(); + } + + /** + * {@inheritdoc} + */ + public function trans($id, array $parameters = array(), $domain = null, $locale = null) + { + return strtr((string) $id, $parameters); + } + + /** + * {@inheritdoc} + */ + public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null) + { + return strtr($this->selector->choose((string) $id, (int) $number, $locale ?: $this->getLocale()), $parameters); + } +} diff --git a/vendor/symfony/translation/Interval.php b/vendor/symfony/translation/Interval.php new file mode 100644 index 000000000..9e2cae648 --- /dev/null +++ b/vendor/symfony/translation/Interval.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\Translation; + +use Symfony\Component\Translation\Exception\InvalidArgumentException; + +/** + * Tests if a given number belongs to a given math interval. + * + * An interval can represent a finite set of numbers: + * + * {1,2,3,4} + * + * An interval can represent numbers between two numbers: + * + * [1, +Inf] + * ]-1,2[ + * + * The left delimiter can be [ (inclusive) or ] (exclusive). + * The right delimiter can be [ (exclusive) or ] (inclusive). + * Beside numbers, you can use -Inf and +Inf for the infinite. + * + * @author Fabien Potencier + * + * @see http://en.wikipedia.org/wiki/Interval_%28mathematics%29#The_ISO_notation + */ +class Interval +{ + /** + * Tests if the given number is in the math interval. + * + * @param int $number A number + * @param string $interval An interval + * + * @return bool + * + * @throws InvalidArgumentException + */ + public static function test($number, $interval) + { + $interval = trim($interval); + + if (!preg_match('/^'.self::getIntervalRegexp().'$/x', $interval, $matches)) { + throw new InvalidArgumentException(sprintf('"%s" is not a valid interval.', $interval)); + } + + if ($matches[1]) { + foreach (explode(',', $matches[2]) as $n) { + if ($number == $n) { + return true; + } + } + } else { + $leftNumber = self::convertNumber($matches['left']); + $rightNumber = self::convertNumber($matches['right']); + + return + ('[' === $matches['left_delimiter'] ? $number >= $leftNumber : $number > $leftNumber) + && (']' === $matches['right_delimiter'] ? $number <= $rightNumber : $number < $rightNumber) + ; + } + + return false; + } + + /** + * Returns a Regexp that matches valid intervals. + * + * @return string A Regexp (without the delimiters) + */ + public static function getIntervalRegexp() + { + return <<[\[\]]) + \s* + (?P-Inf|\-?\d+(\.\d+)?) + \s*,\s* + (?P\+?Inf|\-?\d+(\.\d+)?) + \s* + (?P[\[\]]) +EOF; + } + + private static function convertNumber($number) + { + if ('-Inf' === $number) { + return log(0); + } elseif ('+Inf' === $number || 'Inf' === $number) { + return -log(0); + } + + return (float) $number; + } +} diff --git a/vendor/symfony/translation/LICENSE b/vendor/symfony/translation/LICENSE new file mode 100644 index 000000000..21d7fb9e2 --- /dev/null +++ b/vendor/symfony/translation/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2018 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/symfony/translation/Loader/ArrayLoader.php b/vendor/symfony/translation/Loader/ArrayLoader.php new file mode 100644 index 000000000..7bbfca68a --- /dev/null +++ b/vendor/symfony/translation/Loader/ArrayLoader.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Loader; + +use Symfony\Component\Translation\MessageCatalogue; + +/** + * ArrayLoader loads translations from a PHP array. + * + * @author Fabien Potencier + */ +class ArrayLoader implements LoaderInterface +{ + /** + * {@inheritdoc} + */ + public function load($resource, $locale, $domain = 'messages') + { + $this->flatten($resource); + $catalogue = new MessageCatalogue($locale); + $catalogue->add($resource, $domain); + + return $catalogue; + } + + /** + * Flattens an nested array of translations. + * + * The scheme used is: + * 'key' => array('key2' => array('key3' => 'value')) + * Becomes: + * 'key.key2.key3' => 'value' + * + * This function takes an array by reference and will modify it + * + * @param array &$messages The array that will be flattened + * @param array $subnode Current subnode being parsed, used internally for recursive calls + * @param string $path Current path being parsed, used internally for recursive calls + */ + private function flatten(array &$messages, array $subnode = null, $path = null) + { + if (null === $subnode) { + $subnode = &$messages; + } + foreach ($subnode as $key => $value) { + if (\is_array($value)) { + $nodePath = $path ? $path.'.'.$key : $key; + $this->flatten($messages, $value, $nodePath); + if (null === $path) { + unset($messages[$key]); + } + } elseif (null !== $path) { + $messages[$path.'.'.$key] = $value; + } + } + } +} diff --git a/vendor/symfony/translation/Loader/CsvFileLoader.php b/vendor/symfony/translation/Loader/CsvFileLoader.php new file mode 100644 index 000000000..8a763e725 --- /dev/null +++ b/vendor/symfony/translation/Loader/CsvFileLoader.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Loader; + +use Symfony\Component\Translation\Exception\NotFoundResourceException; + +/** + * CsvFileLoader loads translations from CSV files. + * + * @author Saša Stamenković + */ +class CsvFileLoader extends FileLoader +{ + private $delimiter = ';'; + private $enclosure = '"'; + private $escape = '\\'; + + /** + * {@inheritdoc} + */ + protected function loadResource($resource) + { + $messages = array(); + + try { + $file = new \SplFileObject($resource, 'rb'); + } catch (\RuntimeException $e) { + throw new NotFoundResourceException(sprintf('Error opening file "%s".', $resource), 0, $e); + } + + $file->setFlags(\SplFileObject::READ_CSV | \SplFileObject::SKIP_EMPTY); + $file->setCsvControl($this->delimiter, $this->enclosure, $this->escape); + + foreach ($file as $data) { + if ('#' !== substr($data[0], 0, 1) && isset($data[1]) && 2 === \count($data)) { + $messages[$data[0]] = $data[1]; + } + } + + return $messages; + } + + /** + * Sets the delimiter, enclosure, and escape character for CSV. + * + * @param string $delimiter Delimiter character + * @param string $enclosure Enclosure character + * @param string $escape Escape character + */ + public function setCsvControl($delimiter = ';', $enclosure = '"', $escape = '\\') + { + $this->delimiter = $delimiter; + $this->enclosure = $enclosure; + $this->escape = $escape; + } +} diff --git a/vendor/symfony/translation/Loader/FileLoader.php b/vendor/symfony/translation/Loader/FileLoader.php new file mode 100644 index 000000000..9a02f5250 --- /dev/null +++ b/vendor/symfony/translation/Loader/FileLoader.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Loader; + +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\Translation\Exception\InvalidResourceException; +use Symfony\Component\Translation\Exception\NotFoundResourceException; + +/** + * @author Abdellatif Ait boudad + */ +abstract class FileLoader extends ArrayLoader +{ + /** + * {@inheritdoc} + */ + public function load($resource, $locale, $domain = 'messages') + { + if (!stream_is_local($resource)) { + throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource)); + } + + if (!file_exists($resource)) { + throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource)); + } + + $messages = $this->loadResource($resource); + + // empty resource + if (null === $messages) { + $messages = array(); + } + + // not an array + if (!\is_array($messages)) { + throw new InvalidResourceException(sprintf('Unable to load file "%s".', $resource)); + } + + $catalogue = parent::load($messages, $locale, $domain); + + if (class_exists('Symfony\Component\Config\Resource\FileResource')) { + $catalogue->addResource(new FileResource($resource)); + } + + return $catalogue; + } + + /** + * @param string $resource + * + * @return array + * + * @throws InvalidResourceException if stream content has an invalid format + */ + abstract protected function loadResource($resource); +} diff --git a/vendor/symfony/translation/Loader/IcuDatFileLoader.php b/vendor/symfony/translation/Loader/IcuDatFileLoader.php new file mode 100644 index 000000000..7bbf4c200 --- /dev/null +++ b/vendor/symfony/translation/Loader/IcuDatFileLoader.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Loader; + +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\Translation\Exception\InvalidResourceException; +use Symfony\Component\Translation\Exception\NotFoundResourceException; +use Symfony\Component\Translation\MessageCatalogue; + +/** + * IcuResFileLoader loads translations from a resource bundle. + * + * @author stealth35 + */ +class IcuDatFileLoader extends IcuResFileLoader +{ + /** + * {@inheritdoc} + */ + public function load($resource, $locale, $domain = 'messages') + { + if (!stream_is_local($resource.'.dat')) { + throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource)); + } + + if (!file_exists($resource.'.dat')) { + throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource)); + } + + try { + $rb = new \ResourceBundle($locale, $resource); + } catch (\Exception $e) { + $rb = null; + } + + if (!$rb) { + throw new InvalidResourceException(sprintf('Cannot load resource "%s"', $resource)); + } elseif (intl_is_failure($rb->getErrorCode())) { + throw new InvalidResourceException($rb->getErrorMessage(), $rb->getErrorCode()); + } + + $messages = $this->flatten($rb); + $catalogue = new MessageCatalogue($locale); + $catalogue->add($messages, $domain); + + if (class_exists('Symfony\Component\Config\Resource\FileResource')) { + $catalogue->addResource(new FileResource($resource.'.dat')); + } + + return $catalogue; + } +} diff --git a/vendor/symfony/translation/Loader/IcuResFileLoader.php b/vendor/symfony/translation/Loader/IcuResFileLoader.php new file mode 100644 index 000000000..28ace2977 --- /dev/null +++ b/vendor/symfony/translation/Loader/IcuResFileLoader.php @@ -0,0 +1,91 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Loader; + +use Symfony\Component\Config\Resource\DirectoryResource; +use Symfony\Component\Translation\Exception\InvalidResourceException; +use Symfony\Component\Translation\Exception\NotFoundResourceException; +use Symfony\Component\Translation\MessageCatalogue; + +/** + * IcuResFileLoader loads translations from a resource bundle. + * + * @author stealth35 + */ +class IcuResFileLoader implements LoaderInterface +{ + /** + * {@inheritdoc} + */ + public function load($resource, $locale, $domain = 'messages') + { + if (!stream_is_local($resource)) { + throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource)); + } + + if (!is_dir($resource)) { + throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource)); + } + + try { + $rb = new \ResourceBundle($locale, $resource); + } catch (\Exception $e) { + $rb = null; + } + + if (!$rb) { + throw new InvalidResourceException(sprintf('Cannot load resource "%s"', $resource)); + } elseif (intl_is_failure($rb->getErrorCode())) { + throw new InvalidResourceException($rb->getErrorMessage(), $rb->getErrorCode()); + } + + $messages = $this->flatten($rb); + $catalogue = new MessageCatalogue($locale); + $catalogue->add($messages, $domain); + + if (class_exists('Symfony\Component\Config\Resource\DirectoryResource')) { + $catalogue->addResource(new DirectoryResource($resource)); + } + + return $catalogue; + } + + /** + * Flattens an ResourceBundle. + * + * The scheme used is: + * key { key2 { key3 { "value" } } } + * Becomes: + * 'key.key2.key3' => 'value' + * + * This function takes an array by reference and will modify it + * + * @param \ResourceBundle $rb The ResourceBundle that will be flattened + * @param array $messages Used internally for recursive calls + * @param string $path Current path being parsed, used internally for recursive calls + * + * @return array the flattened ResourceBundle + */ + protected function flatten(\ResourceBundle $rb, array &$messages = array(), $path = null) + { + foreach ($rb as $key => $value) { + $nodePath = $path ? $path.'.'.$key : $key; + if ($value instanceof \ResourceBundle) { + $this->flatten($value, $messages, $nodePath); + } else { + $messages[$nodePath] = $value; + } + } + + return $messages; + } +} diff --git a/vendor/symfony/translation/Loader/IniFileLoader.php b/vendor/symfony/translation/Loader/IniFileLoader.php new file mode 100644 index 000000000..11d9b272e --- /dev/null +++ b/vendor/symfony/translation/Loader/IniFileLoader.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Loader; + +/** + * IniFileLoader loads translations from an ini file. + * + * @author stealth35 + */ +class IniFileLoader extends FileLoader +{ + /** + * {@inheritdoc} + */ + protected function loadResource($resource) + { + return parse_ini_file($resource, true); + } +} diff --git a/vendor/symfony/translation/Loader/JsonFileLoader.php b/vendor/symfony/translation/Loader/JsonFileLoader.php new file mode 100644 index 000000000..ce4e91ff4 --- /dev/null +++ b/vendor/symfony/translation/Loader/JsonFileLoader.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Loader; + +use Symfony\Component\Translation\Exception\InvalidResourceException; + +/** + * JsonFileLoader loads translations from an json file. + * + * @author singles + */ +class JsonFileLoader extends FileLoader +{ + /** + * {@inheritdoc} + */ + protected function loadResource($resource) + { + $messages = array(); + if ($data = file_get_contents($resource)) { + $messages = json_decode($data, true); + + if (0 < $errorCode = json_last_error()) { + throw new InvalidResourceException(sprintf('Error parsing JSON - %s', $this->getJSONErrorMessage($errorCode))); + } + } + + return $messages; + } + + /** + * Translates JSON_ERROR_* constant into meaningful message. + * + * @param int $errorCode Error code returned by json_last_error() call + * + * @return string Message string + */ + private function getJSONErrorMessage($errorCode) + { + switch ($errorCode) { + case JSON_ERROR_DEPTH: + return 'Maximum stack depth exceeded'; + case JSON_ERROR_STATE_MISMATCH: + return 'Underflow or the modes mismatch'; + case JSON_ERROR_CTRL_CHAR: + return 'Unexpected control character found'; + case JSON_ERROR_SYNTAX: + return 'Syntax error, malformed JSON'; + case JSON_ERROR_UTF8: + return 'Malformed UTF-8 characters, possibly incorrectly encoded'; + default: + return 'Unknown error'; + } + } +} diff --git a/vendor/symfony/translation/Loader/LoaderInterface.php b/vendor/symfony/translation/Loader/LoaderInterface.php new file mode 100644 index 000000000..1785402d9 --- /dev/null +++ b/vendor/symfony/translation/Loader/LoaderInterface.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Loader; + +use Symfony\Component\Translation\Exception\InvalidResourceException; +use Symfony\Component\Translation\Exception\NotFoundResourceException; +use Symfony\Component\Translation\MessageCatalogue; + +/** + * LoaderInterface is the interface implemented by all translation loaders. + * + * @author Fabien Potencier + */ +interface LoaderInterface +{ + /** + * Loads a locale. + * + * @param mixed $resource A resource + * @param string $locale A locale + * @param string $domain The domain + * + * @return MessageCatalogue A MessageCatalogue instance + * + * @throws NotFoundResourceException when the resource cannot be found + * @throws InvalidResourceException when the resource cannot be loaded + */ + public function load($resource, $locale, $domain = 'messages'); +} diff --git a/vendor/symfony/translation/Loader/MoFileLoader.php b/vendor/symfony/translation/Loader/MoFileLoader.php new file mode 100644 index 000000000..a1d7c4aef --- /dev/null +++ b/vendor/symfony/translation/Loader/MoFileLoader.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\Translation\Loader; + +use Symfony\Component\Translation\Exception\InvalidResourceException; + +/** + * @copyright Copyright (c) 2010, Union of RAD http://union-of-rad.org (http://lithify.me/) + */ +class MoFileLoader extends FileLoader +{ + /** + * Magic used for validating the format of a MO file as well as + * detecting if the machine used to create that file was little endian. + */ + const MO_LITTLE_ENDIAN_MAGIC = 0x950412de; + + /** + * Magic used for validating the format of a MO file as well as + * detecting if the machine used to create that file was big endian. + */ + const MO_BIG_ENDIAN_MAGIC = 0xde120495; + + /** + * The size of the header of a MO file in bytes. + */ + const MO_HEADER_SIZE = 28; + + /** + * Parses machine object (MO) format, independent of the machine's endian it + * was created on. Both 32bit and 64bit systems are supported. + * + * {@inheritdoc} + */ + protected function loadResource($resource) + { + $stream = fopen($resource, 'r'); + + $stat = fstat($stream); + + if ($stat['size'] < self::MO_HEADER_SIZE) { + throw new InvalidResourceException('MO stream content has an invalid format.'); + } + $magic = unpack('V1', fread($stream, 4)); + $magic = hexdec(substr(dechex(current($magic)), -8)); + + if (self::MO_LITTLE_ENDIAN_MAGIC == $magic) { + $isBigEndian = false; + } elseif (self::MO_BIG_ENDIAN_MAGIC == $magic) { + $isBigEndian = true; + } else { + throw new InvalidResourceException('MO stream content has an invalid format.'); + } + + // formatRevision + $this->readLong($stream, $isBigEndian); + $count = $this->readLong($stream, $isBigEndian); + $offsetId = $this->readLong($stream, $isBigEndian); + $offsetTranslated = $this->readLong($stream, $isBigEndian); + // sizeHashes + $this->readLong($stream, $isBigEndian); + // offsetHashes + $this->readLong($stream, $isBigEndian); + + $messages = array(); + + for ($i = 0; $i < $count; ++$i) { + $pluralId = null; + $translated = null; + + fseek($stream, $offsetId + $i * 8); + + $length = $this->readLong($stream, $isBigEndian); + $offset = $this->readLong($stream, $isBigEndian); + + if ($length < 1) { + continue; + } + + fseek($stream, $offset); + $singularId = fread($stream, $length); + + if (false !== strpos($singularId, "\000")) { + list($singularId, $pluralId) = explode("\000", $singularId); + } + + fseek($stream, $offsetTranslated + $i * 8); + $length = $this->readLong($stream, $isBigEndian); + $offset = $this->readLong($stream, $isBigEndian); + + if ($length < 1) { + continue; + } + + fseek($stream, $offset); + $translated = fread($stream, $length); + + if (false !== strpos($translated, "\000")) { + $translated = explode("\000", $translated); + } + + $ids = array('singular' => $singularId, 'plural' => $pluralId); + $item = compact('ids', 'translated'); + + if (\is_array($item['translated'])) { + $messages[$item['ids']['singular']] = stripcslashes($item['translated'][0]); + if (isset($item['ids']['plural'])) { + $plurals = array(); + foreach ($item['translated'] as $plural => $translated) { + $plurals[] = sprintf('{%d} %s', $plural, $translated); + } + $messages[$item['ids']['plural']] = stripcslashes(implode('|', $plurals)); + } + } elseif (!empty($item['ids']['singular'])) { + $messages[$item['ids']['singular']] = stripcslashes($item['translated']); + } + } + + fclose($stream); + + return array_filter($messages); + } + + /** + * Reads an unsigned long from stream respecting endianness. + * + * @param resource $stream + */ + private function readLong($stream, bool $isBigEndian): int + { + $result = unpack($isBigEndian ? 'N1' : 'V1', fread($stream, 4)); + $result = current($result); + + return (int) substr($result, -8); + } +} diff --git a/vendor/symfony/translation/Loader/PhpFileLoader.php b/vendor/symfony/translation/Loader/PhpFileLoader.php new file mode 100644 index 000000000..a0050e8db --- /dev/null +++ b/vendor/symfony/translation/Loader/PhpFileLoader.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Loader; + +/** + * PhpFileLoader loads translations from PHP files returning an array of translations. + * + * @author Fabien Potencier + */ +class PhpFileLoader extends FileLoader +{ + /** + * {@inheritdoc} + */ + protected function loadResource($resource) + { + return require $resource; + } +} diff --git a/vendor/symfony/translation/Loader/PoFileLoader.php b/vendor/symfony/translation/Loader/PoFileLoader.php new file mode 100644 index 000000000..066168dc7 --- /dev/null +++ b/vendor/symfony/translation/Loader/PoFileLoader.php @@ -0,0 +1,148 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Loader; + +/** + * @copyright Copyright (c) 2010, Union of RAD http://union-of-rad.org (http://lithify.me/) + * @copyright Copyright (c) 2012, Clemens Tolboom + */ +class PoFileLoader extends FileLoader +{ + /** + * Parses portable object (PO) format. + * + * From http://www.gnu.org/software/gettext/manual/gettext.html#PO-Files + * we should be able to parse files having: + * + * white-space + * # translator-comments + * #. extracted-comments + * #: reference... + * #, flag... + * #| msgid previous-untranslated-string + * msgid untranslated-string + * msgstr translated-string + * + * extra or different lines are: + * + * #| msgctxt previous-context + * #| msgid previous-untranslated-string + * msgctxt context + * + * #| msgid previous-untranslated-string-singular + * #| msgid_plural previous-untranslated-string-plural + * msgid untranslated-string-singular + * msgid_plural untranslated-string-plural + * msgstr[0] translated-string-case-0 + * ... + * msgstr[N] translated-string-case-n + * + * The definition states: + * - white-space and comments are optional. + * - msgid "" that an empty singleline defines a header. + * + * This parser sacrifices some features of the reference implementation the + * differences to that implementation are as follows. + * - No support for comments spanning multiple lines. + * - Translator and extracted comments are treated as being the same type. + * - Message IDs are allowed to have other encodings as just US-ASCII. + * + * Items with an empty id are ignored. + * + * {@inheritdoc} + */ + protected function loadResource($resource) + { + $stream = fopen($resource, 'r'); + + $defaults = array( + 'ids' => array(), + 'translated' => null, + ); + + $messages = array(); + $item = $defaults; + $flags = array(); + + while ($line = fgets($stream)) { + $line = trim($line); + + if ('' === $line) { + // Whitespace indicated current item is done + if (!\in_array('fuzzy', $flags)) { + $this->addMessage($messages, $item); + } + $item = $defaults; + $flags = array(); + } elseif ('#,' === substr($line, 0, 2)) { + $flags = array_map('trim', explode(',', substr($line, 2))); + } elseif ('msgid "' === substr($line, 0, 7)) { + // We start a new msg so save previous + // TODO: this fails when comments or contexts are added + $this->addMessage($messages, $item); + $item = $defaults; + $item['ids']['singular'] = substr($line, 7, -1); + } elseif ('msgstr "' === substr($line, 0, 8)) { + $item['translated'] = substr($line, 8, -1); + } elseif ('"' === $line[0]) { + $continues = isset($item['translated']) ? 'translated' : 'ids'; + + if (\is_array($item[$continues])) { + end($item[$continues]); + $item[$continues][key($item[$continues])] .= substr($line, 1, -1); + } else { + $item[$continues] .= substr($line, 1, -1); + } + } elseif ('msgid_plural "' === substr($line, 0, 14)) { + $item['ids']['plural'] = substr($line, 14, -1); + } elseif ('msgstr[' === substr($line, 0, 7)) { + $size = strpos($line, ']'); + $item['translated'][(int) substr($line, 7, 1)] = substr($line, $size + 3, -1); + } + } + // save last item + if (!\in_array('fuzzy', $flags)) { + $this->addMessage($messages, $item); + } + fclose($stream); + + return $messages; + } + + /** + * Save a translation item to the messages. + * + * A .po file could contain by error missing plural indexes. We need to + * fix these before saving them. + */ + private function addMessage(array &$messages, array $item) + { + if (\is_array($item['translated'])) { + $messages[stripcslashes($item['ids']['singular'])] = stripcslashes($item['translated'][0]); + if (isset($item['ids']['plural'])) { + $plurals = $item['translated']; + // PO are by definition indexed so sort by index. + ksort($plurals); + // Make sure every index is filled. + end($plurals); + $count = key($plurals); + // Fill missing spots with '-'. + $empties = array_fill(0, $count + 1, '-'); + $plurals += $empties; + ksort($plurals); + $messages[stripcslashes($item['ids']['plural'])] = stripcslashes(implode('|', $plurals)); + } + } elseif (!empty($item['ids']['singular'])) { + $messages[stripcslashes($item['ids']['singular'])] = stripcslashes($item['translated']); + } + } +} diff --git a/vendor/symfony/translation/Loader/QtFileLoader.php b/vendor/symfony/translation/Loader/QtFileLoader.php new file mode 100644 index 000000000..2d4a4c084 --- /dev/null +++ b/vendor/symfony/translation/Loader/QtFileLoader.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Loader; + +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\Config\Util\XmlUtils; +use Symfony\Component\Translation\Exception\InvalidResourceException; +use Symfony\Component\Translation\Exception\NotFoundResourceException; +use Symfony\Component\Translation\MessageCatalogue; + +/** + * QtFileLoader loads translations from QT Translations XML files. + * + * @author Benjamin Eberlei + */ +class QtFileLoader implements LoaderInterface +{ + /** + * {@inheritdoc} + */ + public function load($resource, $locale, $domain = 'messages') + { + if (!stream_is_local($resource)) { + throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource)); + } + + if (!file_exists($resource)) { + throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource)); + } + + try { + $dom = XmlUtils::loadFile($resource); + } catch (\InvalidArgumentException $e) { + throw new InvalidResourceException(sprintf('Unable to load "%s".', $resource), $e->getCode(), $e); + } + + $internalErrors = libxml_use_internal_errors(true); + libxml_clear_errors(); + + $xpath = new \DOMXPath($dom); + $nodes = $xpath->evaluate('//TS/context/name[text()="'.$domain.'"]'); + + $catalogue = new MessageCatalogue($locale); + if (1 == $nodes->length) { + $translations = $nodes->item(0)->nextSibling->parentNode->parentNode->getElementsByTagName('message'); + foreach ($translations as $translation) { + $translationValue = (string) $translation->getElementsByTagName('translation')->item(0)->nodeValue; + + if (!empty($translationValue)) { + $catalogue->set( + (string) $translation->getElementsByTagName('source')->item(0)->nodeValue, + $translationValue, + $domain + ); + } + $translation = $translation->nextSibling; + } + + if (class_exists('Symfony\Component\Config\Resource\FileResource')) { + $catalogue->addResource(new FileResource($resource)); + } + } + + libxml_use_internal_errors($internalErrors); + + return $catalogue; + } +} diff --git a/vendor/symfony/translation/Loader/XliffFileLoader.php b/vendor/symfony/translation/Loader/XliffFileLoader.php new file mode 100644 index 000000000..9c6b8c9f4 --- /dev/null +++ b/vendor/symfony/translation/Loader/XliffFileLoader.php @@ -0,0 +1,314 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Loader; + +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\Config\Util\XmlUtils; +use Symfony\Component\Translation\Exception\InvalidArgumentException; +use Symfony\Component\Translation\Exception\InvalidResourceException; +use Symfony\Component\Translation\Exception\NotFoundResourceException; +use Symfony\Component\Translation\MessageCatalogue; + +/** + * XliffFileLoader loads translations from XLIFF files. + * + * @author Fabien Potencier + */ +class XliffFileLoader implements LoaderInterface +{ + /** + * {@inheritdoc} + */ + public function load($resource, $locale, $domain = 'messages') + { + if (!stream_is_local($resource)) { + throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource)); + } + + if (!file_exists($resource)) { + throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource)); + } + + $catalogue = new MessageCatalogue($locale); + $this->extract($resource, $catalogue, $domain); + + if (class_exists('Symfony\Component\Config\Resource\FileResource')) { + $catalogue->addResource(new FileResource($resource)); + } + + return $catalogue; + } + + private function extract($resource, MessageCatalogue $catalogue, $domain) + { + try { + $dom = XmlUtils::loadFile($resource); + } catch (\InvalidArgumentException $e) { + throw new InvalidResourceException(sprintf('Unable to load "%s": %s', $resource, $e->getMessage()), $e->getCode(), $e); + } + + $xliffVersion = $this->getVersionNumber($dom); + $this->validateSchema($xliffVersion, $dom, $this->getSchema($xliffVersion)); + + if ('1.2' === $xliffVersion) { + $this->extractXliff1($dom, $catalogue, $domain); + } + + if ('2.0' === $xliffVersion) { + $this->extractXliff2($dom, $catalogue, $domain); + } + } + + /** + * Extract messages and metadata from DOMDocument into a MessageCatalogue. + * + * @param \DOMDocument $dom Source to extract messages and metadata + * @param MessageCatalogue $catalogue Catalogue where we'll collect messages and metadata + * @param string $domain The domain + */ + private function extractXliff1(\DOMDocument $dom, MessageCatalogue $catalogue, string $domain) + { + $xml = simplexml_import_dom($dom); + $encoding = strtoupper($dom->encoding); + + $xml->registerXPathNamespace('xliff', 'urn:oasis:names:tc:xliff:document:1.2'); + foreach ($xml->xpath('//xliff:trans-unit') as $translation) { + $attributes = $translation->attributes(); + + if (!(isset($attributes['resname']) || isset($translation->source))) { + continue; + } + + $source = isset($attributes['resname']) && $attributes['resname'] ? $attributes['resname'] : $translation->source; + // If the xlf file has another encoding specified, try to convert it because + // simple_xml will always return utf-8 encoded values + $target = $this->utf8ToCharset((string) (isset($translation->target) ? $translation->target : $source), $encoding); + + $catalogue->set((string) $source, $target, $domain); + + $metadata = array(); + if ($notes = $this->parseNotesMetadata($translation->note, $encoding)) { + $metadata['notes'] = $notes; + } + + if (isset($translation->target) && $translation->target->attributes()) { + $metadata['target-attributes'] = array(); + foreach ($translation->target->attributes() as $key => $value) { + $metadata['target-attributes'][$key] = (string) $value; + } + } + + if (isset($attributes['id'])) { + $metadata['id'] = (string) $attributes['id']; + } + + $catalogue->setMetadata((string) $source, $metadata, $domain); + } + } + + private function extractXliff2(\DOMDocument $dom, MessageCatalogue $catalogue, string $domain) + { + $xml = simplexml_import_dom($dom); + $encoding = strtoupper($dom->encoding); + + $xml->registerXPathNamespace('xliff', 'urn:oasis:names:tc:xliff:document:2.0'); + + foreach ($xml->xpath('//xliff:unit') as $unit) { + foreach ($unit->segment as $segment) { + $source = $segment->source; + + // If the xlf file has another encoding specified, try to convert it because + // simple_xml will always return utf-8 encoded values + $target = $this->utf8ToCharset((string) (isset($segment->target) ? $segment->target : $source), $encoding); + + $catalogue->set((string) $source, $target, $domain); + + $metadata = array(); + if (isset($segment->target) && $segment->target->attributes()) { + $metadata['target-attributes'] = array(); + foreach ($segment->target->attributes() as $key => $value) { + $metadata['target-attributes'][$key] = (string) $value; + } + } + + if (isset($unit->notes)) { + $metadata['notes'] = array(); + foreach ($unit->notes->note as $noteNode) { + $note = array(); + foreach ($noteNode->attributes() as $key => $value) { + $note[$key] = (string) $value; + } + $note['content'] = (string) $noteNode; + $metadata['notes'][] = $note; + } + } + + $catalogue->setMetadata((string) $source, $metadata, $domain); + } + } + } + + /** + * Convert a UTF8 string to the specified encoding. + */ + private function utf8ToCharset(string $content, string $encoding = null): string + { + if ('UTF-8' !== $encoding && !empty($encoding)) { + return mb_convert_encoding($content, $encoding, 'UTF-8'); + } + + return $content; + } + + /** + * Validates and parses the given file into a DOMDocument. + * + * @throws InvalidResourceException + */ + private function validateSchema(string $file, \DOMDocument $dom, string $schema) + { + $internalErrors = libxml_use_internal_errors(true); + + $disableEntities = libxml_disable_entity_loader(false); + + if (!@$dom->schemaValidateSource($schema)) { + libxml_disable_entity_loader($disableEntities); + + throw new InvalidResourceException(sprintf('Invalid resource provided: "%s"; Errors: %s', $file, implode("\n", $this->getXmlErrors($internalErrors)))); + } + + libxml_disable_entity_loader($disableEntities); + + $dom->normalizeDocument(); + + libxml_clear_errors(); + libxml_use_internal_errors($internalErrors); + } + + private function getSchema($xliffVersion) + { + if ('1.2' === $xliffVersion) { + $schemaSource = file_get_contents(__DIR__.'/schema/dic/xliff-core/xliff-core-1.2-strict.xsd'); + $xmlUri = 'http://www.w3.org/2001/xml.xsd'; + } elseif ('2.0' === $xliffVersion) { + $schemaSource = file_get_contents(__DIR__.'/schema/dic/xliff-core/xliff-core-2.0.xsd'); + $xmlUri = 'informativeCopiesOf3rdPartySchemas/w3c/xml.xsd'; + } else { + throw new InvalidArgumentException(sprintf('No support implemented for loading XLIFF version "%s".', $xliffVersion)); + } + + return $this->fixXmlLocation($schemaSource, $xmlUri); + } + + /** + * Internally changes the URI of a dependent xsd to be loaded locally. + */ + private function fixXmlLocation(string $schemaSource, string $xmlUri): string + { + $newPath = str_replace('\\', '/', __DIR__).'/schema/dic/xliff-core/xml.xsd'; + $parts = explode('/', $newPath); + $locationstart = 'file:///'; + if (0 === stripos($newPath, 'phar://')) { + $tmpfile = tempnam(sys_get_temp_dir(), 'symfony'); + if ($tmpfile) { + copy($newPath, $tmpfile); + $parts = explode('/', str_replace('\\', '/', $tmpfile)); + } else { + array_shift($parts); + $locationstart = 'phar:///'; + } + } + + $drive = '\\' === \DIRECTORY_SEPARATOR ? array_shift($parts).'/' : ''; + $newPath = $locationstart.$drive.implode('/', array_map('rawurlencode', $parts)); + + return str_replace($xmlUri, $newPath, $schemaSource); + } + + /** + * Returns the XML errors of the internal XML parser. + */ + private function getXmlErrors(bool $internalErrors): array + { + $errors = array(); + foreach (libxml_get_errors() as $error) { + $errors[] = sprintf('[%s %s] %s (in %s - line %d, column %d)', + LIBXML_ERR_WARNING == $error->level ? 'WARNING' : 'ERROR', + $error->code, + trim($error->message), + $error->file ?: 'n/a', + $error->line, + $error->column + ); + } + + libxml_clear_errors(); + libxml_use_internal_errors($internalErrors); + + return $errors; + } + + /** + * Gets xliff file version based on the root "version" attribute. + * Defaults to 1.2 for backwards compatibility. + * + * @throws InvalidArgumentException + */ + private function getVersionNumber(\DOMDocument $dom): string + { + /** @var \DOMNode $xliff */ + foreach ($dom->getElementsByTagName('xliff') as $xliff) { + $version = $xliff->attributes->getNamedItem('version'); + if ($version) { + return $version->nodeValue; + } + + $namespace = $xliff->attributes->getNamedItem('xmlns'); + if ($namespace) { + if (0 !== substr_compare('urn:oasis:names:tc:xliff:document:', $namespace->nodeValue, 0, 34)) { + throw new InvalidArgumentException(sprintf('Not a valid XLIFF namespace "%s"', $namespace)); + } + + return substr($namespace, 34); + } + } + + // Falls back to v1.2 + return '1.2'; + } + + private function parseNotesMetadata(\SimpleXMLElement $noteElement = null, string $encoding = null): array + { + $notes = array(); + + if (null === $noteElement) { + return $notes; + } + + /** @var \SimpleXMLElement $xmlNote */ + foreach ($noteElement as $xmlNote) { + $noteAttributes = $xmlNote->attributes(); + $note = array('content' => $this->utf8ToCharset((string) $xmlNote, $encoding)); + if (isset($noteAttributes['priority'])) { + $note['priority'] = (int) $noteAttributes['priority']; + } + + if (isset($noteAttributes['from'])) { + $note['from'] = (string) $noteAttributes['from']; + } + + $notes[] = $note; + } + + return $notes; + } +} diff --git a/vendor/symfony/translation/Loader/YamlFileLoader.php b/vendor/symfony/translation/Loader/YamlFileLoader.php new file mode 100644 index 000000000..584a055cd --- /dev/null +++ b/vendor/symfony/translation/Loader/YamlFileLoader.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\Translation\Loader; + +use Symfony\Component\Translation\Exception\InvalidResourceException; +use Symfony\Component\Translation\Exception\LogicException; +use Symfony\Component\Yaml\Exception\ParseException; +use Symfony\Component\Yaml\Parser as YamlParser; +use Symfony\Component\Yaml\Yaml; + +/** + * YamlFileLoader loads translations from Yaml files. + * + * @author Fabien Potencier + */ +class YamlFileLoader extends FileLoader +{ + private $yamlParser; + + /** + * {@inheritdoc} + */ + protected function loadResource($resource) + { + if (null === $this->yamlParser) { + if (!class_exists('Symfony\Component\Yaml\Parser')) { + throw new LogicException('Loading translations from the YAML format requires the Symfony Yaml component.'); + } + + $this->yamlParser = new YamlParser(); + } + + try { + $messages = $this->yamlParser->parseFile($resource, Yaml::PARSE_CONSTANT); + } catch (ParseException $e) { + throw new InvalidResourceException(sprintf('Error parsing YAML, invalid file "%s"', $resource), 0, $e); + } + + return $messages; + } +} diff --git a/vendor/symfony/translation/Loader/schema/dic/xliff-core/xliff-core-1.2-strict.xsd b/vendor/symfony/translation/Loader/schema/dic/xliff-core/xliff-core-1.2-strict.xsd new file mode 100644 index 000000000..3ce2a8e8a --- /dev/null +++ b/vendor/symfony/translation/Loader/schema/dic/xliff-core/xliff-core-1.2-strict.xsd @@ -0,0 +1,2223 @@ + + + + + + + + + + + + + + + Values for the attribute 'context-type'. + + + + + Indicates a database content. + + + + + Indicates the content of an element within an XML document. + + + + + Indicates the name of an element within an XML document. + + + + + Indicates the line number from the sourcefile (see context-type="sourcefile") where the <source> is found. + + + + + Indicates a the number of parameters contained within the <source>. + + + + + Indicates notes pertaining to the parameters in the <source>. + + + + + Indicates the content of a record within a database. + + + + + Indicates the name of a record within a database. + + + + + Indicates the original source file in the case that multiple files are merged to form the original file from which the XLIFF file is created. This differs from the original <file> attribute in that this sourcefile is one of many that make up that file. + + + + + + + Values for the attribute 'count-type'. + + + + + Indicates the count units are items that are used X times in a certain context; example: this is a reusable text unit which is used 42 times in other texts. + + + + + Indicates the count units are translation units existing already in the same document. + + + + + Indicates a total count. + + + + + + + Values for the attribute 'ctype' when used other elements than <ph> or <x>. + + + + + Indicates a run of bolded text. + + + + + Indicates a run of text in italics. + + + + + Indicates a run of underlined text. + + + + + Indicates a run of hyper-text. + + + + + + + Values for the attribute 'ctype' when used with <ph> or <x>. + + + + + Indicates a inline image. + + + + + Indicates a page break. + + + + + Indicates a line break. + + + + + + + + + + + + Values for the attribute 'datatype'. + + + + + Indicates Active Server Page data. + + + + + Indicates C source file data. + + + + + Indicates Channel Definition Format (CDF) data. + + + + + Indicates ColdFusion data. + + + + + Indicates C++ source file data. + + + + + Indicates C-Sharp data. + + + + + Indicates strings from C, ASM, and driver files data. + + + + + Indicates comma-separated values data. + + + + + Indicates database data. + + + + + Indicates portions of document that follows data and contains metadata. + + + + + Indicates portions of document that precedes data and contains metadata. + + + + + Indicates data from standard UI file operations dialogs (e.g., Open, Save, Save As, Export, Import). + + + + + Indicates standard user input screen data. + + + + + Indicates HyperText Markup Language (HTML) data - document instance. + + + + + Indicates content within an HTML document’s <body> element. + + + + + Indicates Windows INI file data. + + + + + Indicates Interleaf data. + + + + + Indicates Java source file data (extension '.java'). + + + + + Indicates Java property resource bundle data. + + + + + Indicates Java list resource bundle data. + + + + + Indicates JavaScript source file data. + + + + + Indicates JScript source file data. + + + + + Indicates information relating to formatting. + + + + + Indicates LISP source file data. + + + + + Indicates information relating to margin formats. + + + + + Indicates a file containing menu. + + + + + Indicates numerically identified string table. + + + + + Indicates Maker Interchange Format (MIF) data. + + + + + Indicates that the datatype attribute value is a MIME Type value and is defined in the mime-type attribute. + + + + + Indicates GNU Machine Object data. + + + + + Indicates Message Librarian strings created by Novell's Message Librarian Tool. + + + + + Indicates information to be displayed at the bottom of each page of a document. + + + + + Indicates information to be displayed at the top of each page of a document. + + + + + Indicates a list of property values (e.g., settings within INI files or preferences dialog). + + + + + Indicates Pascal source file data. + + + + + Indicates Hypertext Preprocessor data. + + + + + Indicates plain text file (no formatting other than, possibly, wrapping). + + + + + Indicates GNU Portable Object file. + + + + + Indicates dynamically generated user defined document. e.g. Oracle Report, Crystal Report, etc. + + + + + Indicates Windows .NET binary resources. + + + + + Indicates Windows .NET Resources. + + + + + Indicates Rich Text Format (RTF) data. + + + + + Indicates Standard Generalized Markup Language (SGML) data - document instance. + + + + + Indicates Standard Generalized Markup Language (SGML) data - Document Type Definition (DTD). + + + + + Indicates Scalable Vector Graphic (SVG) data. + + + + + Indicates VisualBasic Script source file. + + + + + Indicates warning message. + + + + + Indicates Windows (Win32) resources (i.e. resources extracted from an RC script, a message file, or a compiled file). + + + + + Indicates Extensible HyperText Markup Language (XHTML) data - document instance. + + + + + Indicates Extensible Markup Language (XML) data - document instance. + + + + + Indicates Extensible Markup Language (XML) data - Document Type Definition (DTD). + + + + + Indicates Extensible Stylesheet Language (XSL) data. + + + + + Indicates XUL elements. + + + + + + + Values for the attribute 'mtype'. + + + + + Indicates the marked text is an abbreviation. + + + + + ISO-12620 2.1.8: A term resulting from the omission of any part of the full term while designating the same concept. + + + + + ISO-12620 2.1.8.1: An abbreviated form of a simple term resulting from the omission of some of its letters (e.g. 'adj.' for 'adjective'). + + + + + ISO-12620 2.1.8.4: An abbreviated form of a term made up of letters from the full form of a multiword term strung together into a sequence pronounced only syllabically (e.g. 'radar' for 'radio detecting and ranging'). + + + + + ISO-12620: A proper-name term, such as the name of an agency or other proper entity. + + + + + ISO-12620 2.1.18.1: A recurrent word combination characterized by cohesion in that the components of the collocation must co-occur within an utterance or series of utterances, even though they do not necessarily have to maintain immediate proximity to one another. + + + + + ISO-12620 2.1.5: A synonym for an international scientific term that is used in general discourse in a given language. + + + + + Indicates the marked text is a date and/or time. + + + + + ISO-12620 2.1.15: An expression used to represent a concept based on a statement that two mathematical expressions are, for instance, equal as identified by the equal sign (=), or assigned to one another by a similar sign. + + + + + ISO-12620 2.1.7: The complete representation of a term for which there is an abbreviated form. + + + + + ISO-12620 2.1.14: Figures, symbols or the like used to express a concept briefly, such as a mathematical or chemical formula. + + + + + ISO-12620 2.1.1: The concept designation that has been chosen to head a terminological record. + + + + + ISO-12620 2.1.8.3: An abbreviated form of a term consisting of some of the initial letters of the words making up a multiword term or the term elements making up a compound term when these letters are pronounced individually (e.g. 'BSE' for 'bovine spongiform encephalopathy'). + + + + + ISO-12620 2.1.4: A term that is part of an international scientific nomenclature as adopted by an appropriate scientific body. + + + + + ISO-12620 2.1.6: A term that has the same or nearly identical orthographic or phonemic form in many languages. + + + + + ISO-12620 2.1.16: An expression used to represent a concept based on mathematical or logical relations, such as statements of inequality, set relationships, Boolean operations, and the like. + + + + + ISO-12620 2.1.17: A unit to track object. + + + + + Indicates the marked text is a name. + + + + + ISO-12620 2.1.3: A term that represents the same or a very similar concept as another term in the same language, but for which interchangeability is limited to some contexts and inapplicable in others. + + + + + ISO-12620 2.1.17.2: A unique alphanumeric designation assigned to an object in a manufacturing system. + + + + + Indicates the marked text is a phrase. + + + + + ISO-12620 2.1.18: Any group of two or more words that form a unit, the meaning of which frequently cannot be deduced based on the combined sense of the words making up the phrase. + + + + + Indicates the marked text should not be translated. + + + + + ISO-12620 2.1.12: A form of a term resulting from an operation whereby non-Latin writing systems are converted to the Latin alphabet. + + + + + Indicates that the marked text represents a segment. + + + + + ISO-12620 2.1.18.2: A fixed, lexicalized phrase. + + + + + ISO-12620 2.1.8.2: A variant of a multiword term that includes fewer words than the full form of the term (e.g. 'Group of Twenty-four' for 'Intergovernmental Group of Twenty-four on International Monetary Affairs'). + + + + + ISO-12620 2.1.17.1: Stock keeping unit, an inventory item identified by a unique alphanumeric designation assigned to an object in an inventory control system. + + + + + ISO-12620 2.1.19: A fixed chunk of recurring text. + + + + + ISO-12620 2.1.13: A designation of a concept by letters, numerals, pictograms or any combination thereof. + + + + + ISO-12620 2.1.2: Any term that represents the same or a very similar concept as the main entry term in a term entry. + + + + + ISO-12620 2.1.18.3: Phraseological unit in a language that expresses the same semantic content as another phrase in that same language. + + + + + Indicates the marked text is a term. + + + + + ISO-12620 2.1.11: A form of a term resulting from an operation whereby the characters of one writing system are represented by characters from another writing system, taking into account the pronunciation of the characters converted. + + + + + ISO-12620 2.1.10: A form of a term resulting from an operation whereby the characters of an alphabetic writing system are represented by characters from another alphabetic writing system. + + + + + ISO-12620 2.1.8.5: An abbreviated form of a term resulting from the omission of one or more term elements or syllables (e.g. 'flu' for 'influenza'). + + + + + ISO-12620 2.1.9: One of the alternate forms of a term. + + + + + + + Values for the attribute 'restype'. + + + + + Indicates a Windows RC AUTO3STATE control. + + + + + Indicates a Windows RC AUTOCHECKBOX control. + + + + + Indicates a Windows RC AUTORADIOBUTTON control. + + + + + Indicates a Windows RC BEDIT control. + + + + + Indicates a bitmap, for example a BITMAP resource in Windows. + + + + + Indicates a button object, for example a BUTTON control Windows. + + + + + Indicates a caption, such as the caption of a dialog box. + + + + + Indicates the cell in a table, for example the content of the <td> element in HTML. + + + + + Indicates check box object, for example a CHECKBOX control in Windows. + + + + + Indicates a menu item with an associated checkbox. + + + + + Indicates a list box, but with a check-box for each item. + + + + + Indicates a color selection dialog. + + + + + Indicates a combination of edit box and listbox object, for example a COMBOBOX control in Windows. + + + + + Indicates an initialization entry of an extended combobox DLGINIT resource block. (code 0x1234). + + + + + Indicates an initialization entry of a combobox DLGINIT resource block (code 0x0403). + + + + + Indicates a UI base class element that cannot be represented by any other element. + + + + + Indicates a context menu. + + + + + Indicates a Windows RC CTEXT control. + + + + + Indicates a cursor, for example a CURSOR resource in Windows. + + + + + Indicates a date/time picker. + + + + + Indicates a Windows RC DEFPUSHBUTTON control. + + + + + Indicates a dialog box. + + + + + Indicates a Windows RC DLGINIT resource block. + + + + + Indicates an edit box object, for example an EDIT control in Windows. + + + + + Indicates a filename. + + + + + Indicates a file dialog. + + + + + Indicates a footnote. + + + + + Indicates a font name. + + + + + Indicates a footer. + + + + + Indicates a frame object. + + + + + Indicates a XUL grid element. + + + + + Indicates a groupbox object, for example a GROUPBOX control in Windows. + + + + + Indicates a header item. + + + + + Indicates a heading, such has the content of <h1>, <h2>, etc. in HTML. + + + + + Indicates a Windows RC HEDIT control. + + + + + Indicates a horizontal scrollbar. + + + + + Indicates an icon, for example an ICON resource in Windows. + + + + + Indicates a Windows RC IEDIT control. + + + + + Indicates keyword list, such as the content of the Keywords meta-data in HTML, or a K footnote in WinHelp RTF. + + + + + Indicates a label object. + + + + + Indicates a label that is also a HTML link (not necessarily a URL). + + + + + Indicates a list (a group of list-items, for example an <ol> or <ul> element in HTML). + + + + + Indicates a listbox object, for example an LISTBOX control in Windows. + + + + + Indicates an list item (an entry in a list). + + + + + Indicates a Windows RC LTEXT control. + + + + + Indicates a menu (a group of menu-items). + + + + + Indicates a toolbar containing one or more tope level menus. + + + + + Indicates a menu item (an entry in a menu). + + + + + Indicates a XUL menuseparator element. + + + + + Indicates a message, for example an entry in a MESSAGETABLE resource in Windows. + + + + + Indicates a calendar control. + + + + + Indicates an edit box beside a spin control. + + + + + Indicates a catch all for rectangular areas. + + + + + Indicates a standalone menu not necessarily associated with a menubar. + + + + + Indicates a pushbox object, for example a PUSHBOX control in Windows. + + + + + Indicates a Windows RC PUSHBUTTON control. + + + + + Indicates a radio button object. + + + + + Indicates a menuitem with associated radio button. + + + + + Indicates raw data resources for an application. + + + + + Indicates a row in a table. + + + + + Indicates a Windows RC RTEXT control. + + + + + Indicates a user navigable container used to show a portion of a document. + + + + + Indicates a generic divider object (e.g. menu group separator). + + + + + Windows accelerators, shortcuts in resource or property files. + + + + + Indicates a UI control to indicate process activity but not progress. + + + + + Indicates a splitter bar. + + + + + Indicates a Windows RC STATE3 control. + + + + + Indicates a window for providing feedback to the users, like 'read-only', etc. + + + + + Indicates a string, for example an entry in a STRINGTABLE resource in Windows. + + + + + Indicates a layers of controls with a tab to select layers. + + + + + Indicates a display and edits regular two-dimensional tables of cells. + + + + + Indicates a XUL textbox element. + + + + + Indicates a UI button that can be toggled to on or off state. + + + + + Indicates an array of controls, usually buttons. + + + + + Indicates a pop up tool tip text. + + + + + Indicates a bar with a pointer indicating a position within a certain range. + + + + + Indicates a control that displays a set of hierarchical data. + + + + + Indicates a URI (URN or URL). + + + + + Indicates a Windows RC USERBUTTON control. + + + + + Indicates a user-defined control like CONTROL control in Windows. + + + + + Indicates the text of a variable. + + + + + Indicates version information about a resource like VERSIONINFO in Windows. + + + + + Indicates a vertical scrollbar. + + + + + Indicates a graphical window. + + + + + + + Values for the attribute 'size-unit'. + + + + + Indicates a size in 8-bit bytes. + + + + + Indicates a size in Unicode characters. + + + + + Indicates a size in columns. Used for HTML text area. + + + + + Indicates a size in centimeters. + + + + + Indicates a size in dialog units, as defined in Windows resources. + + + + + Indicates a size in 'font-size' units (as defined in CSS). + + + + + Indicates a size in 'x-height' units (as defined in CSS). + + + + + Indicates a size in glyphs. A glyph is considered to be one or more combined Unicode characters that represent a single displayable text character. Sometimes referred to as a 'grapheme cluster' + + + + + Indicates a size in inches. + + + + + Indicates a size in millimeters. + + + + + Indicates a size in percentage. + + + + + Indicates a size in pixels. + + + + + Indicates a size in point. + + + + + Indicates a size in rows. Used for HTML text area. + + + + + + + Values for the attribute 'state'. + + + + + Indicates the terminating state. + + + + + Indicates only non-textual information needs adaptation. + + + + + Indicates both text and non-textual information needs adaptation. + + + + + Indicates only non-textual information needs review. + + + + + Indicates both text and non-textual information needs review. + + + + + Indicates that only the text of the item needs to be reviewed. + + + + + Indicates that the item needs to be translated. + + + + + Indicates that the item is new. For example, translation units that were not in a previous version of the document. + + + + + Indicates that changes are reviewed and approved. + + + + + Indicates that the item has been translated. + + + + + + + Values for the attribute 'state-qualifier'. + + + + + Indicates an exact match. An exact match occurs when a source text of a segment is exactly the same as the source text of a segment that was translated previously. + + + + + Indicates a fuzzy match. A fuzzy match occurs when a source text of a segment is very similar to the source text of a segment that was translated previously (e.g. when the difference is casing, a few changed words, white-space discripancy, etc.). + + + + + Indicates a match based on matching IDs (in addition to matching text). + + + + + Indicates a translation derived from a glossary. + + + + + Indicates a translation derived from existing translation. + + + + + Indicates a translation derived from machine translation. + + + + + Indicates a translation derived from a translation repository. + + + + + Indicates a translation derived from a translation memory. + + + + + Indicates the translation is suggested by machine translation. + + + + + Indicates that the item has been rejected because of incorrect grammar. + + + + + Indicates that the item has been rejected because it is incorrect. + + + + + Indicates that the item has been rejected because it is too long or too short. + + + + + Indicates that the item has been rejected because of incorrect spelling. + + + + + Indicates the translation is suggested by translation memory. + + + + + + + Values for the attribute 'unit'. + + + + + Refers to words. + + + + + Refers to pages. + + + + + Refers to <trans-unit> elements. + + + + + Refers to <bin-unit> elements. + + + + + Refers to glyphs. + + + + + Refers to <trans-unit> and/or <bin-unit> elements. + + + + + Refers to the occurrences of instances defined by the count-type value. + + + + + Refers to characters. + + + + + Refers to lines. + + + + + Refers to sentences. + + + + + Refers to paragraphs. + + + + + Refers to segments. + + + + + Refers to placeables (inline elements). + + + + + + + Values for the attribute 'priority'. + + + + + Highest priority. + + + + + High priority. + + + + + High priority, but not as important as 2. + + + + + High priority, but not as important as 3. + + + + + Medium priority, but more important than 6. + + + + + Medium priority, but less important than 5. + + + + + Low priority, but more important than 8. + + + + + Low priority, but more important than 9. + + + + + Low priority. + + + + + Lowest priority. + + + + + + + + + This value indicates that all properties can be reformatted. This value must be used alone. + + + + + This value indicates that no properties should be reformatted. This value must be used alone. + + + + + + + + + + + + + This value indicates that all information in the coord attribute can be modified. + + + + + This value indicates that the x information in the coord attribute can be modified. + + + + + This value indicates that the y information in the coord attribute can be modified. + + + + + This value indicates that the cx information in the coord attribute can be modified. + + + + + This value indicates that the cy information in the coord attribute can be modified. + + + + + This value indicates that all the information in the font attribute can be modified. + + + + + This value indicates that the name information in the font attribute can be modified. + + + + + This value indicates that the size information in the font attribute can be modified. + + + + + This value indicates that the weight information in the font attribute can be modified. + + + + + This value indicates that the information in the css-style attribute can be modified. + + + + + This value indicates that the information in the style attribute can be modified. + + + + + This value indicates that the information in the exstyle attribute can be modified. + + + + + + + + + + + + + Indicates that the context is informational in nature, specifying for example, how a term should be translated. Thus, should be displayed to anyone editing the XLIFF document. + + + + + Indicates that the context-group is used to specify where the term was found in the translatable source. Thus, it is not displayed. + + + + + Indicates that the context information should be used during translation memory lookups. Thus, it is not displayed. + + + + + + + + + Represents a translation proposal from a translation memory or other resource. + + + + + Represents a previous version of the target element. + + + + + Represents a rejected version of the target element. + + + + + Represents a translation to be used for reference purposes only, for example from a related product or a different language. + + + + + Represents a proposed translation that was used for the translation of the trans-unit, possibly modified. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Values for the attribute 'coord'. + + + + + + + + Version values: 1.0 and 1.1 are allowed for backward compatibility. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/translation/Loader/schema/dic/xliff-core/xliff-core-2.0.xsd b/vendor/symfony/translation/Loader/schema/dic/xliff-core/xliff-core-2.0.xsd new file mode 100644 index 000000000..963232f97 --- /dev/null +++ b/vendor/symfony/translation/Loader/schema/dic/xliff-core/xliff-core-2.0.xsd @@ -0,0 +1,411 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/translation/Loader/schema/dic/xliff-core/xml.xsd b/vendor/symfony/translation/Loader/schema/dic/xliff-core/xml.xsd new file mode 100644 index 000000000..a46162a7a --- /dev/null +++ b/vendor/symfony/translation/Loader/schema/dic/xliff-core/xml.xsd @@ -0,0 +1,309 @@ + + + + + + +
+

About the XML namespace

+ +
+

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

+

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

+ +

+ Note that local names in this namespace are intended to be + defined only by the World Wide Web Consortium or its subgroups. + The names currently defined in this namespace are listed below. + They should not be used with conflicting semantics by any Working + Group, specification, or document instance. +

+

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

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

lang (as an attribute name)

+

+ + denotes an attribute whose value + is a language code for the natural language of the content of + any element; its value is inherited. This name is reserved + by virtue of its definition in the XML specification.

+ +
+
+

Notes

+

+ Attempting to install the relevant ISO 2- and 3-letter + codes as the enumerated possible values is probably never + going to be a realistic possibility. +

+

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

+

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

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

space (as an attribute name)

+

+ denotes an attribute whose + value is a keyword indicating what whitespace processing + discipline is intended for the content of the element; its + value is inherited. This name is reserved by virtue of its + definition in the XML specification.

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

base (as an attribute name)

+

+ denotes an attribute whose value + provides a URI to be used as the base for interpreting any + relative URIs in the scope of the element on which it + appears; its value is inherited. This name is reserved + by virtue of its definition in the XML Base specification.

+ +

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

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

id (as an attribute name)

+

+ + denotes an attribute whose value + should be interpreted as if declared to be of type ID. + This name is reserved by virtue of its definition in the + xml:id specification.

+ +

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

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

Father (in any context at all)

+ +
+

+ denotes Jon Bosak, the chair of + the original XML Working Group. This name is reserved by + the following decision of the W3C XML Plenary and + XML Coordination groups: +

+
+

+ + In appreciation for his vision, leadership and + dedication the W3C XML Plenary on this 10th day of + February, 2000, reserves for Jon Bosak in perpetuity + the XML name "xml:Father". +

+
+
+
+
+
+ + + + +
+

About this schema document

+ +
+

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

+ +

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

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

+ or +

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

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

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

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

+ +
+
+
+
+ + + +
+

Versioning policy for this schema document

+ +
+

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

+

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

+ +

+ The schema document at that URI may however change in the future, + in order to remain compatible with the latest version of XML + Schema itself, or with the XML namespace itself. In other words, + if the XML Schema or XML namespaces change, the version of this + document at + http://www.w3.org/2001/xml.xsd + + will change accordingly; the version at + + http://www.w3.org/2009/01/xml.xsd + + will not change. +

+

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

+ +
+
+
+
+ +
diff --git a/vendor/symfony/translation/LoggingTranslator.php b/vendor/symfony/translation/LoggingTranslator.php new file mode 100644 index 000000000..014567086 --- /dev/null +++ b/vendor/symfony/translation/LoggingTranslator.php @@ -0,0 +1,136 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation; + +use Psr\Log\LoggerInterface; +use Symfony\Component\Translation\Exception\InvalidArgumentException; + +/** + * @author Abdellatif Ait boudad + */ +class LoggingTranslator implements TranslatorInterface, TranslatorBagInterface +{ + /** + * @var TranslatorInterface|TranslatorBagInterface + */ + private $translator; + + private $logger; + + /** + * @param TranslatorInterface $translator The translator must implement TranslatorBagInterface + * @param LoggerInterface $logger + */ + public function __construct(TranslatorInterface $translator, LoggerInterface $logger) + { + if (!$translator instanceof TranslatorBagInterface) { + throw new InvalidArgumentException(sprintf('The Translator "%s" must implement TranslatorInterface and TranslatorBagInterface.', \get_class($translator))); + } + + $this->translator = $translator; + $this->logger = $logger; + } + + /** + * {@inheritdoc} + */ + public function trans($id, array $parameters = array(), $domain = null, $locale = null) + { + $trans = $this->translator->trans($id, $parameters, $domain, $locale); + $this->log($id, $domain, $locale); + + return $trans; + } + + /** + * {@inheritdoc} + */ + public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null) + { + $trans = $this->translator->transChoice($id, $number, $parameters, $domain, $locale); + $this->log($id, $domain, $locale); + + return $trans; + } + + /** + * {@inheritdoc} + */ + public function setLocale($locale) + { + $this->translator->setLocale($locale); + } + + /** + * {@inheritdoc} + */ + public function getLocale() + { + return $this->translator->getLocale(); + } + + /** + * {@inheritdoc} + */ + public function getCatalogue($locale = null) + { + return $this->translator->getCatalogue($locale); + } + + /** + * Gets the fallback locales. + * + * @return array $locales The fallback locales + */ + public function getFallbackLocales() + { + if ($this->translator instanceof Translator || method_exists($this->translator, 'getFallbackLocales')) { + return $this->translator->getFallbackLocales(); + } + + return array(); + } + + /** + * Passes through all unknown calls onto the translator object. + */ + public function __call($method, $args) + { + return \call_user_func_array(array($this->translator, $method), $args); + } + + /** + * Logs for missing translations. + * + * @param string $id + * @param string|null $domain + * @param string|null $locale + */ + private function log($id, $domain, $locale) + { + if (null === $domain) { + $domain = 'messages'; + } + + $id = (string) $id; + $catalogue = $this->translator->getCatalogue($locale); + if ($catalogue->defines($id, $domain)) { + return; + } + + if ($catalogue->has($id, $domain)) { + $this->logger->debug('Translation use fallback catalogue.', array('id' => $id, 'domain' => $domain, 'locale' => $catalogue->getLocale())); + } else { + $this->logger->warning('Translation not found.', array('id' => $id, 'domain' => $domain, 'locale' => $catalogue->getLocale())); + } + } +} diff --git a/vendor/symfony/translation/MessageCatalogue.php b/vendor/symfony/translation/MessageCatalogue.php new file mode 100644 index 000000000..c36ea30ec --- /dev/null +++ b/vendor/symfony/translation/MessageCatalogue.php @@ -0,0 +1,271 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation; + +use Symfony\Component\Config\Resource\ResourceInterface; +use Symfony\Component\Translation\Exception\LogicException; + +/** + * @author Fabien Potencier + */ +class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterface +{ + private $messages = array(); + private $metadata = array(); + private $resources = array(); + private $locale; + private $fallbackCatalogue; + private $parent; + + /** + * @param string $locale The locale + * @param array $messages An array of messages classified by domain + */ + public function __construct(?string $locale, array $messages = array()) + { + $this->locale = $locale; + $this->messages = $messages; + } + + /** + * {@inheritdoc} + */ + public function getLocale() + { + return $this->locale; + } + + /** + * {@inheritdoc} + */ + public function getDomains() + { + return array_keys($this->messages); + } + + /** + * {@inheritdoc} + */ + public function all($domain = null) + { + if (null === $domain) { + return $this->messages; + } + + return isset($this->messages[$domain]) ? $this->messages[$domain] : array(); + } + + /** + * {@inheritdoc} + */ + public function set($id, $translation, $domain = 'messages') + { + $this->add(array($id => $translation), $domain); + } + + /** + * {@inheritdoc} + */ + public function has($id, $domain = 'messages') + { + if (isset($this->messages[$domain][$id])) { + return true; + } + + if (null !== $this->fallbackCatalogue) { + return $this->fallbackCatalogue->has($id, $domain); + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function defines($id, $domain = 'messages') + { + return isset($this->messages[$domain][$id]); + } + + /** + * {@inheritdoc} + */ + public function get($id, $domain = 'messages') + { + if (isset($this->messages[$domain][$id])) { + return $this->messages[$domain][$id]; + } + + if (null !== $this->fallbackCatalogue) { + return $this->fallbackCatalogue->get($id, $domain); + } + + return $id; + } + + /** + * {@inheritdoc} + */ + public function replace($messages, $domain = 'messages') + { + $this->messages[$domain] = array(); + + $this->add($messages, $domain); + } + + /** + * {@inheritdoc} + */ + public function add($messages, $domain = 'messages') + { + if (!isset($this->messages[$domain])) { + $this->messages[$domain] = $messages; + } else { + $this->messages[$domain] = array_replace($this->messages[$domain], $messages); + } + } + + /** + * {@inheritdoc} + */ + public function addCatalogue(MessageCatalogueInterface $catalogue) + { + if ($catalogue->getLocale() !== $this->locale) { + throw new LogicException(sprintf('Cannot add a catalogue for locale "%s" as the current locale for this catalogue is "%s"', $catalogue->getLocale(), $this->locale)); + } + + foreach ($catalogue->all() as $domain => $messages) { + $this->add($messages, $domain); + } + + foreach ($catalogue->getResources() as $resource) { + $this->addResource($resource); + } + + if ($catalogue instanceof MetadataAwareInterface) { + $metadata = $catalogue->getMetadata('', ''); + $this->addMetadata($metadata); + } + } + + /** + * {@inheritdoc} + */ + public function addFallbackCatalogue(MessageCatalogueInterface $catalogue) + { + // detect circular references + $c = $catalogue; + while ($c = $c->getFallbackCatalogue()) { + if ($c->getLocale() === $this->getLocale()) { + throw new LogicException(sprintf('Circular reference detected when adding a fallback catalogue for locale "%s".', $catalogue->getLocale())); + } + } + + $c = $this; + do { + if ($c->getLocale() === $catalogue->getLocale()) { + throw new LogicException(sprintf('Circular reference detected when adding a fallback catalogue for locale "%s".', $catalogue->getLocale())); + } + + foreach ($catalogue->getResources() as $resource) { + $c->addResource($resource); + } + } while ($c = $c->parent); + + $catalogue->parent = $this; + $this->fallbackCatalogue = $catalogue; + + foreach ($catalogue->getResources() as $resource) { + $this->addResource($resource); + } + } + + /** + * {@inheritdoc} + */ + public function getFallbackCatalogue() + { + return $this->fallbackCatalogue; + } + + /** + * {@inheritdoc} + */ + public function getResources() + { + return array_values($this->resources); + } + + /** + * {@inheritdoc} + */ + public function addResource(ResourceInterface $resource) + { + $this->resources[$resource->__toString()] = $resource; + } + + /** + * {@inheritdoc} + */ + public function getMetadata($key = '', $domain = 'messages') + { + if ('' == $domain) { + return $this->metadata; + } + + if (isset($this->metadata[$domain])) { + if ('' == $key) { + return $this->metadata[$domain]; + } + + if (isset($this->metadata[$domain][$key])) { + return $this->metadata[$domain][$key]; + } + } + } + + /** + * {@inheritdoc} + */ + public function setMetadata($key, $value, $domain = 'messages') + { + $this->metadata[$domain][$key] = $value; + } + + /** + * {@inheritdoc} + */ + public function deleteMetadata($key = '', $domain = 'messages') + { + if ('' == $domain) { + $this->metadata = array(); + } elseif ('' == $key) { + unset($this->metadata[$domain]); + } else { + unset($this->metadata[$domain][$key]); + } + } + + /** + * Adds current values with the new values. + * + * @param array $values Values to add + */ + private function addMetadata(array $values) + { + foreach ($values as $domain => $keys) { + foreach ($keys as $key => $value) { + $this->setMetadata($key, $value, $domain); + } + } + } +} diff --git a/vendor/symfony/translation/MessageCatalogueInterface.php b/vendor/symfony/translation/MessageCatalogueInterface.php new file mode 100644 index 000000000..e0dbb2bd9 --- /dev/null +++ b/vendor/symfony/translation/MessageCatalogueInterface.php @@ -0,0 +1,136 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation; + +use Symfony\Component\Config\Resource\ResourceInterface; + +/** + * MessageCatalogueInterface. + * + * @author Fabien Potencier + */ +interface MessageCatalogueInterface +{ + /** + * Gets the catalogue locale. + * + * @return string The locale + */ + public function getLocale(); + + /** + * Gets the domains. + * + * @return array An array of domains + */ + public function getDomains(); + + /** + * Gets the messages within a given domain. + * + * If $domain is null, it returns all messages. + * + * @param string $domain The domain name + * + * @return array An array of messages + */ + public function all($domain = null); + + /** + * Sets a message translation. + * + * @param string $id The message id + * @param string $translation The messages translation + * @param string $domain The domain name + */ + public function set($id, $translation, $domain = 'messages'); + + /** + * Checks if a message has a translation. + * + * @param string $id The message id + * @param string $domain The domain name + * + * @return bool true if the message has a translation, false otherwise + */ + public function has($id, $domain = 'messages'); + + /** + * Checks if a message has a translation (it does not take into account the fallback mechanism). + * + * @param string $id The message id + * @param string $domain The domain name + * + * @return bool true if the message has a translation, false otherwise + */ + public function defines($id, $domain = 'messages'); + + /** + * Gets a message translation. + * + * @param string $id The message id + * @param string $domain The domain name + * + * @return string The message translation + */ + public function get($id, $domain = 'messages'); + + /** + * Sets translations for a given domain. + * + * @param array $messages An array of translations + * @param string $domain The domain name + */ + public function replace($messages, $domain = 'messages'); + + /** + * Adds translations for a given domain. + * + * @param array $messages An array of translations + * @param string $domain The domain name + */ + public function add($messages, $domain = 'messages'); + + /** + * Merges translations from the given Catalogue into the current one. + * + * The two catalogues must have the same locale. + */ + public function addCatalogue(self $catalogue); + + /** + * Merges translations from the given Catalogue into the current one + * only when the translation does not exist. + * + * This is used to provide default translations when they do not exist for the current locale. + */ + public function addFallbackCatalogue(self $catalogue); + + /** + * Gets the fallback catalogue. + * + * @return self|null A MessageCatalogueInterface instance or null when no fallback has been set + */ + public function getFallbackCatalogue(); + + /** + * Returns an array of resources loaded to build this collection. + * + * @return ResourceInterface[] An array of resources + */ + public function getResources(); + + /** + * Adds a resource for this collection. + */ + public function addResource(ResourceInterface $resource); +} diff --git a/vendor/symfony/translation/MessageSelector.php b/vendor/symfony/translation/MessageSelector.php new file mode 100644 index 000000000..5de171c5f --- /dev/null +++ b/vendor/symfony/translation/MessageSelector.php @@ -0,0 +1,94 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation; + +use Symfony\Component\Translation\Exception\InvalidArgumentException; + +/** + * MessageSelector. + * + * @author Fabien Potencier + * @author Bernhard Schussek + */ +class MessageSelector +{ + /** + * Given a message with different plural translations separated by a + * pipe (|), this method returns the correct portion of the message based + * on the given number, locale and the pluralization rules in the message + * itself. + * + * The message supports two different types of pluralization rules: + * + * interval: {0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples + * indexed: There is one apple|There are %count% apples + * + * The indexed solution can also contain labels (e.g. one: There is one apple). + * This is purely for making the translations more clear - it does not + * affect the functionality. + * + * The two methods can also be mixed: + * {0} There are no apples|one: There is one apple|more: There are %count% apples + * + * @param string $message The message being translated + * @param int $number The number of items represented for the message + * @param string $locale The locale to use for choosing + * + * @return string + * + * @throws InvalidArgumentException + */ + public function choose($message, $number, $locale) + { + $parts = array(); + if (preg_match('/^\|++$/', $message)) { + $parts = explode('|', $message); + } elseif (preg_match_all('/(?:\|\||[^\|])++/', $message, $matches)) { + $parts = $matches[0]; + } + + $explicitRules = array(); + $standardRules = array(); + foreach ($parts as $part) { + $part = trim(str_replace('||', '|', $part)); + + if (preg_match('/^(?P'.Interval::getIntervalRegexp().')\s*(?P.*?)$/xs', $part, $matches)) { + $explicitRules[$matches['interval']] = $matches['message']; + } elseif (preg_match('/^\w+\:\s*(.*?)$/', $part, $matches)) { + $standardRules[] = $matches[1]; + } else { + $standardRules[] = $part; + } + } + + // try to match an explicit rule, then fallback to the standard ones + foreach ($explicitRules as $interval => $m) { + if (Interval::test($number, $interval)) { + return $m; + } + } + + $position = PluralizationRules::get($number, $locale); + + if (!isset($standardRules[$position])) { + // when there's exactly one rule given, and that rule is a standard + // rule, use this rule + if (1 === \count($parts) && isset($standardRules[0])) { + return $standardRules[0]; + } + + throw new InvalidArgumentException(sprintf('Unable to choose a translation for "%s" with locale "%s" for value "%d". Double check that this translation has the correct plural options (e.g. "There is one apple|There are %%count%% apples").', $message, $locale, $number)); + } + + return $standardRules[$position]; + } +} diff --git a/vendor/symfony/translation/MetadataAwareInterface.php b/vendor/symfony/translation/MetadataAwareInterface.php new file mode 100644 index 000000000..e93c6fbc7 --- /dev/null +++ b/vendor/symfony/translation/MetadataAwareInterface.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation; + +/** + * MetadataAwareInterface. + * + * @author Fabien Potencier + */ +interface MetadataAwareInterface +{ + /** + * Gets metadata for the given domain and key. + * + * Passing an empty domain will return an array with all metadata indexed by + * domain and then by key. Passing an empty key will return an array with all + * metadata for the given domain. + * + * @param string $key The key + * @param string $domain The domain name + * + * @return mixed The value that was set or an array with the domains/keys or null + */ + public function getMetadata($key = '', $domain = 'messages'); + + /** + * Adds metadata to a message domain. + * + * @param string $key The key + * @param mixed $value The value + * @param string $domain The domain name + */ + public function setMetadata($key, $value, $domain = 'messages'); + + /** + * Deletes metadata for the given key and domain. + * + * Passing an empty domain will delete all metadata. Passing an empty key will + * delete all metadata for the given domain. + * + * @param string $key The key + * @param string $domain The domain name + */ + public function deleteMetadata($key = '', $domain = 'messages'); +} diff --git a/vendor/symfony/translation/PluralizationRules.php b/vendor/symfony/translation/PluralizationRules.php new file mode 100644 index 000000000..3aca0ba96 --- /dev/null +++ b/vendor/symfony/translation/PluralizationRules.php @@ -0,0 +1,210 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation; + +/** + * Returns the plural rules for a given locale. + * + * @author Fabien Potencier + */ +class PluralizationRules +{ + private static $rules = array(); + + /** + * Returns the plural position to use for the given locale and number. + * + * @param int $number The number + * @param string $locale The locale + * + * @return int The plural position + */ + public static function get($number, $locale) + { + if ('pt_BR' === $locale) { + // temporary set a locale for brazilian + $locale = 'xbr'; + } + + if (\strlen($locale) > 3) { + $locale = substr($locale, 0, -\strlen(strrchr($locale, '_'))); + } + + if (isset(self::$rules[$locale])) { + $return = \call_user_func(self::$rules[$locale], $number); + + if (!\is_int($return) || $return < 0) { + return 0; + } + + return $return; + } + + /* + * The plural rules are derived from code of the Zend Framework (2010-09-25), + * which is subject to the new BSD license (http://framework.zend.com/license/new-bsd). + * Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) + */ + switch ($locale) { + case 'az': + case 'bo': + case 'dz': + case 'id': + case 'ja': + case 'jv': + case 'ka': + case 'km': + case 'kn': + case 'ko': + case 'ms': + case 'th': + case 'tr': + case 'vi': + case 'zh': + return 0; + + case 'af': + case 'bn': + case 'bg': + case 'ca': + case 'da': + case 'de': + case 'el': + case 'en': + case 'eo': + case 'es': + case 'et': + case 'eu': + case 'fa': + case 'fi': + case 'fo': + case 'fur': + case 'fy': + case 'gl': + case 'gu': + case 'ha': + case 'he': + case 'hu': + case 'is': + case 'it': + case 'ku': + case 'lb': + case 'ml': + case 'mn': + case 'mr': + case 'nah': + case 'nb': + case 'ne': + case 'nl': + case 'nn': + case 'no': + case 'oc': + case 'om': + case 'or': + case 'pa': + case 'pap': + case 'ps': + case 'pt': + case 'so': + case 'sq': + case 'sv': + case 'sw': + case 'ta': + case 'te': + case 'tk': + case 'ur': + case 'zu': + return (1 == $number) ? 0 : 1; + + case 'am': + case 'bh': + case 'fil': + case 'fr': + case 'gun': + case 'hi': + case 'hy': + case 'ln': + case 'mg': + case 'nso': + case 'xbr': + case 'ti': + case 'wa': + return ((0 == $number) || (1 == $number)) ? 0 : 1; + + case 'be': + case 'bs': + case 'hr': + case 'ru': + case 'sh': + case 'sr': + case 'uk': + return ((1 == $number % 10) && (11 != $number % 100)) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2); + + case 'cs': + case 'sk': + return (1 == $number) ? 0 : ((($number >= 2) && ($number <= 4)) ? 1 : 2); + + case 'ga': + return (1 == $number) ? 0 : ((2 == $number) ? 1 : 2); + + case 'lt': + return ((1 == $number % 10) && (11 != $number % 100)) ? 0 : ((($number % 10 >= 2) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2); + + case 'sl': + return (1 == $number % 100) ? 0 : ((2 == $number % 100) ? 1 : (((3 == $number % 100) || (4 == $number % 100)) ? 2 : 3)); + + case 'mk': + return (1 == $number % 10) ? 0 : 1; + + case 'mt': + return (1 == $number) ? 0 : (((0 == $number) || (($number % 100 > 1) && ($number % 100 < 11))) ? 1 : ((($number % 100 > 10) && ($number % 100 < 20)) ? 2 : 3)); + + case 'lv': + return (0 == $number) ? 0 : (((1 == $number % 10) && (11 != $number % 100)) ? 1 : 2); + + case 'pl': + return (1 == $number) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 12) || ($number % 100 > 14))) ? 1 : 2); + + case 'cy': + return (1 == $number) ? 0 : ((2 == $number) ? 1 : (((8 == $number) || (11 == $number)) ? 2 : 3)); + + case 'ro': + return (1 == $number) ? 0 : (((0 == $number) || (($number % 100 > 0) && ($number % 100 < 20))) ? 1 : 2); + + case 'ar': + return (0 == $number) ? 0 : ((1 == $number) ? 1 : ((2 == $number) ? 2 : ((($number % 100 >= 3) && ($number % 100 <= 10)) ? 3 : ((($number % 100 >= 11) && ($number % 100 <= 99)) ? 4 : 5)))); + + default: + return 0; + } + } + + /** + * Overrides the default plural rule for a given locale. + * + * @param callable $rule A PHP callable + * @param string $locale The locale + */ + public static function set(callable $rule, $locale) + { + if ('pt_BR' === $locale) { + // temporary set a locale for brazilian + $locale = 'xbr'; + } + + if (\strlen($locale) > 3) { + $locale = substr($locale, 0, -\strlen(strrchr($locale, '_'))); + } + + self::$rules[$locale] = $rule; + } +} diff --git a/vendor/symfony/translation/README.md b/vendor/symfony/translation/README.md new file mode 100644 index 000000000..46f3d1f2f --- /dev/null +++ b/vendor/symfony/translation/README.md @@ -0,0 +1,13 @@ +Translation Component +===================== + +The Translation component provides tools to internationalize your application. + +Resources +--------- + + * [Documentation](https://symfony.com/doc/current/components/translation/index.html) + * [Contributing](https://symfony.com/doc/current/contributing/index.html) + * [Report issues](https://github.com/symfony/symfony/issues) and + [send Pull Requests](https://github.com/symfony/symfony/pulls) + in the [main Symfony repository](https://github.com/symfony/symfony) diff --git a/vendor/symfony/translation/Reader/TranslationReader.php b/vendor/symfony/translation/Reader/TranslationReader.php new file mode 100644 index 000000000..948edec49 --- /dev/null +++ b/vendor/symfony/translation/Reader/TranslationReader.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Reader; + +use Symfony\Component\Finder\Finder; +use Symfony\Component\Translation\Loader\LoaderInterface; +use Symfony\Component\Translation\MessageCatalogue; + +/** + * TranslationReader reads translation messages from translation files. + * + * @author Michel Salib + */ +class TranslationReader implements TranslationReaderInterface +{ + /** + * Loaders used for import. + * + * @var array + */ + private $loaders = array(); + + /** + * Adds a loader to the translation extractor. + * + * @param string $format The format of the loader + * @param LoaderInterface $loader + */ + public function addLoader($format, LoaderInterface $loader) + { + $this->loaders[$format] = $loader; + } + + /** + * {@inheritdoc} + */ + public function read($directory, MessageCatalogue $catalogue) + { + if (!is_dir($directory)) { + return; + } + + foreach ($this->loaders as $format => $loader) { + // load any existing translation files + $finder = new Finder(); + $extension = $catalogue->getLocale().'.'.$format; + $files = $finder->files()->name('*.'.$extension)->in($directory); + foreach ($files as $file) { + $domain = substr($file->getFilename(), 0, -1 * \strlen($extension) - 1); + $catalogue->addCatalogue($loader->load($file->getPathname(), $catalogue->getLocale(), $domain)); + } + } + } +} diff --git a/vendor/symfony/translation/Reader/TranslationReaderInterface.php b/vendor/symfony/translation/Reader/TranslationReaderInterface.php new file mode 100644 index 000000000..0aa55c6d3 --- /dev/null +++ b/vendor/symfony/translation/Reader/TranslationReaderInterface.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Reader; + +use Symfony\Component\Translation\MessageCatalogue; + +/** + * TranslationReader reads translation messages from translation files. + * + * @author Tobias Nyholm + */ +interface TranslationReaderInterface +{ + /** + * Reads translation messages from a directory to the catalogue. + * + * @param string $directory + * @param MessageCatalogue $catalogue + */ + public function read($directory, MessageCatalogue $catalogue); +} diff --git a/vendor/symfony/translation/Resources/schemas/xliff-core-1.2-strict.xsd b/vendor/symfony/translation/Resources/schemas/xliff-core-1.2-strict.xsd new file mode 100644 index 000000000..4102d6472 --- /dev/null +++ b/vendor/symfony/translation/Resources/schemas/xliff-core-1.2-strict.xsd @@ -0,0 +1,2223 @@ + + + + + + + + + + + + + + + Values for the attribute 'context-type'. + + + + + Indicates a database content. + + + + + Indicates the content of an element within an XML document. + + + + + Indicates the name of an element within an XML document. + + + + + Indicates the line number from the sourcefile (see context-type="sourcefile") where the <source> is found. + + + + + Indicates a the number of parameters contained within the <source>. + + + + + Indicates notes pertaining to the parameters in the <source>. + + + + + Indicates the content of a record within a database. + + + + + Indicates the name of a record within a database. + + + + + Indicates the original source file in the case that multiple files are merged to form the original file from which the XLIFF file is created. This differs from the original <file> attribute in that this sourcefile is one of many that make up that file. + + + + + + + Values for the attribute 'count-type'. + + + + + Indicates the count units are items that are used X times in a certain context; example: this is a reusable text unit which is used 42 times in other texts. + + + + + Indicates the count units are translation units existing already in the same document. + + + + + Indicates a total count. + + + + + + + Values for the attribute 'ctype' when used other elements than <ph> or <x>. + + + + + Indicates a run of bolded text. + + + + + Indicates a run of text in italics. + + + + + Indicates a run of underlined text. + + + + + Indicates a run of hyper-text. + + + + + + + Values for the attribute 'ctype' when used with <ph> or <x>. + + + + + Indicates a inline image. + + + + + Indicates a page break. + + + + + Indicates a line break. + + + + + + + + + + + + Values for the attribute 'datatype'. + + + + + Indicates Active Server Page data. + + + + + Indicates C source file data. + + + + + Indicates Channel Definition Format (CDF) data. + + + + + Indicates ColdFusion data. + + + + + Indicates C++ source file data. + + + + + Indicates C-Sharp data. + + + + + Indicates strings from C, ASM, and driver files data. + + + + + Indicates comma-separated values data. + + + + + Indicates database data. + + + + + Indicates portions of document that follows data and contains metadata. + + + + + Indicates portions of document that precedes data and contains metadata. + + + + + Indicates data from standard UI file operations dialogs (e.g., Open, Save, Save As, Export, Import). + + + + + Indicates standard user input screen data. + + + + + Indicates HyperText Markup Language (HTML) data - document instance. + + + + + Indicates content within an HTML document’s <body> element. + + + + + Indicates Windows INI file data. + + + + + Indicates Interleaf data. + + + + + Indicates Java source file data (extension '.java'). + + + + + Indicates Java property resource bundle data. + + + + + Indicates Java list resource bundle data. + + + + + Indicates JavaScript source file data. + + + + + Indicates JScript source file data. + + + + + Indicates information relating to formatting. + + + + + Indicates LISP source file data. + + + + + Indicates information relating to margin formats. + + + + + Indicates a file containing menu. + + + + + Indicates numerically identified string table. + + + + + Indicates Maker Interchange Format (MIF) data. + + + + + Indicates that the datatype attribute value is a MIME Type value and is defined in the mime-type attribute. + + + + + Indicates GNU Machine Object data. + + + + + Indicates Message Librarian strings created by Novell's Message Librarian Tool. + + + + + Indicates information to be displayed at the bottom of each page of a document. + + + + + Indicates information to be displayed at the top of each page of a document. + + + + + Indicates a list of property values (e.g., settings within INI files or preferences dialog). + + + + + Indicates Pascal source file data. + + + + + Indicates Hypertext Preprocessor data. + + + + + Indicates plain text file (no formatting other than, possibly, wrapping). + + + + + Indicates GNU Portable Object file. + + + + + Indicates dynamically generated user defined document. e.g. Oracle Report, Crystal Report, etc. + + + + + Indicates Windows .NET binary resources. + + + + + Indicates Windows .NET Resources. + + + + + Indicates Rich Text Format (RTF) data. + + + + + Indicates Standard Generalized Markup Language (SGML) data - document instance. + + + + + Indicates Standard Generalized Markup Language (SGML) data - Document Type Definition (DTD). + + + + + Indicates Scalable Vector Graphic (SVG) data. + + + + + Indicates VisualBasic Script source file. + + + + + Indicates warning message. + + + + + Indicates Windows (Win32) resources (i.e. resources extracted from an RC script, a message file, or a compiled file). + + + + + Indicates Extensible HyperText Markup Language (XHTML) data - document instance. + + + + + Indicates Extensible Markup Language (XML) data - document instance. + + + + + Indicates Extensible Markup Language (XML) data - Document Type Definition (DTD). + + + + + Indicates Extensible Stylesheet Language (XSL) data. + + + + + Indicates XUL elements. + + + + + + + Values for the attribute 'mtype'. + + + + + Indicates the marked text is an abbreviation. + + + + + ISO-12620 2.1.8: A term resulting from the omission of any part of the full term while designating the same concept. + + + + + ISO-12620 2.1.8.1: An abbreviated form of a simple term resulting from the omission of some of its letters (e.g. 'adj.' for 'adjective'). + + + + + ISO-12620 2.1.8.4: An abbreviated form of a term made up of letters from the full form of a multiword term strung together into a sequence pronounced only syllabically (e.g. 'radar' for 'radio detecting and ranging'). + + + + + ISO-12620: A proper-name term, such as the name of an agency or other proper entity. + + + + + ISO-12620 2.1.18.1: A recurrent word combination characterized by cohesion in that the components of the collocation must co-occur within an utterance or series of utterances, even though they do not necessarily have to maintain immediate proximity to one another. + + + + + ISO-12620 2.1.5: A synonym for an international scientific term that is used in general discourse in a given language. + + + + + Indicates the marked text is a date and/or time. + + + + + ISO-12620 2.1.15: An expression used to represent a concept based on a statement that two mathematical expressions are, for instance, equal as identified by the equal sign (=), or assigned to one another by a similar sign. + + + + + ISO-12620 2.1.7: The complete representation of a term for which there is an abbreviated form. + + + + + ISO-12620 2.1.14: Figures, symbols or the like used to express a concept briefly, such as a mathematical or chemical formula. + + + + + ISO-12620 2.1.1: The concept designation that has been chosen to head a terminological record. + + + + + ISO-12620 2.1.8.3: An abbreviated form of a term consisting of some of the initial letters of the words making up a multiword term or the term elements making up a compound term when these letters are pronounced individually (e.g. 'BSE' for 'bovine spongiform encephalopathy'). + + + + + ISO-12620 2.1.4: A term that is part of an international scientific nomenclature as adopted by an appropriate scientific body. + + + + + ISO-12620 2.1.6: A term that has the same or nearly identical orthographic or phonemic form in many languages. + + + + + ISO-12620 2.1.16: An expression used to represent a concept based on mathematical or logical relations, such as statements of inequality, set relationships, Boolean operations, and the like. + + + + + ISO-12620 2.1.17: A unit to track object. + + + + + Indicates the marked text is a name. + + + + + ISO-12620 2.1.3: A term that represents the same or a very similar concept as another term in the same language, but for which interchangeability is limited to some contexts and inapplicable in others. + + + + + ISO-12620 2.1.17.2: A unique alphanumeric designation assigned to an object in a manufacturing system. + + + + + Indicates the marked text is a phrase. + + + + + ISO-12620 2.1.18: Any group of two or more words that form a unit, the meaning of which frequently cannot be deduced based on the combined sense of the words making up the phrase. + + + + + Indicates the marked text should not be translated. + + + + + ISO-12620 2.1.12: A form of a term resulting from an operation whereby non-Latin writing systems are converted to the Latin alphabet. + + + + + Indicates that the marked text represents a segment. + + + + + ISO-12620 2.1.18.2: A fixed, lexicalized phrase. + + + + + ISO-12620 2.1.8.2: A variant of a multiword term that includes fewer words than the full form of the term (e.g. 'Group of Twenty-four' for 'Intergovernmental Group of Twenty-four on International Monetary Affairs'). + + + + + ISO-12620 2.1.17.1: Stock keeping unit, an inventory item identified by a unique alphanumeric designation assigned to an object in an inventory control system. + + + + + ISO-12620 2.1.19: A fixed chunk of recurring text. + + + + + ISO-12620 2.1.13: A designation of a concept by letters, numerals, pictograms or any combination thereof. + + + + + ISO-12620 2.1.2: Any term that represents the same or a very similar concept as the main entry term in a term entry. + + + + + ISO-12620 2.1.18.3: Phraseological unit in a language that expresses the same semantic content as another phrase in that same language. + + + + + Indicates the marked text is a term. + + + + + ISO-12620 2.1.11: A form of a term resulting from an operation whereby the characters of one writing system are represented by characters from another writing system, taking into account the pronunciation of the characters converted. + + + + + ISO-12620 2.1.10: A form of a term resulting from an operation whereby the characters of an alphabetic writing system are represented by characters from another alphabetic writing system. + + + + + ISO-12620 2.1.8.5: An abbreviated form of a term resulting from the omission of one or more term elements or syllables (e.g. 'flu' for 'influenza'). + + + + + ISO-12620 2.1.9: One of the alternate forms of a term. + + + + + + + Values for the attribute 'restype'. + + + + + Indicates a Windows RC AUTO3STATE control. + + + + + Indicates a Windows RC AUTOCHECKBOX control. + + + + + Indicates a Windows RC AUTORADIOBUTTON control. + + + + + Indicates a Windows RC BEDIT control. + + + + + Indicates a bitmap, for example a BITMAP resource in Windows. + + + + + Indicates a button object, for example a BUTTON control Windows. + + + + + Indicates a caption, such as the caption of a dialog box. + + + + + Indicates the cell in a table, for example the content of the <td> element in HTML. + + + + + Indicates check box object, for example a CHECKBOX control in Windows. + + + + + Indicates a menu item with an associated checkbox. + + + + + Indicates a list box, but with a check-box for each item. + + + + + Indicates a color selection dialog. + + + + + Indicates a combination of edit box and listbox object, for example a COMBOBOX control in Windows. + + + + + Indicates an initialization entry of an extended combobox DLGINIT resource block. (code 0x1234). + + + + + Indicates an initialization entry of a combobox DLGINIT resource block (code 0x0403). + + + + + Indicates a UI base class element that cannot be represented by any other element. + + + + + Indicates a context menu. + + + + + Indicates a Windows RC CTEXT control. + + + + + Indicates a cursor, for example a CURSOR resource in Windows. + + + + + Indicates a date/time picker. + + + + + Indicates a Windows RC DEFPUSHBUTTON control. + + + + + Indicates a dialog box. + + + + + Indicates a Windows RC DLGINIT resource block. + + + + + Indicates an edit box object, for example an EDIT control in Windows. + + + + + Indicates a filename. + + + + + Indicates a file dialog. + + + + + Indicates a footnote. + + + + + Indicates a font name. + + + + + Indicates a footer. + + + + + Indicates a frame object. + + + + + Indicates a XUL grid element. + + + + + Indicates a groupbox object, for example a GROUPBOX control in Windows. + + + + + Indicates a header item. + + + + + Indicates a heading, such has the content of <h1>, <h2>, etc. in HTML. + + + + + Indicates a Windows RC HEDIT control. + + + + + Indicates a horizontal scrollbar. + + + + + Indicates an icon, for example an ICON resource in Windows. + + + + + Indicates a Windows RC IEDIT control. + + + + + Indicates keyword list, such as the content of the Keywords meta-data in HTML, or a K footnote in WinHelp RTF. + + + + + Indicates a label object. + + + + + Indicates a label that is also a HTML link (not necessarily a URL). + + + + + Indicates a list (a group of list-items, for example an <ol> or <ul> element in HTML). + + + + + Indicates a listbox object, for example an LISTBOX control in Windows. + + + + + Indicates an list item (an entry in a list). + + + + + Indicates a Windows RC LTEXT control. + + + + + Indicates a menu (a group of menu-items). + + + + + Indicates a toolbar containing one or more tope level menus. + + + + + Indicates a menu item (an entry in a menu). + + + + + Indicates a XUL menuseparator element. + + + + + Indicates a message, for example an entry in a MESSAGETABLE resource in Windows. + + + + + Indicates a calendar control. + + + + + Indicates an edit box beside a spin control. + + + + + Indicates a catch all for rectangular areas. + + + + + Indicates a standalone menu not necessarily associated with a menubar. + + + + + Indicates a pushbox object, for example a PUSHBOX control in Windows. + + + + + Indicates a Windows RC PUSHBUTTON control. + + + + + Indicates a radio button object. + + + + + Indicates a menuitem with associated radio button. + + + + + Indicates raw data resources for an application. + + + + + Indicates a row in a table. + + + + + Indicates a Windows RC RTEXT control. + + + + + Indicates a user navigable container used to show a portion of a document. + + + + + Indicates a generic divider object (e.g. menu group separator). + + + + + Windows accelerators, shortcuts in resource or property files. + + + + + Indicates a UI control to indicate process activity but not progress. + + + + + Indicates a splitter bar. + + + + + Indicates a Windows RC STATE3 control. + + + + + Indicates a window for providing feedback to the users, like 'read-only', etc. + + + + + Indicates a string, for example an entry in a STRINGTABLE resource in Windows. + + + + + Indicates a layers of controls with a tab to select layers. + + + + + Indicates a display and edits regular two-dimensional tables of cells. + + + + + Indicates a XUL textbox element. + + + + + Indicates a UI button that can be toggled to on or off state. + + + + + Indicates an array of controls, usually buttons. + + + + + Indicates a pop up tool tip text. + + + + + Indicates a bar with a pointer indicating a position within a certain range. + + + + + Indicates a control that displays a set of hierarchical data. + + + + + Indicates a URI (URN or URL). + + + + + Indicates a Windows RC USERBUTTON control. + + + + + Indicates a user-defined control like CONTROL control in Windows. + + + + + Indicates the text of a variable. + + + + + Indicates version information about a resource like VERSIONINFO in Windows. + + + + + Indicates a vertical scrollbar. + + + + + Indicates a graphical window. + + + + + + + Values for the attribute 'size-unit'. + + + + + Indicates a size in 8-bit bytes. + + + + + Indicates a size in Unicode characters. + + + + + Indicates a size in columns. Used for HTML text area. + + + + + Indicates a size in centimeters. + + + + + Indicates a size in dialog units, as defined in Windows resources. + + + + + Indicates a size in 'font-size' units (as defined in CSS). + + + + + Indicates a size in 'x-height' units (as defined in CSS). + + + + + Indicates a size in glyphs. A glyph is considered to be one or more combined Unicode characters that represent a single displayable text character. Sometimes referred to as a 'grapheme cluster' + + + + + Indicates a size in inches. + + + + + Indicates a size in millimeters. + + + + + Indicates a size in percentage. + + + + + Indicates a size in pixels. + + + + + Indicates a size in point. + + + + + Indicates a size in rows. Used for HTML text area. + + + + + + + Values for the attribute 'state'. + + + + + Indicates the terminating state. + + + + + Indicates only non-textual information needs adaptation. + + + + + Indicates both text and non-textual information needs adaptation. + + + + + Indicates only non-textual information needs review. + + + + + Indicates both text and non-textual information needs review. + + + + + Indicates that only the text of the item needs to be reviewed. + + + + + Indicates that the item needs to be translated. + + + + + Indicates that the item is new. For example, translation units that were not in a previous version of the document. + + + + + Indicates that changes are reviewed and approved. + + + + + Indicates that the item has been translated. + + + + + + + Values for the attribute 'state-qualifier'. + + + + + Indicates an exact match. An exact match occurs when a source text of a segment is exactly the same as the source text of a segment that was translated previously. + + + + + Indicates a fuzzy match. A fuzzy match occurs when a source text of a segment is very similar to the source text of a segment that was translated previously (e.g. when the difference is casing, a few changed words, white-space discripancy, etc.). + + + + + Indicates a match based on matching IDs (in addition to matching text). + + + + + Indicates a translation derived from a glossary. + + + + + Indicates a translation derived from existing translation. + + + + + Indicates a translation derived from machine translation. + + + + + Indicates a translation derived from a translation repository. + + + + + Indicates a translation derived from a translation memory. + + + + + Indicates the translation is suggested by machine translation. + + + + + Indicates that the item has been rejected because of incorrect grammar. + + + + + Indicates that the item has been rejected because it is incorrect. + + + + + Indicates that the item has been rejected because it is too long or too short. + + + + + Indicates that the item has been rejected because of incorrect spelling. + + + + + Indicates the translation is suggested by translation memory. + + + + + + + Values for the attribute 'unit'. + + + + + Refers to words. + + + + + Refers to pages. + + + + + Refers to <trans-unit> elements. + + + + + Refers to <bin-unit> elements. + + + + + Refers to glyphs. + + + + + Refers to <trans-unit> and/or <bin-unit> elements. + + + + + Refers to the occurrences of instances defined by the count-type value. + + + + + Refers to characters. + + + + + Refers to lines. + + + + + Refers to sentences. + + + + + Refers to paragraphs. + + + + + Refers to segments. + + + + + Refers to placeables (inline elements). + + + + + + + Values for the attribute 'priority'. + + + + + Highest priority. + + + + + High priority. + + + + + High priority, but not as important as 2. + + + + + High priority, but not as important as 3. + + + + + Medium priority, but more important than 6. + + + + + Medium priority, but less important than 5. + + + + + Low priority, but more important than 8. + + + + + Low priority, but more important than 9. + + + + + Low priority. + + + + + Lowest priority. + + + + + + + + + This value indicates that all properties can be reformatted. This value must be used alone. + + + + + This value indicates that no properties should be reformatted. This value must be used alone. + + + + + + + + + + + + + This value indicates that all information in the coord attribute can be modified. + + + + + This value indicates that the x information in the coord attribute can be modified. + + + + + This value indicates that the y information in the coord attribute can be modified. + + + + + This value indicates that the cx information in the coord attribute can be modified. + + + + + This value indicates that the cy information in the coord attribute can be modified. + + + + + This value indicates that all the information in the font attribute can be modified. + + + + + This value indicates that the name information in the font attribute can be modified. + + + + + This value indicates that the size information in the font attribute can be modified. + + + + + This value indicates that the weight information in the font attribute can be modified. + + + + + This value indicates that the information in the css-style attribute can be modified. + + + + + This value indicates that the information in the style attribute can be modified. + + + + + This value indicates that the information in the exstyle attribute can be modified. + + + + + + + + + + + + + Indicates that the context is informational in nature, specifying for example, how a term should be translated. Thus, should be displayed to anyone editing the XLIFF document. + + + + + Indicates that the context-group is used to specify where the term was found in the translatable source. Thus, it is not displayed. + + + + + Indicates that the context information should be used during translation memory lookups. Thus, it is not displayed. + + + + + + + + + Represents a translation proposal from a translation memory or other resource. + + + + + Represents a previous version of the target element. + + + + + Represents a rejected version of the target element. + + + + + Represents a translation to be used for reference purposes only, for example from a related product or a different language. + + + + + Represents a proposed translation that was used for the translation of the trans-unit, possibly modified. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Values for the attribute 'coord'. + + + + + + + + Version values: 1.0 and 1.1 are allowed for backward compatibility. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/translation/Tests/Catalogue/AbstractOperationTest.php b/vendor/symfony/translation/Tests/Catalogue/AbstractOperationTest.php new file mode 100644 index 000000000..90cf4a5dc --- /dev/null +++ b/vendor/symfony/translation/Tests/Catalogue/AbstractOperationTest.php @@ -0,0 +1,74 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Tests\Catalogue; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Translation\MessageCatalogue; +use Symfony\Component\Translation\MessageCatalogueInterface; + +abstract class AbstractOperationTest extends TestCase +{ + public function testGetEmptyDomains() + { + $this->assertEquals( + array(), + $this->createOperation( + new MessageCatalogue('en'), + new MessageCatalogue('en') + )->getDomains() + ); + } + + public function testGetMergedDomains() + { + $this->assertEquals( + array('a', 'b', 'c'), + $this->createOperation( + new MessageCatalogue('en', array('a' => array(), 'b' => array())), + new MessageCatalogue('en', array('b' => array(), 'c' => array())) + )->getDomains() + ); + } + + public function testGetMessagesFromUnknownDomain() + { + $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('InvalidArgumentException'); + $this->createOperation( + new MessageCatalogue('en'), + new MessageCatalogue('en') + )->getMessages('domain'); + } + + public function testGetEmptyMessages() + { + $this->assertEquals( + array(), + $this->createOperation( + new MessageCatalogue('en', array('a' => array())), + new MessageCatalogue('en') + )->getMessages('a') + ); + } + + public function testGetEmptyResult() + { + $this->assertEquals( + new MessageCatalogue('en'), + $this->createOperation( + new MessageCatalogue('en'), + new MessageCatalogue('en') + )->getResult() + ); + } + + abstract protected function createOperation(MessageCatalogueInterface $source, MessageCatalogueInterface $target); +} diff --git a/vendor/symfony/translation/Tests/Catalogue/MergeOperationTest.php b/vendor/symfony/translation/Tests/Catalogue/MergeOperationTest.php new file mode 100644 index 000000000..8b51c15da --- /dev/null +++ b/vendor/symfony/translation/Tests/Catalogue/MergeOperationTest.php @@ -0,0 +1,83 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Tests\Catalogue; + +use Symfony\Component\Translation\Catalogue\MergeOperation; +use Symfony\Component\Translation\MessageCatalogue; +use Symfony\Component\Translation\MessageCatalogueInterface; + +class MergeOperationTest extends AbstractOperationTest +{ + public function testGetMessagesFromSingleDomain() + { + $operation = $this->createOperation( + new MessageCatalogue('en', array('messages' => array('a' => 'old_a', 'b' => 'old_b'))), + new MessageCatalogue('en', array('messages' => array('a' => 'new_a', 'c' => 'new_c'))) + ); + + $this->assertEquals( + array('a' => 'old_a', 'b' => 'old_b', 'c' => 'new_c'), + $operation->getMessages('messages') + ); + + $this->assertEquals( + array('c' => 'new_c'), + $operation->getNewMessages('messages') + ); + + $this->assertEquals( + array(), + $operation->getObsoleteMessages('messages') + ); + } + + public function testGetResultFromSingleDomain() + { + $this->assertEquals( + new MessageCatalogue('en', array( + 'messages' => array('a' => 'old_a', 'b' => 'old_b', 'c' => 'new_c'), + )), + $this->createOperation( + new MessageCatalogue('en', array('messages' => array('a' => 'old_a', 'b' => 'old_b'))), + new MessageCatalogue('en', array('messages' => array('a' => 'new_a', 'c' => 'new_c'))) + )->getResult() + ); + } + + public function testGetResultWithMetadata() + { + $leftCatalogue = new MessageCatalogue('en', array('messages' => array('a' => 'old_a', 'b' => 'old_b'))); + $leftCatalogue->setMetadata('a', 'foo', 'messages'); + $leftCatalogue->setMetadata('b', 'bar', 'messages'); + $rightCatalogue = new MessageCatalogue('en', array('messages' => array('b' => 'new_b', 'c' => 'new_c'))); + $rightCatalogue->setMetadata('b', 'baz', 'messages'); + $rightCatalogue->setMetadata('c', 'qux', 'messages'); + + $mergedCatalogue = new MessageCatalogue('en', array('messages' => array('a' => 'old_a', 'b' => 'old_b', 'c' => 'new_c'))); + $mergedCatalogue->setMetadata('a', 'foo', 'messages'); + $mergedCatalogue->setMetadata('b', 'bar', 'messages'); + $mergedCatalogue->setMetadata('c', 'qux', 'messages'); + + $this->assertEquals( + $mergedCatalogue, + $this->createOperation( + $leftCatalogue, + $rightCatalogue + )->getResult() + ); + } + + protected function createOperation(MessageCatalogueInterface $source, MessageCatalogueInterface $target) + { + return new MergeOperation($source, $target); + } +} diff --git a/vendor/symfony/translation/Tests/Catalogue/TargetOperationTest.php b/vendor/symfony/translation/Tests/Catalogue/TargetOperationTest.php new file mode 100644 index 000000000..271d17fb8 --- /dev/null +++ b/vendor/symfony/translation/Tests/Catalogue/TargetOperationTest.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Tests\Catalogue; + +use Symfony\Component\Translation\Catalogue\TargetOperation; +use Symfony\Component\Translation\MessageCatalogue; +use Symfony\Component\Translation\MessageCatalogueInterface; + +class TargetOperationTest extends AbstractOperationTest +{ + public function testGetMessagesFromSingleDomain() + { + $operation = $this->createOperation( + new MessageCatalogue('en', array('messages' => array('a' => 'old_a', 'b' => 'old_b'))), + new MessageCatalogue('en', array('messages' => array('a' => 'new_a', 'c' => 'new_c'))) + ); + + $this->assertEquals( + array('a' => 'old_a', 'c' => 'new_c'), + $operation->getMessages('messages') + ); + + $this->assertEquals( + array('c' => 'new_c'), + $operation->getNewMessages('messages') + ); + + $this->assertEquals( + array('b' => 'old_b'), + $operation->getObsoleteMessages('messages') + ); + } + + public function testGetResultFromSingleDomain() + { + $this->assertEquals( + new MessageCatalogue('en', array( + 'messages' => array('a' => 'old_a', 'c' => 'new_c'), + )), + $this->createOperation( + new MessageCatalogue('en', array('messages' => array('a' => 'old_a', 'b' => 'old_b'))), + new MessageCatalogue('en', array('messages' => array('a' => 'new_a', 'c' => 'new_c'))) + )->getResult() + ); + } + + public function testGetResultWithMetadata() + { + $leftCatalogue = new MessageCatalogue('en', array('messages' => array('a' => 'old_a', 'b' => 'old_b'))); + $leftCatalogue->setMetadata('a', 'foo', 'messages'); + $leftCatalogue->setMetadata('b', 'bar', 'messages'); + $rightCatalogue = new MessageCatalogue('en', array('messages' => array('b' => 'new_b', 'c' => 'new_c'))); + $rightCatalogue->setMetadata('b', 'baz', 'messages'); + $rightCatalogue->setMetadata('c', 'qux', 'messages'); + + $diffCatalogue = new MessageCatalogue('en', array('messages' => array('b' => 'old_b', 'c' => 'new_c'))); + $diffCatalogue->setMetadata('b', 'bar', 'messages'); + $diffCatalogue->setMetadata('c', 'qux', 'messages'); + + $this->assertEquals( + $diffCatalogue, + $this->createOperation( + $leftCatalogue, + $rightCatalogue + )->getResult() + ); + } + + protected function createOperation(MessageCatalogueInterface $source, MessageCatalogueInterface $target) + { + return new TargetOperation($source, $target); + } +} diff --git a/vendor/symfony/translation/Tests/Command/XliffLintCommandTest.php b/vendor/symfony/translation/Tests/Command/XliffLintCommandTest.php new file mode 100644 index 000000000..e37600c3e --- /dev/null +++ b/vendor/symfony/translation/Tests/Command/XliffLintCommandTest.php @@ -0,0 +1,163 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Tests\Command; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Tester\CommandTester; +use Symfony\Component\Translation\Command\XliffLintCommand; + +/** + * Tests the XliffLintCommand. + * + * @author Javier Eguiluz + */ +class XliffLintCommandTest extends TestCase +{ + private $files; + + public function testLintCorrectFile() + { + $tester = $this->createCommandTester(); + $filename = $this->createFile(); + + $tester->execute( + array('filename' => $filename), + array('verbosity' => OutputInterface::VERBOSITY_VERBOSE, 'decorated' => false) + ); + + $this->assertEquals(0, $tester->getStatusCode(), 'Returns 0 in case of success'); + $this->assertContains('OK', trim($tester->getDisplay())); + } + + public function testLintIncorrectXmlSyntax() + { + $tester = $this->createCommandTester(); + $filename = $this->createFile('note '); + + $tester->execute(array('filename' => $filename), array('decorated' => false)); + + $this->assertEquals(1, $tester->getStatusCode(), 'Returns 1 in case of error'); + $this->assertContains('Opening and ending tag mismatch: target line 6 and source', trim($tester->getDisplay())); + } + + public function testLintIncorrectTargetLanguage() + { + $tester = $this->createCommandTester(); + $filename = $this->createFile('note', 'es'); + + $tester->execute(array('filename' => $filename), array('decorated' => false)); + + $this->assertEquals(1, $tester->getStatusCode(), 'Returns 1 in case of error'); + $this->assertContains('There is a mismatch between the file extension ("en.xlf") and the "es" value used in the "target-language" attribute of the file.', trim($tester->getDisplay())); + } + + /** + * @expectedException \RuntimeException + */ + public function testLintFileNotReadable() + { + $tester = $this->createCommandTester(); + $filename = $this->createFile(); + unlink($filename); + + $tester->execute(array('filename' => $filename), array('decorated' => false)); + } + + public function testGetHelp() + { + $command = new XliffLintCommand(); + $expected = <<%command.name% command lints a XLIFF file and outputs to STDOUT +the first encountered syntax error. + +You can validates XLIFF contents passed from STDIN: + + cat filename | php %command.full_name% + +You can also validate the syntax of a file: + + php %command.full_name% filename + +Or of a whole directory: + + php %command.full_name% dirname + php %command.full_name% dirname --format=json + +EOF; + + $this->assertEquals($expected, $command->getHelp()); + } + + /** + * @return string Path to the new file + */ + private function createFile($sourceContent = 'note', $targetLanguage = 'en') + { + $xliffContent = << + + + + + $sourceContent + NOTE + + + + +XLIFF; + + $filename = sprintf('%s/translation-xliff-lint-test/messages.en.xlf', sys_get_temp_dir()); + file_put_contents($filename, $xliffContent); + + $this->files[] = $filename; + + return $filename; + } + + /** + * @return CommandTester + */ + private function createCommandTester($application = null) + { + if (!$application) { + $application = new Application(); + $application->add(new XliffLintCommand()); + } + + $command = $application->find('lint:xliff'); + + if ($application) { + $command->setApplication($application); + } + + return new CommandTester($command); + } + + protected function setUp() + { + $this->files = array(); + @mkdir(sys_get_temp_dir().'/translation-xliff-lint-test'); + } + + protected function tearDown() + { + foreach ($this->files as $file) { + if (file_exists($file)) { + unlink($file); + } + } + rmdir(sys_get_temp_dir().'/translation-xliff-lint-test'); + } +} diff --git a/vendor/symfony/translation/Tests/DataCollector/TranslationDataCollectorTest.php b/vendor/symfony/translation/Tests/DataCollector/TranslationDataCollectorTest.php new file mode 100644 index 000000000..6665eca49 --- /dev/null +++ b/vendor/symfony/translation/Tests/DataCollector/TranslationDataCollectorTest.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\Translation\Tests\DataCollector; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Translation\DataCollector\TranslationDataCollector; +use Symfony\Component\Translation\DataCollectorTranslator; + +class TranslationDataCollectorTest extends TestCase +{ + protected function setUp() + { + if (!class_exists('Symfony\Component\HttpKernel\DataCollector\DataCollector')) { + $this->markTestSkipped('The "DataCollector" is not available'); + } + } + + public function testCollectEmptyMessages() + { + $translator = $this->getTranslator(); + $translator->expects($this->any())->method('getCollectedMessages')->will($this->returnValue(array())); + + $dataCollector = new TranslationDataCollector($translator); + $dataCollector->lateCollect(); + + $this->assertEquals(0, $dataCollector->getCountMissings()); + $this->assertEquals(0, $dataCollector->getCountFallbacks()); + $this->assertEquals(0, $dataCollector->getCountDefines()); + $this->assertEquals(array(), $dataCollector->getMessages()->getValue()); + } + + public function testCollect() + { + $collectedMessages = array( + array( + 'id' => 'foo', + 'translation' => 'foo (en)', + 'locale' => 'en', + 'domain' => 'messages', + 'state' => DataCollectorTranslator::MESSAGE_DEFINED, + 'parameters' => array(), + 'transChoiceNumber' => null, + ), + array( + 'id' => 'bar', + 'translation' => 'bar (fr)', + 'locale' => 'fr', + 'domain' => 'messages', + 'state' => DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK, + 'parameters' => array(), + 'transChoiceNumber' => null, + ), + array( + 'id' => 'choice', + 'translation' => 'choice', + 'locale' => 'en', + 'domain' => 'messages', + 'state' => DataCollectorTranslator::MESSAGE_MISSING, + 'parameters' => array('%count%' => 3), + 'transChoiceNumber' => 3, + ), + array( + 'id' => 'choice', + 'translation' => 'choice', + 'locale' => 'en', + 'domain' => 'messages', + 'state' => DataCollectorTranslator::MESSAGE_MISSING, + 'parameters' => array('%count%' => 3), + 'transChoiceNumber' => 3, + ), + array( + 'id' => 'choice', + 'translation' => 'choice', + 'locale' => 'en', + 'domain' => 'messages', + 'state' => DataCollectorTranslator::MESSAGE_MISSING, + 'parameters' => array('%count%' => 4, '%foo%' => 'bar'), + 'transChoiceNumber' => 4, + ), + ); + $expectedMessages = array( + array( + 'id' => 'foo', + 'translation' => 'foo (en)', + 'locale' => 'en', + 'domain' => 'messages', + 'state' => DataCollectorTranslator::MESSAGE_DEFINED, + 'count' => 1, + 'parameters' => array(), + 'transChoiceNumber' => null, + ), + array( + 'id' => 'bar', + 'translation' => 'bar (fr)', + 'locale' => 'fr', + 'domain' => 'messages', + 'state' => DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK, + 'count' => 1, + 'parameters' => array(), + 'transChoiceNumber' => null, + ), + array( + 'id' => 'choice', + 'translation' => 'choice', + 'locale' => 'en', + 'domain' => 'messages', + 'state' => DataCollectorTranslator::MESSAGE_MISSING, + 'count' => 3, + 'parameters' => array( + array('%count%' => 3), + array('%count%' => 3), + array('%count%' => 4, '%foo%' => 'bar'), + ), + 'transChoiceNumber' => 3, + ), + ); + + $translator = $this->getTranslator(); + $translator->expects($this->any())->method('getCollectedMessages')->will($this->returnValue($collectedMessages)); + + $dataCollector = new TranslationDataCollector($translator); + $dataCollector->lateCollect(); + + $this->assertEquals(1, $dataCollector->getCountMissings()); + $this->assertEquals(1, $dataCollector->getCountFallbacks()); + $this->assertEquals(1, $dataCollector->getCountDefines()); + + $this->assertEquals($expectedMessages, array_values($dataCollector->getMessages()->getValue(true))); + } + + private function getTranslator() + { + $translator = $this + ->getMockBuilder('Symfony\Component\Translation\DataCollectorTranslator') + ->disableOriginalConstructor() + ->getMock() + ; + + return $translator; + } +} diff --git a/vendor/symfony/translation/Tests/DataCollectorTranslatorTest.php b/vendor/symfony/translation/Tests/DataCollectorTranslatorTest.php new file mode 100644 index 000000000..1cdd33b39 --- /dev/null +++ b/vendor/symfony/translation/Tests/DataCollectorTranslatorTest.php @@ -0,0 +1,92 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Translation\DataCollectorTranslator; +use Symfony\Component\Translation\Loader\ArrayLoader; +use Symfony\Component\Translation\Translator; + +class DataCollectorTranslatorTest extends TestCase +{ + public function testCollectMessages() + { + $collector = $this->createCollector(); + $collector->setFallbackLocales(array('fr', 'ru')); + + $collector->trans('foo'); + $collector->trans('bar'); + $collector->transChoice('choice', 0); + $collector->trans('bar_ru'); + $collector->trans('bar_ru', array('foo' => 'bar')); + + $expectedMessages = array(); + $expectedMessages[] = array( + 'id' => 'foo', + 'translation' => 'foo (en)', + 'locale' => 'en', + 'domain' => 'messages', + 'state' => DataCollectorTranslator::MESSAGE_DEFINED, + 'parameters' => array(), + 'transChoiceNumber' => null, + ); + $expectedMessages[] = array( + 'id' => 'bar', + 'translation' => 'bar (fr)', + 'locale' => 'fr', + 'domain' => 'messages', + 'state' => DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK, + 'parameters' => array(), + 'transChoiceNumber' => null, + ); + $expectedMessages[] = array( + 'id' => 'choice', + 'translation' => 'choice', + 'locale' => 'en', + 'domain' => 'messages', + 'state' => DataCollectorTranslator::MESSAGE_MISSING, + 'parameters' => array(), + 'transChoiceNumber' => 0, + ); + $expectedMessages[] = array( + 'id' => 'bar_ru', + 'translation' => 'bar (ru)', + 'locale' => 'ru', + 'domain' => 'messages', + 'state' => DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK, + 'parameters' => array(), + 'transChoiceNumber' => null, + ); + $expectedMessages[] = array( + 'id' => 'bar_ru', + 'translation' => 'bar (ru)', + 'locale' => 'ru', + 'domain' => 'messages', + 'state' => DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK, + 'parameters' => array('foo' => 'bar'), + 'transChoiceNumber' => null, + ); + + $this->assertEquals($expectedMessages, $collector->getCollectedMessages()); + } + + private function createCollector() + { + $translator = new Translator('en'); + $translator->addLoader('array', new ArrayLoader()); + $translator->addResource('array', array('foo' => 'foo (en)'), 'en'); + $translator->addResource('array', array('bar' => 'bar (fr)'), 'fr'); + $translator->addResource('array', array('bar_ru' => 'bar (ru)'), 'ru'); + + return new DataCollectorTranslator($translator); + } +} diff --git a/vendor/symfony/translation/Tests/DependencyInjection/TranslationDumperPassTest.php b/vendor/symfony/translation/Tests/DependencyInjection/TranslationDumperPassTest.php new file mode 100644 index 000000000..759bb0e97 --- /dev/null +++ b/vendor/symfony/translation/Tests/DependencyInjection/TranslationDumperPassTest.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\Translation\Tests\DependencyInjection; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\Translation\DependencyInjection\TranslationDumperPass; + +class TranslationDumperPassTest extends TestCase +{ + public function testProcess() + { + $container = new ContainerBuilder(); + $writerDefinition = $container->register('translation.writer'); + $container->register('foo.id') + ->addTag('translation.dumper', array('alias' => 'bar.alias')); + + $translationDumperPass = new TranslationDumperPass(); + $translationDumperPass->process($container); + + $this->assertEquals(array(array('addDumper', array('bar.alias', new Reference('foo.id')))), $writerDefinition->getMethodCalls()); + } + + public function testProcessNoDefinitionFound() + { + $container = new ContainerBuilder(); + + $definitionsBefore = \count($container->getDefinitions()); + $aliasesBefore = \count($container->getAliases()); + + $translationDumperPass = new TranslationDumperPass(); + $translationDumperPass->process($container); + + // the container is untouched (i.e. no new definitions or aliases) + $this->assertCount($definitionsBefore, $container->getDefinitions()); + $this->assertCount($aliasesBefore, $container->getAliases()); + } +} diff --git a/vendor/symfony/translation/Tests/DependencyInjection/TranslationExtractorPassTest.php b/vendor/symfony/translation/Tests/DependencyInjection/TranslationExtractorPassTest.php new file mode 100644 index 000000000..14d164d0e --- /dev/null +++ b/vendor/symfony/translation/Tests/DependencyInjection/TranslationExtractorPassTest.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Tests\DependencyInjection; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\Translation\DependencyInjection\TranslationExtractorPass; + +class TranslationExtractorPassTest extends TestCase +{ + public function testProcess() + { + $container = new ContainerBuilder(); + $extractorDefinition = $container->register('translation.extractor'); + $container->register('foo.id') + ->addTag('translation.extractor', array('alias' => 'bar.alias')); + + $translationDumperPass = new TranslationExtractorPass(); + $translationDumperPass->process($container); + + $this->assertEquals(array(array('addExtractor', array('bar.alias', new Reference('foo.id')))), $extractorDefinition->getMethodCalls()); + } + + public function testProcessNoDefinitionFound() + { + $container = new ContainerBuilder(); + + $definitionsBefore = \count($container->getDefinitions()); + $aliasesBefore = \count($container->getAliases()); + + $translationDumperPass = new TranslationExtractorPass(); + $translationDumperPass->process($container); + + // the container is untouched (i.e. no new definitions or aliases) + $this->assertCount($definitionsBefore, $container->getDefinitions()); + $this->assertCount($aliasesBefore, $container->getAliases()); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage The alias for the tag "translation.extractor" of service "foo.id" must be set. + */ + public function testProcessMissingAlias() + { + $definition = $this->getMockBuilder('Symfony\Component\DependencyInjection\Definition')->disableOriginalConstructor()->getMock(); + $container = new ContainerBuilder(); + $container->register('translation.extractor'); + $container->register('foo.id') + ->addTag('translation.extractor', array()); + + $definition->expects($this->never())->method('addMethodCall'); + + $translationDumperPass = new TranslationExtractorPass(); + $translationDumperPass->process($container); + } +} diff --git a/vendor/symfony/translation/Tests/DependencyInjection/TranslationPassTest.php b/vendor/symfony/translation/Tests/DependencyInjection/TranslationPassTest.php new file mode 100644 index 000000000..4082d169c --- /dev/null +++ b/vendor/symfony/translation/Tests/DependencyInjection/TranslationPassTest.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Tests\DependencyInjection; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\Translation\DependencyInjection\TranslatorPass; + +class TranslationPassTest extends TestCase +{ + public function testValidCollector() + { + $loader = (new Definition()) + ->addTag('translation.loader', array('alias' => 'xliff', 'legacy-alias' => 'xlf')); + + $reader = new Definition(); + + $translator = (new Definition()) + ->setArguments(array(null, null, null, null)); + + $container = new ContainerBuilder(); + $container->setDefinition('translator.default', $translator); + $container->setDefinition('translation.reader', $reader); + $container->setDefinition('translation.xliff_loader', $loader); + + $pass = new TranslatorPass('translator.default', 'translation.reader'); + $pass->process($container); + + $expectedReader = (new Definition()) + ->addMethodCall('addLoader', array('xliff', new Reference('translation.xliff_loader'))) + ->addMethodCall('addLoader', array('xlf', new Reference('translation.xliff_loader'))) + ; + $this->assertEquals($expectedReader, $reader); + + $expectedLoader = (new Definition()) + ->addTag('translation.loader', array('alias' => 'xliff', 'legacy-alias' => 'xlf')) + ; + $this->assertEquals($expectedLoader, $loader); + + $this->assertSame(array('translation.xliff_loader' => array('xliff', 'xlf')), $translator->getArgument(3)); + + $expected = array('translation.xliff_loader' => new ServiceClosureArgument(new Reference('translation.xliff_loader'))); + $this->assertEquals($expected, $container->getDefinition((string) $translator->getArgument(0))->getArgument(0)); + } +} diff --git a/vendor/symfony/translation/Tests/Dumper/CsvFileDumperTest.php b/vendor/symfony/translation/Tests/Dumper/CsvFileDumperTest.php new file mode 100644 index 000000000..9a7a059a4 --- /dev/null +++ b/vendor/symfony/translation/Tests/Dumper/CsvFileDumperTest.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Tests\Dumper; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Translation\Dumper\CsvFileDumper; +use Symfony\Component\Translation\MessageCatalogue; + +class CsvFileDumperTest extends TestCase +{ + public function testFormatCatalogue() + { + $catalogue = new MessageCatalogue('en'); + $catalogue->add(array('foo' => 'bar', 'bar' => 'foo +foo', 'foo;foo' => 'bar')); + + $dumper = new CsvFileDumper(); + + $this->assertStringEqualsFile(__DIR__.'/../fixtures/valid.csv', $dumper->formatCatalogue($catalogue, 'messages')); + } +} diff --git a/vendor/symfony/translation/Tests/Dumper/FileDumperTest.php b/vendor/symfony/translation/Tests/Dumper/FileDumperTest.php new file mode 100644 index 000000000..d1524d6e5 --- /dev/null +++ b/vendor/symfony/translation/Tests/Dumper/FileDumperTest.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Tests\Dumper; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Translation\Dumper\FileDumper; +use Symfony\Component\Translation\MessageCatalogue; + +class FileDumperTest extends TestCase +{ + public function testDump() + { + $tempDir = sys_get_temp_dir(); + + $catalogue = new MessageCatalogue('en'); + $catalogue->add(array('foo' => 'bar')); + + $dumper = new ConcreteFileDumper(); + $dumper->dump($catalogue, array('path' => $tempDir)); + + $this->assertFileExists($tempDir.'/messages.en.concrete'); + + @unlink($tempDir.'/messages.en.concrete'); + } + + public function testDumpCreatesNestedDirectoriesAndFile() + { + $tempDir = sys_get_temp_dir(); + $translationsDir = $tempDir.'/test/translations'; + $file = $translationsDir.'/messages.en.concrete'; + + $catalogue = new MessageCatalogue('en'); + $catalogue->add(array('foo' => 'bar')); + + $dumper = new ConcreteFileDumper(); + $dumper->setRelativePathTemplate('test/translations/%domain%.%locale%.%extension%'); + $dumper->dump($catalogue, array('path' => $tempDir)); + + $this->assertFileExists($file); + + @unlink($file); + @rmdir($translationsDir); + } +} + +class ConcreteFileDumper extends FileDumper +{ + public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array()) + { + return ''; + } + + protected function getExtension() + { + return 'concrete'; + } +} diff --git a/vendor/symfony/translation/Tests/Dumper/IcuResFileDumperTest.php b/vendor/symfony/translation/Tests/Dumper/IcuResFileDumperTest.php new file mode 100644 index 000000000..78d0abb6e --- /dev/null +++ b/vendor/symfony/translation/Tests/Dumper/IcuResFileDumperTest.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\Translation\Tests\Dumper; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Translation\Dumper\IcuResFileDumper; +use Symfony\Component\Translation\MessageCatalogue; + +class IcuResFileDumperTest extends TestCase +{ + public function testFormatCatalogue() + { + $catalogue = new MessageCatalogue('en'); + $catalogue->add(array('foo' => 'bar')); + + $dumper = new IcuResFileDumper(); + + $this->assertStringEqualsFile(__DIR__.'/../fixtures/resourcebundle/res/en.res', $dumper->formatCatalogue($catalogue, 'messages')); + } +} diff --git a/vendor/symfony/translation/Tests/Dumper/IniFileDumperTest.php b/vendor/symfony/translation/Tests/Dumper/IniFileDumperTest.php new file mode 100644 index 000000000..5f3c1236c --- /dev/null +++ b/vendor/symfony/translation/Tests/Dumper/IniFileDumperTest.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\Translation\Tests\Dumper; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Translation\Dumper\IniFileDumper; +use Symfony\Component\Translation\MessageCatalogue; + +class IniFileDumperTest extends TestCase +{ + public function testFormatCatalogue() + { + $catalogue = new MessageCatalogue('en'); + $catalogue->add(array('foo' => 'bar')); + + $dumper = new IniFileDumper(); + + $this->assertStringEqualsFile(__DIR__.'/../fixtures/resources.ini', $dumper->formatCatalogue($catalogue, 'messages')); + } +} diff --git a/vendor/symfony/translation/Tests/Dumper/JsonFileDumperTest.php b/vendor/symfony/translation/Tests/Dumper/JsonFileDumperTest.php new file mode 100644 index 000000000..b134ce4f2 --- /dev/null +++ b/vendor/symfony/translation/Tests/Dumper/JsonFileDumperTest.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Tests\Dumper; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Translation\Dumper\JsonFileDumper; +use Symfony\Component\Translation\MessageCatalogue; + +class JsonFileDumperTest extends TestCase +{ + public function testFormatCatalogue() + { + $catalogue = new MessageCatalogue('en'); + $catalogue->add(array('foo' => 'bar')); + + $dumper = new JsonFileDumper(); + + $this->assertStringEqualsFile(__DIR__.'/../fixtures/resources.json', $dumper->formatCatalogue($catalogue, 'messages')); + } + + public function testDumpWithCustomEncoding() + { + $catalogue = new MessageCatalogue('en'); + $catalogue->add(array('foo' => '"bar"')); + + $dumper = new JsonFileDumper(); + + $this->assertStringEqualsFile(__DIR__.'/../fixtures/resources.dump.json', $dumper->formatCatalogue($catalogue, 'messages', array('json_encoding' => JSON_HEX_QUOT))); + } +} diff --git a/vendor/symfony/translation/Tests/Dumper/MoFileDumperTest.php b/vendor/symfony/translation/Tests/Dumper/MoFileDumperTest.php new file mode 100644 index 000000000..d0519675b --- /dev/null +++ b/vendor/symfony/translation/Tests/Dumper/MoFileDumperTest.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\Translation\Tests\Dumper; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Translation\Dumper\MoFileDumper; +use Symfony\Component\Translation\MessageCatalogue; + +class MoFileDumperTest extends TestCase +{ + public function testFormatCatalogue() + { + $catalogue = new MessageCatalogue('en'); + $catalogue->add(array('foo' => 'bar')); + + $dumper = new MoFileDumper(); + + $this->assertStringEqualsFile(__DIR__.'/../fixtures/resources.mo', $dumper->formatCatalogue($catalogue, 'messages')); + } +} diff --git a/vendor/symfony/translation/Tests/Dumper/PhpFileDumperTest.php b/vendor/symfony/translation/Tests/Dumper/PhpFileDumperTest.php new file mode 100644 index 000000000..22a39dd43 --- /dev/null +++ b/vendor/symfony/translation/Tests/Dumper/PhpFileDumperTest.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\Translation\Tests\Dumper; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Translation\Dumper\PhpFileDumper; +use Symfony\Component\Translation\MessageCatalogue; + +class PhpFileDumperTest extends TestCase +{ + public function testFormatCatalogue() + { + $catalogue = new MessageCatalogue('en'); + $catalogue->add(array('foo' => 'bar')); + + $dumper = new PhpFileDumper(); + + $this->assertStringEqualsFile(__DIR__.'/../fixtures/resources.php', $dumper->formatCatalogue($catalogue, 'messages')); + } +} diff --git a/vendor/symfony/translation/Tests/Dumper/PoFileDumperTest.php b/vendor/symfony/translation/Tests/Dumper/PoFileDumperTest.php new file mode 100644 index 000000000..5d7524710 --- /dev/null +++ b/vendor/symfony/translation/Tests/Dumper/PoFileDumperTest.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\Translation\Tests\Dumper; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Translation\Dumper\PoFileDumper; +use Symfony\Component\Translation\MessageCatalogue; + +class PoFileDumperTest extends TestCase +{ + public function testFormatCatalogue() + { + $catalogue = new MessageCatalogue('en'); + $catalogue->add(array('foo' => 'bar')); + + $dumper = new PoFileDumper(); + + $this->assertStringEqualsFile(__DIR__.'/../fixtures/resources.po', $dumper->formatCatalogue($catalogue, 'messages')); + } +} diff --git a/vendor/symfony/translation/Tests/Dumper/QtFileDumperTest.php b/vendor/symfony/translation/Tests/Dumper/QtFileDumperTest.php new file mode 100644 index 000000000..8e4a6f43f --- /dev/null +++ b/vendor/symfony/translation/Tests/Dumper/QtFileDumperTest.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\Translation\Tests\Dumper; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Translation\Dumper\QtFileDumper; +use Symfony\Component\Translation\MessageCatalogue; + +class QtFileDumperTest extends TestCase +{ + public function testFormatCatalogue() + { + $catalogue = new MessageCatalogue('en'); + $catalogue->add(array('foo' => 'bar'), 'resources'); + + $dumper = new QtFileDumper(); + + $this->assertStringEqualsFile(__DIR__.'/../fixtures/resources.ts', $dumper->formatCatalogue($catalogue, 'resources')); + } +} diff --git a/vendor/symfony/translation/Tests/Dumper/XliffFileDumperTest.php b/vendor/symfony/translation/Tests/Dumper/XliffFileDumperTest.php new file mode 100644 index 000000000..443dccf2b --- /dev/null +++ b/vendor/symfony/translation/Tests/Dumper/XliffFileDumperTest.php @@ -0,0 +1,115 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Tests\Dumper; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Translation\Dumper\XliffFileDumper; +use Symfony\Component\Translation\MessageCatalogue; + +class XliffFileDumperTest extends TestCase +{ + public function testFormatCatalogue() + { + $catalogue = new MessageCatalogue('en_US'); + $catalogue->add(array( + 'foo' => 'bar', + 'key' => '', + 'key.with.cdata' => ' & ', + )); + $catalogue->setMetadata('foo', array('notes' => array(array('priority' => 1, 'from' => 'bar', 'content' => 'baz')))); + $catalogue->setMetadata('key', array('notes' => array(array('content' => 'baz'), array('content' => 'qux')))); + + $dumper = new XliffFileDumper(); + + $this->assertStringEqualsFile( + __DIR__.'/../fixtures/resources-clean.xlf', + $dumper->formatCatalogue($catalogue, 'messages', array('default_locale' => 'fr_FR')) + ); + } + + public function testFormatCatalogueXliff2() + { + $catalogue = new MessageCatalogue('en_US'); + $catalogue->add(array( + 'foo' => 'bar', + 'key' => '', + 'key.with.cdata' => ' & ', + )); + $catalogue->setMetadata('key', array('target-attributes' => array('order' => 1))); + + $dumper = new XliffFileDumper(); + + $this->assertStringEqualsFile( + __DIR__.'/../fixtures/resources-2.0-clean.xlf', + $dumper->formatCatalogue($catalogue, 'messages', array('default_locale' => 'fr_FR', 'xliff_version' => '2.0')) + ); + } + + public function testFormatCatalogueWithCustomToolInfo() + { + $options = array( + 'default_locale' => 'en_US', + 'tool_info' => array('tool-id' => 'foo', 'tool-name' => 'foo', 'tool-version' => '0.0', 'tool-company' => 'Foo'), + ); + + $catalogue = new MessageCatalogue('en_US'); + $catalogue->add(array('foo' => 'bar')); + + $dumper = new XliffFileDumper(); + + $this->assertStringEqualsFile( + __DIR__.'/../fixtures/resources-tool-info.xlf', + $dumper->formatCatalogue($catalogue, 'messages', $options) + ); + } + + public function testFormatCatalogueWithTargetAttributesMetadata() + { + $catalogue = new MessageCatalogue('en_US'); + $catalogue->add(array( + 'foo' => 'bar', + )); + $catalogue->setMetadata('foo', array('target-attributes' => array('state' => 'needs-translation'))); + + $dumper = new XliffFileDumper(); + + $this->assertStringEqualsFile( + __DIR__.'/../fixtures/resources-target-attributes.xlf', + $dumper->formatCatalogue($catalogue, 'messages', array('default_locale' => 'fr_FR')) + ); + } + + public function testFormatCatalogueWithNotesMetadata() + { + $catalogue = new MessageCatalogue('en_US'); + $catalogue->add(array( + 'foo' => 'bar', + 'baz' => 'biz', + )); + $catalogue->setMetadata('foo', array('notes' => array( + array('category' => 'state', 'content' => 'new'), + array('category' => 'approved', 'content' => 'true'), + array('category' => 'section', 'content' => 'user login', 'priority' => '1'), + ))); + $catalogue->setMetadata('baz', array('notes' => array( + array('id' => 'x', 'content' => 'x_content'), + array('appliesTo' => 'target', 'category' => 'quality', 'content' => 'Fuzzy'), + ))); + + $dumper = new XliffFileDumper(); + + $this->assertStringEqualsFile( + __DIR__.'/../fixtures/resources-notes-meta.xlf', + $dumper->formatCatalogue($catalogue, 'messages', array('default_locale' => 'fr_FR', 'xliff_version' => '2.0')) + ); + } +} diff --git a/vendor/symfony/translation/Tests/Dumper/YamlFileDumperTest.php b/vendor/symfony/translation/Tests/Dumper/YamlFileDumperTest.php new file mode 100644 index 000000000..a5549a2e9 --- /dev/null +++ b/vendor/symfony/translation/Tests/Dumper/YamlFileDumperTest.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Tests\Dumper; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Translation\Dumper\YamlFileDumper; +use Symfony\Component\Translation\MessageCatalogue; + +class YamlFileDumperTest extends TestCase +{ + public function testTreeFormatCatalogue() + { + $catalogue = new MessageCatalogue('en'); + $catalogue->add( + array( + 'foo.bar1' => 'value1', + 'foo.bar2' => 'value2', + )); + + $dumper = new YamlFileDumper(); + + $this->assertStringEqualsFile(__DIR__.'/../fixtures/messages.yml', $dumper->formatCatalogue($catalogue, 'messages', array('as_tree' => true, 'inline' => 999))); + } + + public function testLinearFormatCatalogue() + { + $catalogue = new MessageCatalogue('en'); + $catalogue->add( + array( + 'foo.bar1' => 'value1', + 'foo.bar2' => 'value2', + )); + + $dumper = new YamlFileDumper(); + + $this->assertStringEqualsFile(__DIR__.'/../fixtures/messages_linear.yml', $dumper->formatCatalogue($catalogue, 'messages')); + } +} diff --git a/vendor/symfony/translation/Tests/Extractor/PhpExtractorTest.php b/vendor/symfony/translation/Tests/Extractor/PhpExtractorTest.php new file mode 100644 index 000000000..3487dd624 --- /dev/null +++ b/vendor/symfony/translation/Tests/Extractor/PhpExtractorTest.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Tests\Extractor; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Translation\Extractor\PhpExtractor; +use Symfony\Component\Translation\MessageCatalogue; + +class PhpExtractorTest extends TestCase +{ + /** + * @dataProvider resourcesProvider + * + * @param array|string $resource + */ + public function testExtraction($resource) + { + // Arrange + $extractor = new PhpExtractor(); + $extractor->setPrefix('prefix'); + $catalogue = new MessageCatalogue('en'); + + // Act + $extractor->extract($resource, $catalogue); + + $expectedHeredoc = << array( + 'single-quoted key' => 'prefixsingle-quoted key', + 'double-quoted key' => 'prefixdouble-quoted key', + 'heredoc key' => 'prefixheredoc key', + 'nowdoc key' => 'prefixnowdoc key', + "double-quoted key with whitespace and escaped \$\n\" sequences" => "prefixdouble-quoted key with whitespace and escaped \$\n\" sequences", + 'single-quoted key with whitespace and nonescaped \$\n\' sequences' => 'prefixsingle-quoted key with whitespace and nonescaped \$\n\' sequences', + 'single-quoted key with "quote mark at the end"' => 'prefixsingle-quoted key with "quote mark at the end"', + $expectedHeredoc => 'prefix'.$expectedHeredoc, + $expectedNowdoc => 'prefix'.$expectedNowdoc, + '{0} There is no apples|{1} There is one apple|]1,Inf[ There are %count% apples' => 'prefix{0} There is no apples|{1} There is one apple|]1,Inf[ There are %count% apples', + ), + 'not_messages' => array( + 'other-domain-test-no-params-short-array' => 'prefixother-domain-test-no-params-short-array', + 'other-domain-test-no-params-long-array' => 'prefixother-domain-test-no-params-long-array', + 'other-domain-test-params-short-array' => 'prefixother-domain-test-params-short-array', + 'other-domain-test-params-long-array' => 'prefixother-domain-test-params-long-array', + 'other-domain-test-trans-choice-short-array-%count%' => 'prefixother-domain-test-trans-choice-short-array-%count%', + 'other-domain-test-trans-choice-long-array-%count%' => 'prefixother-domain-test-trans-choice-long-array-%count%', + 'typecast' => 'prefixtypecast', + 'msg1' => 'prefixmsg1', + 'msg2' => 'prefixmsg2', + ), + ); + $actualCatalogue = $catalogue->all(); + + $this->assertEquals($expectedCatalogue, $actualCatalogue); + } + + public function resourcesProvider() + { + $directory = __DIR__.'/../fixtures/extractor/'; + $splFiles = array(); + foreach (new \DirectoryIterator($directory) as $fileInfo) { + if ($fileInfo->isDot()) { + continue; + } + if ('translation.html.php' === $fileInfo->getBasename()) { + $phpFile = $fileInfo->getPathname(); + } + $splFiles[] = $fileInfo->getFileInfo(); + } + + return array( + array($directory), + array($phpFile), + array(glob($directory.'*')), + array($splFiles), + array(new \ArrayObject(glob($directory.'*'))), + array(new \ArrayObject($splFiles)), + ); + } +} diff --git a/vendor/symfony/translation/Tests/Formatter/MessageFormatterTest.php b/vendor/symfony/translation/Tests/Formatter/MessageFormatterTest.php new file mode 100644 index 000000000..1fa736e7e --- /dev/null +++ b/vendor/symfony/translation/Tests/Formatter/MessageFormatterTest.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Tests\Formatter; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Translation\Formatter\MessageFormatter; + +class MessageFormatterTest extends TestCase +{ + /** + * @dataProvider getTransMessages + */ + public function testFormat($expected, $message, $parameters = array()) + { + $this->assertEquals($expected, $this->getMessageFormatter()->format($message, 'en', $parameters)); + } + + /** + * @dataProvider getTransChoiceMessages + */ + public function testFormatPlural($expected, $message, $number, $parameters) + { + $this->assertEquals($expected, $this->getMessageFormatter()->choiceFormat($message, $number, 'fr', $parameters)); + } + + public function getTransMessages() + { + return array( + array( + 'There is one apple', + 'There is one apple', + ), + array( + 'There are 5 apples', + 'There are %count% apples', + array('%count%' => 5), + ), + array( + 'There are 5 apples', + 'There are {{count}} apples', + array('{{count}}' => 5), + ), + ); + } + + public function getTransChoiceMessages() + { + return array( + array('Il y a 0 pomme', '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 0, array('%count%' => 0)), + array('Il y a 1 pomme', '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 1, array('%count%' => 1)), + array('Il y a 10 pommes', '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 10, array('%count%' => 10)), + + array('Il y a 0 pomme', 'Il y a %count% pomme|Il y a %count% pommes', 0, array('%count%' => 0)), + array('Il y a 1 pomme', 'Il y a %count% pomme|Il y a %count% pommes', 1, array('%count%' => 1)), + array('Il y a 10 pommes', 'Il y a %count% pomme|Il y a %count% pommes', 10, array('%count%' => 10)), + + array('Il y a 0 pomme', 'one: Il y a %count% pomme|more: Il y a %count% pommes', 0, array('%count%' => 0)), + array('Il y a 1 pomme', 'one: Il y a %count% pomme|more: Il y a %count% pommes', 1, array('%count%' => 1)), + array('Il y a 10 pommes', 'one: Il y a %count% pomme|more: Il y a %count% pommes', 10, array('%count%' => 10)), + + array('Il n\'y a aucune pomme', '{0} Il n\'y a aucune pomme|one: Il y a %count% pomme|more: Il y a %count% pommes', 0, array('%count%' => 0)), + array('Il y a 1 pomme', '{0} Il n\'y a aucune pomme|one: Il y a %count% pomme|more: Il y a %count% pommes', 1, array('%count%' => 1)), + array('Il y a 10 pommes', '{0} Il n\'y a aucune pomme|one: Il y a %count% pomme|more: Il y a %count% pommes', 10, array('%count%' => 10)), + + array('Il y a 0 pomme', '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 0, array('%count%' => 0)), + ); + } + + private function getMessageFormatter() + { + return new MessageFormatter(); + } +} diff --git a/vendor/symfony/translation/Tests/IdentityTranslatorTest.php b/vendor/symfony/translation/Tests/IdentityTranslatorTest.php new file mode 100644 index 000000000..78288da40 --- /dev/null +++ b/vendor/symfony/translation/Tests/IdentityTranslatorTest.php @@ -0,0 +1,96 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Intl\Util\IntlTestHelper; +use Symfony\Component\Translation\IdentityTranslator; + +class IdentityTranslatorTest extends TestCase +{ + /** + * @dataProvider getTransTests + */ + public function testTrans($expected, $id, $parameters) + { + $translator = new IdentityTranslator(); + + $this->assertEquals($expected, $translator->trans($id, $parameters)); + } + + /** + * @dataProvider getTransChoiceTests + */ + public function testTransChoiceWithExplicitLocale($expected, $id, $number, $parameters) + { + $translator = new IdentityTranslator(); + $translator->setLocale('en'); + + $this->assertEquals($expected, $translator->transChoice($id, $number, $parameters)); + } + + /** + * @dataProvider getTransChoiceTests + */ + public function testTransChoiceWithDefaultLocale($expected, $id, $number, $parameters) + { + \Locale::setDefault('en'); + + $translator = new IdentityTranslator(); + + $this->assertEquals($expected, $translator->transChoice($id, $number, $parameters)); + } + + public function testGetSetLocale() + { + $translator = new IdentityTranslator(); + $translator->setLocale('en'); + + $this->assertEquals('en', $translator->getLocale()); + } + + public function testGetLocaleReturnsDefaultLocaleIfNotSet() + { + // in order to test with "pt_BR" + IntlTestHelper::requireFullIntl($this, false); + + $translator = new IdentityTranslator(); + + \Locale::setDefault('en'); + $this->assertEquals('en', $translator->getLocale()); + + \Locale::setDefault('pt_BR'); + $this->assertEquals('pt_BR', $translator->getLocale()); + } + + public function getTransTests() + { + return array( + array('Symfony is great!', 'Symfony is great!', array()), + array('Symfony is awesome!', 'Symfony is %what%!', array('%what%' => 'awesome')), + ); + } + + public function getTransChoiceTests() + { + return array( + array('There are no apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 0, array('%count%' => 0)), + array('There is one apple', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 1, array('%count%' => 1)), + array('There are 10 apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 10, array('%count%' => 10)), + array('There are 0 apples', 'There is 1 apple|There are %count% apples', 0, array('%count%' => 0)), + array('There is 1 apple', 'There is 1 apple|There are %count% apples', 1, array('%count%' => 1)), + array('There are 10 apples', 'There is 1 apple|There are %count% apples', 10, array('%count%' => 10)), + // custom validation messages may be coded with a fixed value + array('There are 2 apples', 'There are 2 apples', 2, array('%count%' => 2)), + ); + } +} diff --git a/vendor/symfony/translation/Tests/IntervalTest.php b/vendor/symfony/translation/Tests/IntervalTest.php new file mode 100644 index 000000000..99b209a70 --- /dev/null +++ b/vendor/symfony/translation/Tests/IntervalTest.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Translation\Interval; + +class IntervalTest extends TestCase +{ + /** + * @dataProvider getTests + */ + public function testTest($expected, $number, $interval) + { + $this->assertEquals($expected, Interval::test($number, $interval)); + } + + /** + * @expectedException \Symfony\Component\Translation\Exception\InvalidArgumentException + */ + public function testTestException() + { + Interval::test(1, 'foobar'); + } + + public function getTests() + { + return array( + array(true, 3, '{1,2, 3 ,4}'), + array(false, 10, '{1,2, 3 ,4}'), + array(false, 3, '[1,2]'), + array(true, 1, '[1,2]'), + array(true, 2, '[1,2]'), + array(false, 1, ']1,2['), + array(false, 2, ']1,2['), + array(true, log(0), '[-Inf,2['), + array(true, -log(0), '[-2,+Inf]'), + ); + } +} diff --git a/vendor/symfony/translation/Tests/Loader/CsvFileLoaderTest.php b/vendor/symfony/translation/Tests/Loader/CsvFileLoaderTest.php new file mode 100644 index 000000000..29efdef00 --- /dev/null +++ b/vendor/symfony/translation/Tests/Loader/CsvFileLoaderTest.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Tests\Loader; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\Translation\Loader\CsvFileLoader; + +class CsvFileLoaderTest extends TestCase +{ + public function testLoad() + { + $loader = new CsvFileLoader(); + $resource = __DIR__.'/../fixtures/resources.csv'; + $catalogue = $loader->load($resource, 'en', 'domain1'); + + $this->assertEquals(array('foo' => 'bar'), $catalogue->all('domain1')); + $this->assertEquals('en', $catalogue->getLocale()); + $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources()); + } + + public function testLoadDoesNothingIfEmpty() + { + $loader = new CsvFileLoader(); + $resource = __DIR__.'/../fixtures/empty.csv'; + $catalogue = $loader->load($resource, 'en', 'domain1'); + + $this->assertEquals(array(), $catalogue->all('domain1')); + $this->assertEquals('en', $catalogue->getLocale()); + $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources()); + } + + /** + * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException + */ + public function testLoadNonExistingResource() + { + $loader = new CsvFileLoader(); + $resource = __DIR__.'/../fixtures/not-exists.csv'; + $loader->load($resource, 'en', 'domain1'); + } + + /** + * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException + */ + public function testLoadNonLocalResource() + { + $loader = new CsvFileLoader(); + $resource = 'http://example.com/resources.csv'; + $loader->load($resource, 'en', 'domain1'); + } +} diff --git a/vendor/symfony/translation/Tests/Loader/IcuDatFileLoaderTest.php b/vendor/symfony/translation/Tests/Loader/IcuDatFileLoaderTest.php new file mode 100644 index 000000000..e28db6013 --- /dev/null +++ b/vendor/symfony/translation/Tests/Loader/IcuDatFileLoaderTest.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Tests\Loader; + +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\Translation\Loader\IcuDatFileLoader; + +/** + * @requires extension intl + */ +class IcuDatFileLoaderTest extends LocalizedTestCase +{ + /** + * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException + */ + public function testLoadInvalidResource() + { + $loader = new IcuDatFileLoader(); + $loader->load(__DIR__.'/../fixtures/resourcebundle/corrupted/resources', 'es', 'domain2'); + } + + public function testDatEnglishLoad() + { + // bundled resource is build using pkgdata command which at least in ICU 4.2 comes in extremely! buggy form + // you must specify an temporary build directory which is not the same as current directory and + // MUST reside on the same partition. pkgdata -p resources -T /srv -d.packagelist.txt + $loader = new IcuDatFileLoader(); + $resource = __DIR__.'/../fixtures/resourcebundle/dat/resources'; + $catalogue = $loader->load($resource, 'en', 'domain1'); + + $this->assertEquals(array('symfony' => 'Symfony 2 is great'), $catalogue->all('domain1')); + $this->assertEquals('en', $catalogue->getLocale()); + $this->assertEquals(array(new FileResource($resource.'.dat')), $catalogue->getResources()); + } + + public function testDatFrenchLoad() + { + $loader = new IcuDatFileLoader(); + $resource = __DIR__.'/../fixtures/resourcebundle/dat/resources'; + $catalogue = $loader->load($resource, 'fr', 'domain1'); + + $this->assertEquals(array('symfony' => 'Symfony 2 est génial'), $catalogue->all('domain1')); + $this->assertEquals('fr', $catalogue->getLocale()); + $this->assertEquals(array(new FileResource($resource.'.dat')), $catalogue->getResources()); + } + + /** + * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException + */ + public function testLoadNonExistingResource() + { + $loader = new IcuDatFileLoader(); + $loader->load(__DIR__.'/../fixtures/non-existing.txt', 'en', 'domain1'); + } +} diff --git a/vendor/symfony/translation/Tests/Loader/IcuResFileLoaderTest.php b/vendor/symfony/translation/Tests/Loader/IcuResFileLoaderTest.php new file mode 100644 index 000000000..92d8933c8 --- /dev/null +++ b/vendor/symfony/translation/Tests/Loader/IcuResFileLoaderTest.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\Translation\Tests\Loader; + +use Symfony\Component\Config\Resource\DirectoryResource; +use Symfony\Component\Translation\Loader\IcuResFileLoader; + +/** + * @requires extension intl + */ +class IcuResFileLoaderTest extends LocalizedTestCase +{ + public function testLoad() + { + // resource is build using genrb command + $loader = new IcuResFileLoader(); + $resource = __DIR__.'/../fixtures/resourcebundle/res'; + $catalogue = $loader->load($resource, 'en', 'domain1'); + + $this->assertEquals(array('foo' => 'bar'), $catalogue->all('domain1')); + $this->assertEquals('en', $catalogue->getLocale()); + $this->assertEquals(array(new DirectoryResource($resource)), $catalogue->getResources()); + } + + /** + * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException + */ + public function testLoadNonExistingResource() + { + $loader = new IcuResFileLoader(); + $loader->load(__DIR__.'/../fixtures/non-existing.txt', 'en', 'domain1'); + } + + /** + * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException + */ + public function testLoadInvalidResource() + { + $loader = new IcuResFileLoader(); + $loader->load(__DIR__.'/../fixtures/resourcebundle/corrupted', 'en', 'domain1'); + } +} diff --git a/vendor/symfony/translation/Tests/Loader/IniFileLoaderTest.php b/vendor/symfony/translation/Tests/Loader/IniFileLoaderTest.php new file mode 100644 index 000000000..d9dcc5e8f --- /dev/null +++ b/vendor/symfony/translation/Tests/Loader/IniFileLoaderTest.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\Translation\Tests\Loader; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\Translation\Loader\IniFileLoader; + +class IniFileLoaderTest extends TestCase +{ + public function testLoad() + { + $loader = new IniFileLoader(); + $resource = __DIR__.'/../fixtures/resources.ini'; + $catalogue = $loader->load($resource, 'en', 'domain1'); + + $this->assertEquals(array('foo' => 'bar'), $catalogue->all('domain1')); + $this->assertEquals('en', $catalogue->getLocale()); + $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources()); + } + + public function testLoadDoesNothingIfEmpty() + { + $loader = new IniFileLoader(); + $resource = __DIR__.'/../fixtures/empty.ini'; + $catalogue = $loader->load($resource, 'en', 'domain1'); + + $this->assertEquals(array(), $catalogue->all('domain1')); + $this->assertEquals('en', $catalogue->getLocale()); + $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources()); + } + + /** + * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException + */ + public function testLoadNonExistingResource() + { + $loader = new IniFileLoader(); + $resource = __DIR__.'/../fixtures/non-existing.ini'; + $loader->load($resource, 'en', 'domain1'); + } +} diff --git a/vendor/symfony/translation/Tests/Loader/JsonFileLoaderTest.php b/vendor/symfony/translation/Tests/Loader/JsonFileLoaderTest.php new file mode 100644 index 000000000..cea0aef4b --- /dev/null +++ b/vendor/symfony/translation/Tests/Loader/JsonFileLoaderTest.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Tests\Loader; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\Translation\Loader\JsonFileLoader; + +class JsonFileLoaderTest extends TestCase +{ + public function testLoad() + { + $loader = new JsonFileLoader(); + $resource = __DIR__.'/../fixtures/resources.json'; + $catalogue = $loader->load($resource, 'en', 'domain1'); + + $this->assertEquals(array('foo' => 'bar'), $catalogue->all('domain1')); + $this->assertEquals('en', $catalogue->getLocale()); + $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources()); + } + + public function testLoadDoesNothingIfEmpty() + { + $loader = new JsonFileLoader(); + $resource = __DIR__.'/../fixtures/empty.json'; + $catalogue = $loader->load($resource, 'en', 'domain1'); + + $this->assertEquals(array(), $catalogue->all('domain1')); + $this->assertEquals('en', $catalogue->getLocale()); + $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources()); + } + + /** + * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException + */ + public function testLoadNonExistingResource() + { + $loader = new JsonFileLoader(); + $resource = __DIR__.'/../fixtures/non-existing.json'; + $loader->load($resource, 'en', 'domain1'); + } + + /** + * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException + * @expectedExceptionMessage Error parsing JSON - Syntax error, malformed JSON + */ + public function testParseException() + { + $loader = new JsonFileLoader(); + $resource = __DIR__.'/../fixtures/malformed.json'; + $loader->load($resource, 'en', 'domain1'); + } +} diff --git a/vendor/symfony/translation/Tests/Loader/LocalizedTestCase.php b/vendor/symfony/translation/Tests/Loader/LocalizedTestCase.php new file mode 100644 index 000000000..279e9fde5 --- /dev/null +++ b/vendor/symfony/translation/Tests/Loader/LocalizedTestCase.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Tests\Loader; + +use PHPUnit\Framework\TestCase; + +abstract class LocalizedTestCase extends TestCase +{ + protected function setUp() + { + if (!\extension_loaded('intl')) { + $this->markTestSkipped('Extension intl is required.'); + } + } +} diff --git a/vendor/symfony/translation/Tests/Loader/MoFileLoaderTest.php b/vendor/symfony/translation/Tests/Loader/MoFileLoaderTest.php new file mode 100644 index 000000000..6ad3d44d2 --- /dev/null +++ b/vendor/symfony/translation/Tests/Loader/MoFileLoaderTest.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\Translation\Tests\Loader; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\Translation\Loader\MoFileLoader; + +class MoFileLoaderTest extends TestCase +{ + public function testLoad() + { + $loader = new MoFileLoader(); + $resource = __DIR__.'/../fixtures/resources.mo'; + $catalogue = $loader->load($resource, 'en', 'domain1'); + + $this->assertEquals(array('foo' => 'bar'), $catalogue->all('domain1')); + $this->assertEquals('en', $catalogue->getLocale()); + $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources()); + } + + public function testLoadPlurals() + { + $loader = new MoFileLoader(); + $resource = __DIR__.'/../fixtures/plurals.mo'; + $catalogue = $loader->load($resource, 'en', 'domain1'); + + $this->assertEquals(array('foo' => 'bar', 'foos' => '{0} bar|{1} bars'), $catalogue->all('domain1')); + $this->assertEquals('en', $catalogue->getLocale()); + $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources()); + } + + /** + * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException + */ + public function testLoadNonExistingResource() + { + $loader = new MoFileLoader(); + $resource = __DIR__.'/../fixtures/non-existing.mo'; + $loader->load($resource, 'en', 'domain1'); + } + + /** + * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException + */ + public function testLoadInvalidResource() + { + $loader = new MoFileLoader(); + $resource = __DIR__.'/../fixtures/empty.mo'; + $loader->load($resource, 'en', 'domain1'); + } + + public function testLoadEmptyTranslation() + { + $loader = new MoFileLoader(); + $resource = __DIR__.'/../fixtures/empty-translation.mo'; + $catalogue = $loader->load($resource, 'en', 'message'); + + $this->assertEquals(array(), $catalogue->all('message')); + $this->assertEquals('en', $catalogue->getLocale()); + $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources()); + } +} diff --git a/vendor/symfony/translation/Tests/Loader/PhpFileLoaderTest.php b/vendor/symfony/translation/Tests/Loader/PhpFileLoaderTest.php new file mode 100644 index 000000000..01b7a5fea --- /dev/null +++ b/vendor/symfony/translation/Tests/Loader/PhpFileLoaderTest.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\Translation\Tests\Loader; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\Translation\Loader\PhpFileLoader; + +class PhpFileLoaderTest extends TestCase +{ + public function testLoad() + { + $loader = new PhpFileLoader(); + $resource = __DIR__.'/../fixtures/resources.php'; + $catalogue = $loader->load($resource, 'en', 'domain1'); + + $this->assertEquals(array('foo' => 'bar'), $catalogue->all('domain1')); + $this->assertEquals('en', $catalogue->getLocale()); + $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources()); + } + + /** + * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException + */ + public function testLoadNonExistingResource() + { + $loader = new PhpFileLoader(); + $resource = __DIR__.'/../fixtures/non-existing.php'; + $loader->load($resource, 'en', 'domain1'); + } + + /** + * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException + */ + public function testLoadThrowsAnExceptionIfFileNotLocal() + { + $loader = new PhpFileLoader(); + $resource = 'http://example.com/resources.php'; + $loader->load($resource, 'en', 'domain1'); + } +} diff --git a/vendor/symfony/translation/Tests/Loader/PoFileLoaderTest.php b/vendor/symfony/translation/Tests/Loader/PoFileLoaderTest.php new file mode 100644 index 000000000..4176cb7ff --- /dev/null +++ b/vendor/symfony/translation/Tests/Loader/PoFileLoaderTest.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\Translation\Tests\Loader; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\Translation\Loader\PoFileLoader; + +class PoFileLoaderTest extends TestCase +{ + public function testLoad() + { + $loader = new PoFileLoader(); + $resource = __DIR__.'/../fixtures/resources.po'; + $catalogue = $loader->load($resource, 'en', 'domain1'); + + $this->assertEquals(array('foo' => 'bar'), $catalogue->all('domain1')); + $this->assertEquals('en', $catalogue->getLocale()); + $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources()); + } + + public function testLoadPlurals() + { + $loader = new PoFileLoader(); + $resource = __DIR__.'/../fixtures/plurals.po'; + $catalogue = $loader->load($resource, 'en', 'domain1'); + + $this->assertEquals(array('foo' => 'bar', 'foos' => 'bar|bars'), $catalogue->all('domain1')); + $this->assertEquals('en', $catalogue->getLocale()); + $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources()); + } + + public function testLoadDoesNothingIfEmpty() + { + $loader = new PoFileLoader(); + $resource = __DIR__.'/../fixtures/empty.po'; + $catalogue = $loader->load($resource, 'en', 'domain1'); + + $this->assertEquals(array(), $catalogue->all('domain1')); + $this->assertEquals('en', $catalogue->getLocale()); + $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources()); + } + + /** + * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException + */ + public function testLoadNonExistingResource() + { + $loader = new PoFileLoader(); + $resource = __DIR__.'/../fixtures/non-existing.po'; + $loader->load($resource, 'en', 'domain1'); + } + + public function testLoadEmptyTranslation() + { + $loader = new PoFileLoader(); + $resource = __DIR__.'/../fixtures/empty-translation.po'; + $catalogue = $loader->load($resource, 'en', 'domain1'); + + $this->assertEquals(array('foo' => ''), $catalogue->all('domain1')); + $this->assertEquals('en', $catalogue->getLocale()); + $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources()); + } + + public function testEscapedId() + { + $loader = new PoFileLoader(); + $resource = __DIR__.'/../fixtures/escaped-id.po'; + $catalogue = $loader->load($resource, 'en', 'domain1'); + + $messages = $catalogue->all('domain1'); + $this->assertArrayHasKey('escaped "foo"', $messages); + $this->assertEquals('escaped "bar"', $messages['escaped "foo"']); + } + + public function testEscapedIdPlurals() + { + $loader = new PoFileLoader(); + $resource = __DIR__.'/../fixtures/escaped-id-plurals.po'; + $catalogue = $loader->load($resource, 'en', 'domain1'); + + $messages = $catalogue->all('domain1'); + $this->assertArrayHasKey('escaped "foo"', $messages); + $this->assertArrayHasKey('escaped "foos"', $messages); + $this->assertEquals('escaped "bar"', $messages['escaped "foo"']); + $this->assertEquals('escaped "bar"|escaped "bars"', $messages['escaped "foos"']); + } + + public function testSkipFuzzyTranslations() + { + $loader = new PoFileLoader(); + $resource = __DIR__.'/../fixtures/fuzzy-translations.po'; + $catalogue = $loader->load($resource, 'en', 'domain1'); + + $messages = $catalogue->all('domain1'); + $this->assertArrayHasKey('foo1', $messages); + $this->assertArrayNotHasKey('foo2', $messages); + $this->assertArrayHasKey('foo3', $messages); + } +} diff --git a/vendor/symfony/translation/Tests/Loader/QtFileLoaderTest.php b/vendor/symfony/translation/Tests/Loader/QtFileLoaderTest.php new file mode 100644 index 000000000..8a00fdedc --- /dev/null +++ b/vendor/symfony/translation/Tests/Loader/QtFileLoaderTest.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\Translation\Tests\Loader; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\Translation\Loader\QtFileLoader; + +class QtFileLoaderTest extends TestCase +{ + public function testLoad() + { + $loader = new QtFileLoader(); + $resource = __DIR__.'/../fixtures/resources.ts'; + $catalogue = $loader->load($resource, 'en', 'resources'); + + $this->assertEquals(array('foo' => 'bar'), $catalogue->all('resources')); + $this->assertEquals('en', $catalogue->getLocale()); + $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources()); + } + + /** + * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException + */ + public function testLoadNonExistingResource() + { + $loader = new QtFileLoader(); + $resource = __DIR__.'/../fixtures/non-existing.ts'; + $loader->load($resource, 'en', 'domain1'); + } + + /** + * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException + */ + public function testLoadNonLocalResource() + { + $loader = new QtFileLoader(); + $resource = 'http://domain1.com/resources.ts'; + $loader->load($resource, 'en', 'domain1'); + } + + /** + * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException + */ + public function testLoadInvalidResource() + { + $loader = new QtFileLoader(); + $resource = __DIR__.'/../fixtures/invalid-xml-resources.xlf'; + $loader->load($resource, 'en', 'domain1'); + } + + public function testLoadEmptyResource() + { + $loader = new QtFileLoader(); + $resource = __DIR__.'/../fixtures/empty.xlf'; + + if (method_exists($this, 'expectException')) { + $this->expectException('Symfony\Component\Translation\Exception\InvalidResourceException'); + $this->expectExceptionMessage(sprintf('Unable to load "%s".', $resource)); + } else { + $this->setExpectedException('Symfony\Component\Translation\Exception\InvalidResourceException', sprintf('Unable to load "%s".', $resource)); + } + + $loader->load($resource, 'en', 'domain1'); + } +} diff --git a/vendor/symfony/translation/Tests/Loader/XliffFileLoaderTest.php b/vendor/symfony/translation/Tests/Loader/XliffFileLoaderTest.php new file mode 100644 index 000000000..94406fd5f --- /dev/null +++ b/vendor/symfony/translation/Tests/Loader/XliffFileLoaderTest.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\Translation\Tests\Loader; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\Translation\Loader\XliffFileLoader; + +class XliffFileLoaderTest extends TestCase +{ + public function testLoad() + { + $loader = new XliffFileLoader(); + $resource = __DIR__.'/../fixtures/resources.xlf'; + $catalogue = $loader->load($resource, 'en', 'domain1'); + + $this->assertEquals('en', $catalogue->getLocale()); + $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources()); + $this->assertSame(array(), libxml_get_errors()); + $this->assertContainsOnly('string', $catalogue->all('domain1')); + } + + public function testLoadWithInternalErrorsEnabled() + { + $internalErrors = libxml_use_internal_errors(true); + + $this->assertSame(array(), libxml_get_errors()); + + $loader = new XliffFileLoader(); + $resource = __DIR__.'/../fixtures/resources.xlf'; + $catalogue = $loader->load($resource, 'en', 'domain1'); + + $this->assertEquals('en', $catalogue->getLocale()); + $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources()); + $this->assertSame(array(), libxml_get_errors()); + + libxml_clear_errors(); + libxml_use_internal_errors($internalErrors); + } + + public function testLoadWithExternalEntitiesDisabled() + { + $disableEntities = libxml_disable_entity_loader(true); + + $loader = new XliffFileLoader(); + $resource = __DIR__.'/../fixtures/resources.xlf'; + $catalogue = $loader->load($resource, 'en', 'domain1'); + + libxml_disable_entity_loader($disableEntities); + + $this->assertEquals('en', $catalogue->getLocale()); + $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources()); + } + + public function testLoadWithResname() + { + $loader = new XliffFileLoader(); + $catalogue = $loader->load(__DIR__.'/../fixtures/resname.xlf', 'en', 'domain1'); + + $this->assertEquals(array('foo' => 'bar', 'bar' => 'baz', 'baz' => 'foo'), $catalogue->all('domain1')); + } + + public function testIncompleteResource() + { + $loader = new XliffFileLoader(); + $catalogue = $loader->load(__DIR__.'/../fixtures/resources.xlf', 'en', 'domain1'); + + $this->assertEquals(array('foo' => 'bar', 'extra' => 'extra', 'key' => '', 'test' => 'with'), $catalogue->all('domain1')); + } + + public function testEncoding() + { + $loader = new XliffFileLoader(); + $catalogue = $loader->load(__DIR__.'/../fixtures/encoding.xlf', 'en', 'domain1'); + + $this->assertEquals(utf8_decode('föö'), $catalogue->get('bar', 'domain1')); + $this->assertEquals(utf8_decode('bär'), $catalogue->get('foo', 'domain1')); + $this->assertEquals(array('notes' => array(array('content' => utf8_decode('bäz'))), 'id' => '1'), $catalogue->getMetadata('foo', 'domain1')); + } + + public function testTargetAttributesAreStoredCorrectly() + { + $loader = new XliffFileLoader(); + $catalogue = $loader->load(__DIR__.'/../fixtures/with-attributes.xlf', 'en', 'domain1'); + + $metadata = $catalogue->getMetadata('foo', 'domain1'); + $this->assertEquals('translated', $metadata['target-attributes']['state']); + } + + /** + * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException + */ + public function testLoadInvalidResource() + { + $loader = new XliffFileLoader(); + $loader->load(__DIR__.'/../fixtures/resources.php', 'en', 'domain1'); + } + + /** + * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException + */ + public function testLoadResourceDoesNotValidate() + { + $loader = new XliffFileLoader(); + $loader->load(__DIR__.'/../fixtures/non-valid.xlf', 'en', 'domain1'); + } + + /** + * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException + */ + public function testLoadNonExistingResource() + { + $loader = new XliffFileLoader(); + $resource = __DIR__.'/../fixtures/non-existing.xlf'; + $loader->load($resource, 'en', 'domain1'); + } + + /** + * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException + */ + public function testLoadThrowsAnExceptionIfFileNotLocal() + { + $loader = new XliffFileLoader(); + $resource = 'http://example.com/resources.xlf'; + $loader->load($resource, 'en', 'domain1'); + } + + /** + * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException + * @expectedExceptionMessage Document types are not allowed. + */ + public function testDocTypeIsNotAllowed() + { + $loader = new XliffFileLoader(); + $loader->load(__DIR__.'/../fixtures/withdoctype.xlf', 'en', 'domain1'); + } + + public function testParseEmptyFile() + { + $loader = new XliffFileLoader(); + $resource = __DIR__.'/../fixtures/empty.xlf'; + + if (method_exists($this, 'expectException')) { + $this->expectException('Symfony\Component\Translation\Exception\InvalidResourceException'); + $this->expectExceptionMessage(sprintf('Unable to load "%s":', $resource)); + } else { + $this->setExpectedException('Symfony\Component\Translation\Exception\InvalidResourceException', sprintf('Unable to load "%s":', $resource)); + } + + $loader->load($resource, 'en', 'domain1'); + } + + public function testLoadNotes() + { + $loader = new XliffFileLoader(); + $catalogue = $loader->load(__DIR__.'/../fixtures/withnote.xlf', 'en', 'domain1'); + + $this->assertEquals(array('notes' => array(array('priority' => 1, 'content' => 'foo')), 'id' => '1'), $catalogue->getMetadata('foo', 'domain1')); + // message without target + $this->assertEquals(array('notes' => array(array('content' => 'bar', 'from' => 'foo')), 'id' => '2'), $catalogue->getMetadata('extra', 'domain1')); + // message with empty target + $this->assertEquals(array('notes' => array(array('content' => 'baz'), array('priority' => 2, 'from' => 'bar', 'content' => 'qux')), 'id' => '123'), $catalogue->getMetadata('key', 'domain1')); + } + + public function testLoadVersion2() + { + $loader = new XliffFileLoader(); + $resource = __DIR__.'/../fixtures/resources-2.0.xlf'; + $catalogue = $loader->load($resource, 'en', 'domain1'); + + $this->assertEquals('en', $catalogue->getLocale()); + $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources()); + $this->assertSame(array(), libxml_get_errors()); + + $domains = $catalogue->all(); + $this->assertCount(3, $domains['domain1']); + $this->assertContainsOnly('string', $catalogue->all('domain1')); + + // target attributes + $this->assertEquals(array('target-attributes' => array('order' => 1)), $catalogue->getMetadata('bar', 'domain1')); + } + + public function testLoadVersion2WithNoteMeta() + { + $loader = new XliffFileLoader(); + $resource = __DIR__.'/../fixtures/resources-notes-meta.xlf'; + $catalogue = $loader->load($resource, 'en', 'domain1'); + + $this->assertEquals('en', $catalogue->getLocale()); + $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources()); + $this->assertSame(array(), libxml_get_errors()); + + // test for "foo" metadata + $this->assertTrue($catalogue->defines('foo', 'domain1')); + $metadata = $catalogue->getMetadata('foo', 'domain1'); + $this->assertNotEmpty($metadata); + $this->assertCount(3, $metadata['notes']); + + $this->assertEquals('state', $metadata['notes'][0]['category']); + $this->assertEquals('new', $metadata['notes'][0]['content']); + + $this->assertEquals('approved', $metadata['notes'][1]['category']); + $this->assertEquals('true', $metadata['notes'][1]['content']); + + $this->assertEquals('section', $metadata['notes'][2]['category']); + $this->assertEquals('1', $metadata['notes'][2]['priority']); + $this->assertEquals('user login', $metadata['notes'][2]['content']); + + // test for "baz" metadata + $this->assertTrue($catalogue->defines('baz', 'domain1')); + $metadata = $catalogue->getMetadata('baz', 'domain1'); + $this->assertNotEmpty($metadata); + $this->assertCount(2, $metadata['notes']); + + $this->assertEquals('x', $metadata['notes'][0]['id']); + $this->assertEquals('x_content', $metadata['notes'][0]['content']); + + $this->assertEquals('target', $metadata['notes'][1]['appliesTo']); + $this->assertEquals('quality', $metadata['notes'][1]['category']); + $this->assertEquals('Fuzzy', $metadata['notes'][1]['content']); + } + + public function testLoadVersion2WithMultiSegmentUnit() + { + $loader = new XliffFileLoader(); + $resource = __DIR__.'/../fixtures/resources-2.0-multi-segment-unit.xlf'; + $catalog = $loader->load($resource, 'en', 'domain1'); + + $this->assertSame('en', $catalog->getLocale()); + $this->assertEquals(array(new FileResource($resource)), $catalog->getResources()); + $this->assertFalse(libxml_get_last_error()); + + // test for "foo" metadata + $this->assertTrue($catalog->defines('foo', 'domain1')); + $metadata = $catalog->getMetadata('foo', 'domain1'); + $this->assertNotEmpty($metadata); + $this->assertCount(1, $metadata['notes']); + + $this->assertSame('processed', $metadata['notes'][0]['category']); + $this->assertSame('true', $metadata['notes'][0]['content']); + + // test for "bar" metadata + $this->assertTrue($catalog->defines('bar', 'domain1')); + $metadata = $catalog->getMetadata('bar', 'domain1'); + $this->assertNotEmpty($metadata); + $this->assertCount(1, $metadata['notes']); + + $this->assertSame('processed', $metadata['notes'][0]['category']); + $this->assertSame('true', $metadata['notes'][0]['content']); + } +} diff --git a/vendor/symfony/translation/Tests/Loader/YamlFileLoaderTest.php b/vendor/symfony/translation/Tests/Loader/YamlFileLoaderTest.php new file mode 100644 index 000000000..bb3c1a99a --- /dev/null +++ b/vendor/symfony/translation/Tests/Loader/YamlFileLoaderTest.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Tests\Loader; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\Translation\Loader\YamlFileLoader; + +class YamlFileLoaderTest extends TestCase +{ + public function testLoad() + { + $loader = new YamlFileLoader(); + $resource = __DIR__.'/../fixtures/resources.yml'; + $catalogue = $loader->load($resource, 'en', 'domain1'); + + $this->assertEquals(array('foo' => 'bar'), $catalogue->all('domain1')); + $this->assertEquals('en', $catalogue->getLocale()); + $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources()); + } + + public function testLoadDoesNothingIfEmpty() + { + $loader = new YamlFileLoader(); + $resource = __DIR__.'/../fixtures/empty.yml'; + $catalogue = $loader->load($resource, 'en', 'domain1'); + + $this->assertEquals(array(), $catalogue->all('domain1')); + $this->assertEquals('en', $catalogue->getLocale()); + $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources()); + } + + /** + * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException + */ + public function testLoadNonExistingResource() + { + $loader = new YamlFileLoader(); + $resource = __DIR__.'/../fixtures/non-existing.yml'; + $loader->load($resource, 'en', 'domain1'); + } + + /** + * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException + */ + public function testLoadThrowsAnExceptionIfFileNotLocal() + { + $loader = new YamlFileLoader(); + $resource = 'http://example.com/resources.yml'; + $loader->load($resource, 'en', 'domain1'); + } + + /** + * @expectedException \Symfony\Component\Translation\Exception\InvalidResourceException + */ + public function testLoadThrowsAnExceptionIfNotAnArray() + { + $loader = new YamlFileLoader(); + $resource = __DIR__.'/../fixtures/non-valid.yml'; + $loader->load($resource, 'en', 'domain1'); + } +} diff --git a/vendor/symfony/translation/Tests/LoggingTranslatorTest.php b/vendor/symfony/translation/Tests/LoggingTranslatorTest.php new file mode 100644 index 000000000..022379e93 --- /dev/null +++ b/vendor/symfony/translation/Tests/LoggingTranslatorTest.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\Translation\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Translation\Loader\ArrayLoader; +use Symfony\Component\Translation\LoggingTranslator; +use Symfony\Component\Translation\Translator; + +class LoggingTranslatorTest extends TestCase +{ + public function testTransWithNoTranslationIsLogged() + { + $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); + $logger->expects($this->exactly(2)) + ->method('warning') + ->with('Translation not found.') + ; + + $translator = new Translator('ar'); + $loggableTranslator = new LoggingTranslator($translator, $logger); + $loggableTranslator->transChoice('some_message2', 10, array('%count%' => 10)); + $loggableTranslator->trans('bar'); + } + + public function testTransChoiceFallbackIsLogged() + { + $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); + $logger->expects($this->once()) + ->method('debug') + ->with('Translation use fallback catalogue.') + ; + + $translator = new Translator('ar'); + $translator->setFallbackLocales(array('en')); + $translator->addLoader('array', new ArrayLoader()); + $translator->addResource('array', array('some_message2' => 'one thing|%count% things'), 'en'); + $loggableTranslator = new LoggingTranslator($translator, $logger); + $loggableTranslator->transChoice('some_message2', 10, array('%count%' => 10)); + } +} diff --git a/vendor/symfony/translation/Tests/MessageCatalogueTest.php b/vendor/symfony/translation/Tests/MessageCatalogueTest.php new file mode 100644 index 000000000..1ab824696 --- /dev/null +++ b/vendor/symfony/translation/Tests/MessageCatalogueTest.php @@ -0,0 +1,222 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Translation\MessageCatalogue; + +class MessageCatalogueTest extends TestCase +{ + public function testGetLocale() + { + $catalogue = new MessageCatalogue('en'); + + $this->assertEquals('en', $catalogue->getLocale()); + } + + public function testGetDomains() + { + $catalogue = new MessageCatalogue('en', array('domain1' => array(), 'domain2' => array())); + + $this->assertEquals(array('domain1', 'domain2'), $catalogue->getDomains()); + } + + public function testAll() + { + $catalogue = new MessageCatalogue('en', $messages = array('domain1' => array('foo' => 'foo'), 'domain2' => array('bar' => 'bar'))); + + $this->assertEquals(array('foo' => 'foo'), $catalogue->all('domain1')); + $this->assertEquals(array(), $catalogue->all('domain88')); + $this->assertEquals($messages, $catalogue->all()); + } + + public function testHas() + { + $catalogue = new MessageCatalogue('en', array('domain1' => array('foo' => 'foo'), 'domain2' => array('bar' => 'bar'))); + + $this->assertTrue($catalogue->has('foo', 'domain1')); + $this->assertFalse($catalogue->has('bar', 'domain1')); + $this->assertFalse($catalogue->has('foo', 'domain88')); + } + + public function testGetSet() + { + $catalogue = new MessageCatalogue('en', array('domain1' => array('foo' => 'foo'), 'domain2' => array('bar' => 'bar'))); + $catalogue->set('foo1', 'foo1', 'domain1'); + + $this->assertEquals('foo', $catalogue->get('foo', 'domain1')); + $this->assertEquals('foo1', $catalogue->get('foo1', 'domain1')); + } + + public function testAdd() + { + $catalogue = new MessageCatalogue('en', array('domain1' => array('foo' => 'foo'), 'domain2' => array('bar' => 'bar'))); + $catalogue->add(array('foo1' => 'foo1'), 'domain1'); + + $this->assertEquals('foo', $catalogue->get('foo', 'domain1')); + $this->assertEquals('foo1', $catalogue->get('foo1', 'domain1')); + + $catalogue->add(array('foo' => 'bar'), 'domain1'); + $this->assertEquals('bar', $catalogue->get('foo', 'domain1')); + $this->assertEquals('foo1', $catalogue->get('foo1', 'domain1')); + + $catalogue->add(array('foo' => 'bar'), 'domain88'); + $this->assertEquals('bar', $catalogue->get('foo', 'domain88')); + } + + public function testReplace() + { + $catalogue = new MessageCatalogue('en', array('domain1' => array('foo' => 'foo'), 'domain2' => array('bar' => 'bar'))); + $catalogue->replace($messages = array('foo1' => 'foo1'), 'domain1'); + + $this->assertEquals($messages, $catalogue->all('domain1')); + } + + public function testAddCatalogue() + { + $r = $this->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')->getMock(); + $r->expects($this->any())->method('__toString')->will($this->returnValue('r')); + + $r1 = $this->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')->getMock(); + $r1->expects($this->any())->method('__toString')->will($this->returnValue('r1')); + + $catalogue = new MessageCatalogue('en', array('domain1' => array('foo' => 'foo'), 'domain2' => array('bar' => 'bar'))); + $catalogue->addResource($r); + + $catalogue1 = new MessageCatalogue('en', array('domain1' => array('foo1' => 'foo1'))); + $catalogue1->addResource($r1); + + $catalogue->addCatalogue($catalogue1); + + $this->assertEquals('foo', $catalogue->get('foo', 'domain1')); + $this->assertEquals('foo1', $catalogue->get('foo1', 'domain1')); + + $this->assertEquals(array($r, $r1), $catalogue->getResources()); + } + + public function testAddFallbackCatalogue() + { + $r = $this->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')->getMock(); + $r->expects($this->any())->method('__toString')->will($this->returnValue('r')); + + $r1 = $this->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')->getMock(); + $r1->expects($this->any())->method('__toString')->will($this->returnValue('r1')); + + $r2 = $this->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')->getMock(); + $r2->expects($this->any())->method('__toString')->will($this->returnValue('r2')); + + $catalogue = new MessageCatalogue('fr_FR', array('domain1' => array('foo' => 'foo'), 'domain2' => array('bar' => 'bar'))); + $catalogue->addResource($r); + + $catalogue1 = new MessageCatalogue('fr', array('domain1' => array('foo' => 'bar', 'foo1' => 'foo1'))); + $catalogue1->addResource($r1); + + $catalogue2 = new MessageCatalogue('en'); + $catalogue2->addResource($r2); + + $catalogue->addFallbackCatalogue($catalogue1); + $catalogue1->addFallbackCatalogue($catalogue2); + + $this->assertEquals('foo', $catalogue->get('foo', 'domain1')); + $this->assertEquals('foo1', $catalogue->get('foo1', 'domain1')); + + $this->assertEquals(array($r, $r1, $r2), $catalogue->getResources()); + } + + /** + * @expectedException \Symfony\Component\Translation\Exception\LogicException + */ + public function testAddFallbackCatalogueWithParentCircularReference() + { + $main = new MessageCatalogue('en_US'); + $fallback = new MessageCatalogue('fr_FR'); + + $fallback->addFallbackCatalogue($main); + $main->addFallbackCatalogue($fallback); + } + + /** + * @expectedException \Symfony\Component\Translation\Exception\LogicException + */ + public function testAddFallbackCatalogueWithFallbackCircularReference() + { + $fr = new MessageCatalogue('fr'); + $en = new MessageCatalogue('en'); + $es = new MessageCatalogue('es'); + + $fr->addFallbackCatalogue($en); + $es->addFallbackCatalogue($en); + $en->addFallbackCatalogue($fr); + } + + /** + * @expectedException \Symfony\Component\Translation\Exception\LogicException + */ + public function testAddCatalogueWhenLocaleIsNotTheSameAsTheCurrentOne() + { + $catalogue = new MessageCatalogue('en'); + $catalogue->addCatalogue(new MessageCatalogue('fr', array())); + } + + public function testGetAddResource() + { + $catalogue = new MessageCatalogue('en'); + $r = $this->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')->getMock(); + $r->expects($this->any())->method('__toString')->will($this->returnValue('r')); + $catalogue->addResource($r); + $catalogue->addResource($r); + $r1 = $this->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')->getMock(); + $r1->expects($this->any())->method('__toString')->will($this->returnValue('r1')); + $catalogue->addResource($r1); + + $this->assertEquals(array($r, $r1), $catalogue->getResources()); + } + + public function testMetadataDelete() + { + $catalogue = new MessageCatalogue('en'); + $this->assertEquals(array(), $catalogue->getMetadata('', ''), 'Metadata is empty'); + $catalogue->deleteMetadata('key', 'messages'); + $catalogue->deleteMetadata('', 'messages'); + $catalogue->deleteMetadata(); + } + + public function testMetadataSetGetDelete() + { + $catalogue = new MessageCatalogue('en'); + $catalogue->setMetadata('key', 'value'); + $this->assertEquals('value', $catalogue->getMetadata('key', 'messages'), "Metadata 'key' = 'value'"); + + $catalogue->setMetadata('key2', array()); + $this->assertEquals(array(), $catalogue->getMetadata('key2', 'messages'), 'Metadata key2 is array'); + + $catalogue->deleteMetadata('key2', 'messages'); + $this->assertNull($catalogue->getMetadata('key2', 'messages'), 'Metadata key2 should is deleted.'); + + $catalogue->deleteMetadata('key2', 'domain'); + $this->assertNull($catalogue->getMetadata('key2', 'domain'), 'Metadata key2 should is deleted.'); + } + + public function testMetadataMerge() + { + $cat1 = new MessageCatalogue('en'); + $cat1->setMetadata('a', 'b'); + $this->assertEquals(array('messages' => array('a' => 'b')), $cat1->getMetadata('', ''), 'Cat1 contains messages metadata.'); + + $cat2 = new MessageCatalogue('en'); + $cat2->setMetadata('b', 'c', 'domain'); + $this->assertEquals(array('domain' => array('b' => 'c')), $cat2->getMetadata('', ''), 'Cat2 contains domain metadata.'); + + $cat1->addCatalogue($cat2); + $this->assertEquals(array('messages' => array('a' => 'b'), 'domain' => array('b' => 'c')), $cat1->getMetadata('', ''), 'Cat1 contains merged metadata.'); + } +} diff --git a/vendor/symfony/translation/Tests/MessageSelectorTest.php b/vendor/symfony/translation/Tests/MessageSelectorTest.php new file mode 100644 index 000000000..42b7e0a3f --- /dev/null +++ b/vendor/symfony/translation/Tests/MessageSelectorTest.php @@ -0,0 +1,137 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Translation\MessageSelector; + +class MessageSelectorTest extends TestCase +{ + /** + * @dataProvider getChooseTests + */ + public function testChoose($expected, $id, $number) + { + $selector = new MessageSelector(); + + $this->assertEquals($expected, $selector->choose($id, $number, 'en')); + } + + public function testReturnMessageIfExactlyOneStandardRuleIsGiven() + { + $selector = new MessageSelector(); + + $this->assertEquals('There are two apples', $selector->choose('There are two apples', 2, 'en')); + } + + /** + * @dataProvider getNonMatchingMessages + * @expectedException \Symfony\Component\Translation\Exception\InvalidArgumentException + */ + public function testThrowExceptionIfMatchingMessageCannotBeFound($id, $number) + { + $selector = new MessageSelector(); + + $selector->choose($id, $number, 'en'); + } + + public function getNonMatchingMessages() + { + return array( + array('{0} There are no apples|{1} There is one apple', 2), + array('{1} There is one apple|]1,Inf] There are %count% apples', 0), + array('{1} There is one apple|]2,Inf] There are %count% apples', 2), + array('{0} There are no apples|There is one apple', 2), + ); + } + + public function getChooseTests() + { + return array( + array('There are no apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 0), + array('There are no apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 0), + array('There are no apples', '{0}There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 0), + + array('There is one apple', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 1), + + array('There are %count% apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 10), + array('There are %count% apples', '{0} There are no apples|{1} There is one apple|]1,Inf]There are %count% apples', 10), + array('There are %count% apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 10), + + array('There are %count% apples', 'There is one apple|There are %count% apples', 0), + array('There is one apple', 'There is one apple|There are %count% apples', 1), + array('There are %count% apples', 'There is one apple|There are %count% apples', 10), + + array('There are %count% apples', 'one: There is one apple|more: There are %count% apples', 0), + array('There is one apple', 'one: There is one apple|more: There are %count% apples', 1), + array('There are %count% apples', 'one: There is one apple|more: There are %count% apples', 10), + + array('There are no apples', '{0} There are no apples|one: There is one apple|more: There are %count% apples', 0), + array('There is one apple', '{0} There are no apples|one: There is one apple|more: There are %count% apples', 1), + array('There are %count% apples', '{0} There are no apples|one: There is one apple|more: There are %count% apples', 10), + + array('', '{0}|{1} There is one apple|]1,Inf] There are %count% apples', 0), + array('', '{0} There are no apples|{1}|]1,Inf] There are %count% apples', 1), + + // Indexed only tests which are Gettext PoFile* compatible strings. + array('There are %count% apples', 'There is one apple|There are %count% apples', 0), + array('There is one apple', 'There is one apple|There are %count% apples', 1), + array('There are %count% apples', 'There is one apple|There are %count% apples', 2), + + // Tests for float numbers + array('There is almost one apple', '{0} There are no apples|]0,1[ There is almost one apple|{1} There is one apple|[1,Inf] There is more than one apple', 0.7), + array('There is one apple', '{0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 1), + array('There is more than one apple', '{0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 1.7), + array('There are no apples', '{0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 0), + array('There are no apples', '{0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 0.0), + array('There are no apples', '{0.0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 0), + + // Test texts with new-lines + // with double-quotes and \n in id & double-quotes and actual newlines in text + array("This is a text with a\n new-line in it. Selector = 0.", '{0}This is a text with a + new-line in it. Selector = 0.|{1}This is a text with a + new-line in it. Selector = 1.|[1,Inf]This is a text with a + new-line in it. Selector > 1.', 0), + // with double-quotes and \n in id and single-quotes and actual newlines in text + array("This is a text with a\n new-line in it. Selector = 1.", '{0}This is a text with a + new-line in it. Selector = 0.|{1}This is a text with a + new-line in it. Selector = 1.|[1,Inf]This is a text with a + new-line in it. Selector > 1.', 1), + array("This is a text with a\n new-line in it. Selector > 1.", '{0}This is a text with a + new-line in it. Selector = 0.|{1}This is a text with a + new-line in it. Selector = 1.|[1,Inf]This is a text with a + new-line in it. Selector > 1.', 5), + // with double-quotes and id split accros lines + array('This is a text with a + new-line in it. Selector = 1.', '{0}This is a text with a + new-line in it. Selector = 0.|{1}This is a text with a + new-line in it. Selector = 1.|[1,Inf]This is a text with a + new-line in it. Selector > 1.', 1), + // with single-quotes and id split accros lines + array('This is a text with a + new-line in it. Selector > 1.', '{0}This is a text with a + new-line in it. Selector = 0.|{1}This is a text with a + new-line in it. Selector = 1.|[1,Inf]This is a text with a + new-line in it. Selector > 1.', 5), + // with single-quotes and \n in text + array('This is a text with a\nnew-line in it. Selector = 0.', '{0}This is a text with a\nnew-line in it. Selector = 0.|{1}This is a text with a\nnew-line in it. Selector = 1.|[1,Inf]This is a text with a\nnew-line in it. Selector > 1.', 0), + // with double-quotes and id split accros lines + array("This is a text with a\nnew-line in it. Selector = 1.", "{0}This is a text with a\nnew-line in it. Selector = 0.|{1}This is a text with a\nnew-line in it. Selector = 1.|[1,Inf]This is a text with a\nnew-line in it. Selector > 1.", 1), + // esacape pipe + array('This is a text with | in it. Selector = 0.', '{0}This is a text with || in it. Selector = 0.|{1}This is a text with || in it. Selector = 1.', 0), + // Empty plural set (2 plural forms) from a .PO file + array('', '|', 1), + // Empty plural set (3 plural forms) from a .PO file + array('', '||', 1), + ); + } +} diff --git a/vendor/symfony/translation/Tests/PluralizationRulesTest.php b/vendor/symfony/translation/Tests/PluralizationRulesTest.php new file mode 100644 index 000000000..5eb6c01f5 --- /dev/null +++ b/vendor/symfony/translation/Tests/PluralizationRulesTest.php @@ -0,0 +1,122 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Translation\PluralizationRules; + +/** + * Test should cover all languages mentioned on http://translate.sourceforge.net/wiki/l10n/pluralforms + * and Plural forms mentioned on http://www.gnu.org/software/gettext/manual/gettext.html#Plural-forms. + * + * See also https://developer.mozilla.org/en/Localization_and_Plurals which mentions 15 rules having a maximum of 6 forms. + * The mozilla code is also interesting to check for. + * + * As mentioned by chx http://drupal.org/node/1273968 we can cover all by testing number from 0 to 199 + * + * The goal to cover all languages is to far fetched so this test case is smaller. + * + * @author Clemens Tolboom clemens@build2be.nl + */ +class PluralizationRulesTest extends TestCase +{ + /** + * We test failed langcode here. + * + * TODO: The languages mentioned in the data provide need to get fixed somehow within PluralizationRules. + * + * @dataProvider failingLangcodes + */ + public function testFailedLangcodes($nplural, $langCodes) + { + $matrix = $this->generateTestData($langCodes); + $this->validateMatrix($nplural, $matrix, false); + } + + /** + * @dataProvider successLangcodes + */ + public function testLangcodes($nplural, $langCodes) + { + $matrix = $this->generateTestData($langCodes); + $this->validateMatrix($nplural, $matrix); + } + + /** + * This array should contain all currently known langcodes. + * + * As it is impossible to have this ever complete we should try as hard as possible to have it almost complete. + * + * @return array + */ + public function successLangcodes() + { + return array( + array('1', array('ay', 'bo', 'cgg', 'dz', 'id', 'ja', 'jbo', 'ka', 'kk', 'km', 'ko', 'ky')), + array('2', array('nl', 'fr', 'en', 'de', 'de_GE', 'hy', 'hy_AM')), + array('3', array('be', 'bs', 'cs', 'hr')), + array('4', array('cy', 'mt', 'sl')), + array('6', array('ar')), + ); + } + + /** + * This array should be at least empty within the near future. + * + * This both depends on a complete list trying to add above as understanding + * the plural rules of the current failing languages. + * + * @return array with nplural together with langcodes + */ + public function failingLangcodes() + { + return array( + array('1', array('fa')), + array('2', array('jbo')), + array('3', array('cbs')), + array('4', array('gd', 'kw')), + array('5', array('ga')), + ); + } + + /** + * We validate only on the plural coverage. Thus the real rules is not tested. + * + * @param string $nplural Plural expected + * @param array $matrix Containing langcodes and their plural index values + * @param bool $expectSuccess + */ + protected function validateMatrix($nplural, $matrix, $expectSuccess = true) + { + foreach ($matrix as $langCode => $data) { + $indexes = array_flip($data); + if ($expectSuccess) { + $this->assertEquals($nplural, \count($indexes), "Langcode '$langCode' has '$nplural' plural forms."); + } else { + $this->assertNotEquals((int) $nplural, \count($indexes), "Langcode '$langCode' has '$nplural' plural forms."); + } + } + } + + protected function generateTestData($langCodes) + { + $matrix = array(); + foreach ($langCodes as $langCode) { + for ($count = 0; $count < 200; ++$count) { + $plural = PluralizationRules::get($count, $langCode); + $matrix[$langCode][$count] = $plural; + } + } + + return $matrix; + } +} diff --git a/vendor/symfony/translation/Tests/TranslatorCacheTest.php b/vendor/symfony/translation/Tests/TranslatorCacheTest.php new file mode 100644 index 000000000..3e71ae74e --- /dev/null +++ b/vendor/symfony/translation/Tests/TranslatorCacheTest.php @@ -0,0 +1,311 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Resource\SelfCheckingResourceInterface; +use Symfony\Component\Translation\Loader\ArrayLoader; +use Symfony\Component\Translation\Loader\LoaderInterface; +use Symfony\Component\Translation\MessageCatalogue; +use Symfony\Component\Translation\Translator; + +class TranslatorCacheTest extends TestCase +{ + protected $tmpDir; + + protected function setUp() + { + $this->tmpDir = sys_get_temp_dir().'/sf2_translation'; + $this->deleteTmpDir(); + } + + protected function tearDown() + { + $this->deleteTmpDir(); + } + + protected function deleteTmpDir() + { + if (!file_exists($dir = $this->tmpDir)) { + return; + } + + $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->tmpDir), \RecursiveIteratorIterator::CHILD_FIRST); + foreach ($iterator as $path) { + if (preg_match('#[/\\\\]\.\.?$#', $path->__toString())) { + continue; + } + if ($path->isDir()) { + rmdir($path->__toString()); + } else { + unlink($path->__toString()); + } + } + rmdir($this->tmpDir); + } + + /** + * @dataProvider runForDebugAndProduction + */ + public function testThatACacheIsUsed($debug) + { + $locale = 'any_locale'; + $format = 'some_format'; + $msgid = 'test'; + + // Prime the cache + $translator = new Translator($locale, null, $this->tmpDir, $debug); + $translator->addLoader($format, new ArrayLoader()); + $translator->addResource($format, array($msgid => 'OK'), $locale); + $translator->trans($msgid); + + // Try again and see we get a valid result whilst no loader can be used + $translator = new Translator($locale, null, $this->tmpDir, $debug); + $translator->addLoader($format, $this->createFailingLoader()); + $translator->addResource($format, array($msgid => 'OK'), $locale); + $this->assertEquals('OK', $translator->trans($msgid), '-> caching does not work in '.($debug ? 'debug' : 'production')); + } + + public function testCatalogueIsReloadedWhenResourcesAreNoLongerFresh() + { + /* + * The testThatACacheIsUsed() test showed that we don't need the loader as long as the cache + * is fresh. + * + * Now we add a Resource that is never fresh and make sure that the + * cache is discarded (the loader is called twice). + * + * We need to run this for debug=true only because in production the cache + * will never be revalidated. + */ + + $locale = 'any_locale'; + $format = 'some_format'; + $msgid = 'test'; + + $catalogue = new MessageCatalogue($locale, array()); + $catalogue->addResource(new StaleResource()); // better use a helper class than a mock, because it gets serialized in the cache and re-loaded + + /** @var LoaderInterface|\PHPUnit_Framework_MockObject_MockObject $loader */ + $loader = $this->getMockBuilder('Symfony\Component\Translation\Loader\LoaderInterface')->getMock(); + $loader + ->expects($this->exactly(2)) + ->method('load') + ->will($this->returnValue($catalogue)) + ; + + // 1st pass + $translator = new Translator($locale, null, $this->tmpDir, true); + $translator->addLoader($format, $loader); + $translator->addResource($format, null, $locale); + $translator->trans($msgid); + + // 2nd pass + $translator = new Translator($locale, null, $this->tmpDir, true); + $translator->addLoader($format, $loader); + $translator->addResource($format, null, $locale); + $translator->trans($msgid); + } + + /** + * @dataProvider runForDebugAndProduction + */ + public function testDifferentTranslatorsForSameLocaleDoNotOverwriteEachOthersCache($debug) + { + /* + * Similar to the previous test. After we used the second translator, make + * sure there's still a useable cache for the first one. + */ + + $locale = 'any_locale'; + $format = 'some_format'; + $msgid = 'test'; + + // Create a Translator and prime its cache + $translator = new Translator($locale, null, $this->tmpDir, $debug); + $translator->addLoader($format, new ArrayLoader()); + $translator->addResource($format, array($msgid => 'OK'), $locale); + $translator->trans($msgid); + + // Create another Translator with a different catalogue for the same locale + $translator = new Translator($locale, null, $this->tmpDir, $debug); + $translator->addLoader($format, new ArrayLoader()); + $translator->addResource($format, array($msgid => 'FAIL'), $locale); + $translator->trans($msgid); + + // Now the first translator must still have a useable cache. + $translator = new Translator($locale, null, $this->tmpDir, $debug); + $translator->addLoader($format, $this->createFailingLoader()); + $translator->addResource($format, array($msgid => 'OK'), $locale); + $this->assertEquals('OK', $translator->trans($msgid), '-> the cache was overwritten by another translator instance in '.($debug ? 'debug' : 'production')); + } + + public function testGeneratedCacheFilesAreOnlyBelongRequestedLocales() + { + $translator = new Translator('a', null, $this->tmpDir); + $translator->setFallbackLocales(array('b')); + $translator->trans('bar'); + + $cachedFiles = glob($this->tmpDir.'/*.php'); + + $this->assertCount(1, $cachedFiles); + } + + public function testDifferentCacheFilesAreUsedForDifferentSetsOfFallbackLocales() + { + /* + * Because the cache file contains a catalogue including all of its fallback + * catalogues, we must take the set of fallback locales into consideration when + * loading a catalogue from the cache. + */ + $translator = new Translator('a', null, $this->tmpDir); + $translator->setFallbackLocales(array('b')); + + $translator->addLoader('array', new ArrayLoader()); + $translator->addResource('array', array('foo' => 'foo (a)'), 'a'); + $translator->addResource('array', array('bar' => 'bar (b)'), 'b'); + + $this->assertEquals('bar (b)', $translator->trans('bar')); + + // Remove fallback locale + $translator->setFallbackLocales(array()); + $this->assertEquals('bar', $translator->trans('bar')); + + // Use a fresh translator with no fallback locales, result should be the same + $translator = new Translator('a', null, $this->tmpDir); + + $translator->addLoader('array', new ArrayLoader()); + $translator->addResource('array', array('foo' => 'foo (a)'), 'a'); + $translator->addResource('array', array('bar' => 'bar (b)'), 'b'); + + $this->assertEquals('bar', $translator->trans('bar')); + } + + public function testPrimaryAndFallbackCataloguesContainTheSameMessagesRegardlessOfCaching() + { + /* + * As a safeguard against potential BC breaks, make sure that primary and fallback + * catalogues (reachable via getFallbackCatalogue()) always contain the full set of + * messages provided by the loader. This must also be the case when these catalogues + * are (internally) read from a cache. + * + * Optimizations inside the translator must not change this behaviour. + */ + + /* + * Create a translator that loads two catalogues for two different locales. + * The catalogues contain distinct sets of messages. + */ + $translator = new Translator('a', null, $this->tmpDir); + $translator->setFallbackLocales(array('b')); + + $translator->addLoader('array', new ArrayLoader()); + $translator->addResource('array', array('foo' => 'foo (a)'), 'a'); + $translator->addResource('array', array('foo' => 'foo (b)'), 'b'); + $translator->addResource('array', array('bar' => 'bar (b)'), 'b'); + + $catalogue = $translator->getCatalogue('a'); + $this->assertFalse($catalogue->defines('bar')); // Sure, the "a" catalogue does not contain that message. + + $fallback = $catalogue->getFallbackCatalogue(); + $this->assertTrue($fallback->defines('foo')); // "foo" is present in "a" and "b" + + /* + * Now, repeat the same test. + * Behind the scenes, the cache is used. But that should not matter, right? + */ + $translator = new Translator('a', null, $this->tmpDir); + $translator->setFallbackLocales(array('b')); + + $translator->addLoader('array', new ArrayLoader()); + $translator->addResource('array', array('foo' => 'foo (a)'), 'a'); + $translator->addResource('array', array('foo' => 'foo (b)'), 'b'); + $translator->addResource('array', array('bar' => 'bar (b)'), 'b'); + + $catalogue = $translator->getCatalogue('a'); + $this->assertFalse($catalogue->defines('bar')); + + $fallback = $catalogue->getFallbackCatalogue(); + $this->assertTrue($fallback->defines('foo')); + } + + public function testRefreshCacheWhenResourcesAreNoLongerFresh() + { + $resource = $this->getMockBuilder('Symfony\Component\Config\Resource\SelfCheckingResourceInterface')->getMock(); + $loader = $this->getMockBuilder('Symfony\Component\Translation\Loader\LoaderInterface')->getMock(); + $resource->method('isFresh')->will($this->returnValue(false)); + $loader + ->expects($this->exactly(2)) + ->method('load') + ->will($this->returnValue($this->getCatalogue('fr', array(), array($resource)))); + + // prime the cache + $translator = new Translator('fr', null, $this->tmpDir, true); + $translator->addLoader('loader', $loader); + $translator->addResource('loader', 'foo', 'fr'); + $translator->trans('foo'); + + // prime the cache second time + $translator = new Translator('fr', null, $this->tmpDir, true); + $translator->addLoader('loader', $loader); + $translator->addResource('loader', 'foo', 'fr'); + $translator->trans('foo'); + } + + protected function getCatalogue($locale, $messages, $resources = array()) + { + $catalogue = new MessageCatalogue($locale); + foreach ($messages as $key => $translation) { + $catalogue->set($key, $translation); + } + foreach ($resources as $resource) { + $catalogue->addResource($resource); + } + + return $catalogue; + } + + public function runForDebugAndProduction() + { + return array(array(true), array(false)); + } + + /** + * @return LoaderInterface + */ + private function createFailingLoader() + { + $loader = $this->getMockBuilder('Symfony\Component\Translation\Loader\LoaderInterface')->getMock(); + $loader + ->expects($this->never()) + ->method('load'); + + return $loader; + } +} + +class StaleResource implements SelfCheckingResourceInterface +{ + public function isFresh($timestamp) + { + return false; + } + + public function getResource() + { + } + + public function __toString() + { + return ''; + } +} diff --git a/vendor/symfony/translation/Tests/TranslatorTest.php b/vendor/symfony/translation/Tests/TranslatorTest.php new file mode 100644 index 000000000..3e54839f7 --- /dev/null +++ b/vendor/symfony/translation/Tests/TranslatorTest.php @@ -0,0 +1,549 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Translation\Loader\ArrayLoader; +use Symfony\Component\Translation\MessageCatalogue; +use Symfony\Component\Translation\Translator; + +class TranslatorTest extends TestCase +{ + /** + * @dataProvider getInvalidLocalesTests + * @expectedException \Symfony\Component\Translation\Exception\InvalidArgumentException + */ + public function testConstructorInvalidLocale($locale) + { + new Translator($locale); + } + + /** + * @dataProvider getValidLocalesTests + */ + public function testConstructorValidLocale($locale) + { + $translator = new Translator($locale); + + $this->assertEquals($locale, $translator->getLocale()); + } + + public function testConstructorWithoutLocale() + { + $translator = new Translator(null); + + $this->assertNull($translator->getLocale()); + } + + public function testSetGetLocale() + { + $translator = new Translator('en'); + + $this->assertEquals('en', $translator->getLocale()); + + $translator->setLocale('fr'); + $this->assertEquals('fr', $translator->getLocale()); + } + + /** + * @dataProvider getInvalidLocalesTests + * @expectedException \Symfony\Component\Translation\Exception\InvalidArgumentException + */ + public function testSetInvalidLocale($locale) + { + $translator = new Translator('fr'); + $translator->setLocale($locale); + } + + /** + * @dataProvider getValidLocalesTests + */ + public function testSetValidLocale($locale) + { + $translator = new Translator($locale); + $translator->setLocale($locale); + + $this->assertEquals($locale, $translator->getLocale()); + } + + public function testGetCatalogue() + { + $translator = new Translator('en'); + + $this->assertEquals(new MessageCatalogue('en'), $translator->getCatalogue()); + + $translator->setLocale('fr'); + $this->assertEquals(new MessageCatalogue('fr'), $translator->getCatalogue('fr')); + } + + public function testGetCatalogueReturnsConsolidatedCatalogue() + { + /* + * This will be useful once we refactor so that different domains will be loaded lazily (on-demand). + * In that case, getCatalogue() will probably have to load all missing domains in order to return + * one complete catalogue. + */ + + $locale = 'whatever'; + $translator = new Translator($locale); + $translator->addLoader('loader-a', new ArrayLoader()); + $translator->addLoader('loader-b', new ArrayLoader()); + $translator->addResource('loader-a', array('foo' => 'foofoo'), $locale, 'domain-a'); + $translator->addResource('loader-b', array('bar' => 'foobar'), $locale, 'domain-b'); + + /* + * Test that we get a single catalogue comprising messages + * from different loaders and different domains + */ + $catalogue = $translator->getCatalogue($locale); + $this->assertTrue($catalogue->defines('foo', 'domain-a')); + $this->assertTrue($catalogue->defines('bar', 'domain-b')); + } + + public function testSetFallbackLocales() + { + $translator = new Translator('en'); + $translator->addLoader('array', new ArrayLoader()); + $translator->addResource('array', array('foo' => 'foofoo'), 'en'); + $translator->addResource('array', array('bar' => 'foobar'), 'fr'); + + // force catalogue loading + $translator->trans('bar'); + + $translator->setFallbackLocales(array('fr')); + $this->assertEquals('foobar', $translator->trans('bar')); + } + + public function testSetFallbackLocalesMultiple() + { + $translator = new Translator('en'); + $translator->addLoader('array', new ArrayLoader()); + $translator->addResource('array', array('foo' => 'foo (en)'), 'en'); + $translator->addResource('array', array('bar' => 'bar (fr)'), 'fr'); + + // force catalogue loading + $translator->trans('bar'); + + $translator->setFallbackLocales(array('fr_FR', 'fr')); + $this->assertEquals('bar (fr)', $translator->trans('bar')); + } + + /** + * @dataProvider getInvalidLocalesTests + * @expectedException \Symfony\Component\Translation\Exception\InvalidArgumentException + */ + public function testSetFallbackInvalidLocales($locale) + { + $translator = new Translator('fr'); + $translator->setFallbackLocales(array('fr', $locale)); + } + + /** + * @dataProvider getValidLocalesTests + */ + public function testSetFallbackValidLocales($locale) + { + $translator = new Translator($locale); + $translator->setFallbackLocales(array('fr', $locale)); + // no assertion. this method just asserts that no exception is thrown + $this->addToAssertionCount(1); + } + + public function testTransWithFallbackLocale() + { + $translator = new Translator('fr_FR'); + $translator->setFallbackLocales(array('en')); + + $translator->addLoader('array', new ArrayLoader()); + $translator->addResource('array', array('bar' => 'foobar'), 'en'); + + $this->assertEquals('foobar', $translator->trans('bar')); + } + + /** + * @dataProvider getInvalidLocalesTests + * @expectedException \Symfony\Component\Translation\Exception\InvalidArgumentException + */ + public function testAddResourceInvalidLocales($locale) + { + $translator = new Translator('fr'); + $translator->addResource('array', array('foo' => 'foofoo'), $locale); + } + + /** + * @dataProvider getValidLocalesTests + */ + public function testAddResourceValidLocales($locale) + { + $translator = new Translator('fr'); + $translator->addResource('array', array('foo' => 'foofoo'), $locale); + // no assertion. this method just asserts that no exception is thrown + $this->addToAssertionCount(1); + } + + public function testAddResourceAfterTrans() + { + $translator = new Translator('fr'); + $translator->addLoader('array', new ArrayLoader()); + + $translator->setFallbackLocales(array('en')); + + $translator->addResource('array', array('foo' => 'foofoo'), 'en'); + $this->assertEquals('foofoo', $translator->trans('foo')); + + $translator->addResource('array', array('bar' => 'foobar'), 'en'); + $this->assertEquals('foobar', $translator->trans('bar')); + } + + /** + * @dataProvider getTransFileTests + * @expectedException \Symfony\Component\Translation\Exception\NotFoundResourceException + */ + public function testTransWithoutFallbackLocaleFile($format, $loader) + { + $loaderClass = 'Symfony\\Component\\Translation\\Loader\\'.$loader; + $translator = new Translator('en'); + $translator->addLoader($format, new $loaderClass()); + $translator->addResource($format, __DIR__.'/fixtures/non-existing', 'en'); + $translator->addResource($format, __DIR__.'/fixtures/resources.'.$format, 'en'); + + // force catalogue loading + $translator->trans('foo'); + } + + /** + * @dataProvider getTransFileTests + */ + public function testTransWithFallbackLocaleFile($format, $loader) + { + $loaderClass = 'Symfony\\Component\\Translation\\Loader\\'.$loader; + $translator = new Translator('en_GB'); + $translator->addLoader($format, new $loaderClass()); + $translator->addResource($format, __DIR__.'/fixtures/non-existing', 'en_GB'); + $translator->addResource($format, __DIR__.'/fixtures/resources.'.$format, 'en', 'resources'); + + $this->assertEquals('bar', $translator->trans('foo', array(), 'resources')); + } + + public function testTransWithFallbackLocaleBis() + { + $translator = new Translator('en_US'); + $translator->addLoader('array', new ArrayLoader()); + $translator->addResource('array', array('foo' => 'foofoo'), 'en_US'); + $translator->addResource('array', array('bar' => 'foobar'), 'en'); + $this->assertEquals('foobar', $translator->trans('bar')); + } + + public function testTransWithFallbackLocaleTer() + { + $translator = new Translator('fr_FR'); + $translator->addLoader('array', new ArrayLoader()); + $translator->addResource('array', array('foo' => 'foo (en_US)'), 'en_US'); + $translator->addResource('array', array('bar' => 'bar (en)'), 'en'); + + $translator->setFallbackLocales(array('en_US', 'en')); + + $this->assertEquals('foo (en_US)', $translator->trans('foo')); + $this->assertEquals('bar (en)', $translator->trans('bar')); + } + + public function testTransNonExistentWithFallback() + { + $translator = new Translator('fr'); + $translator->setFallbackLocales(array('en')); + $translator->addLoader('array', new ArrayLoader()); + $this->assertEquals('non-existent', $translator->trans('non-existent')); + } + + /** + * @expectedException \Symfony\Component\Translation\Exception\RuntimeException + */ + public function testWhenAResourceHasNoRegisteredLoader() + { + $translator = new Translator('en'); + $translator->addResource('array', array('foo' => 'foofoo'), 'en'); + + $translator->trans('foo'); + } + + public function testNestedFallbackCatalogueWhenUsingMultipleLocales() + { + $translator = new Translator('fr'); + $translator->setFallbackLocales(array('ru', 'en')); + + $translator->getCatalogue('fr'); + + $this->assertNotNull($translator->getCatalogue('ru')->getFallbackCatalogue()); + } + + public function testFallbackCatalogueResources() + { + $translator = new Translator('en_GB'); + $translator->addLoader('yml', new \Symfony\Component\Translation\Loader\YamlFileLoader()); + $translator->addResource('yml', __DIR__.'/fixtures/empty.yml', 'en_GB'); + $translator->addResource('yml', __DIR__.'/fixtures/resources.yml', 'en'); + + // force catalogue loading + $this->assertEquals('bar', $translator->trans('foo', array())); + + $resources = $translator->getCatalogue('en')->getResources(); + $this->assertCount(1, $resources); + $this->assertContains(__DIR__.\DIRECTORY_SEPARATOR.'fixtures'.\DIRECTORY_SEPARATOR.'resources.yml', $resources); + + $resources = $translator->getCatalogue('en_GB')->getResources(); + $this->assertCount(2, $resources); + $this->assertContains(__DIR__.\DIRECTORY_SEPARATOR.'fixtures'.\DIRECTORY_SEPARATOR.'empty.yml', $resources); + $this->assertContains(__DIR__.\DIRECTORY_SEPARATOR.'fixtures'.\DIRECTORY_SEPARATOR.'resources.yml', $resources); + } + + /** + * @dataProvider getTransTests + */ + public function testTrans($expected, $id, $translation, $parameters, $locale, $domain) + { + $translator = new Translator('en'); + $translator->addLoader('array', new ArrayLoader()); + $translator->addResource('array', array((string) $id => $translation), $locale, $domain); + + $this->assertEquals($expected, $translator->trans($id, $parameters, $domain, $locale)); + } + + /** + * @dataProvider getInvalidLocalesTests + * @expectedException \Symfony\Component\Translation\Exception\InvalidArgumentException + */ + public function testTransInvalidLocale($locale) + { + $translator = new Translator('en'); + $translator->addLoader('array', new ArrayLoader()); + $translator->addResource('array', array('foo' => 'foofoo'), 'en'); + + $translator->trans('foo', array(), '', $locale); + } + + /** + * @dataProvider getValidLocalesTests + */ + public function testTransValidLocale($locale) + { + $translator = new Translator($locale); + $translator->addLoader('array', new ArrayLoader()); + $translator->addResource('array', array('test' => 'OK'), $locale); + + $this->assertEquals('OK', $translator->trans('test')); + $this->assertEquals('OK', $translator->trans('test', array(), null, $locale)); + } + + /** + * @dataProvider getFlattenedTransTests + */ + public function testFlattenedTrans($expected, $messages, $id) + { + $translator = new Translator('en'); + $translator->addLoader('array', new ArrayLoader()); + $translator->addResource('array', $messages, 'fr', ''); + + $this->assertEquals($expected, $translator->trans($id, array(), '', 'fr')); + } + + /** + * @dataProvider getTransChoiceTests + */ + public function testTransChoice($expected, $id, $translation, $number, $parameters, $locale, $domain) + { + $translator = new Translator('en'); + $translator->addLoader('array', new ArrayLoader()); + $translator->addResource('array', array((string) $id => $translation), $locale, $domain); + + $this->assertEquals($expected, $translator->transChoice($id, $number, $parameters, $domain, $locale)); + } + + /** + * @dataProvider getInvalidLocalesTests + * @expectedException \Symfony\Component\Translation\Exception\InvalidArgumentException + */ + public function testTransChoiceInvalidLocale($locale) + { + $translator = new Translator('en'); + $translator->addLoader('array', new ArrayLoader()); + $translator->addResource('array', array('foo' => 'foofoo'), 'en'); + + $translator->transChoice('foo', 1, array(), '', $locale); + } + + /** + * @dataProvider getValidLocalesTests + */ + public function testTransChoiceValidLocale($locale) + { + $translator = new Translator('en'); + $translator->addLoader('array', new ArrayLoader()); + $translator->addResource('array', array('foo' => 'foofoo'), 'en'); + + $translator->transChoice('foo', 1, array(), '', $locale); + // no assertion. this method just asserts that no exception is thrown + $this->addToAssertionCount(1); + } + + public function getTransFileTests() + { + return array( + array('csv', 'CsvFileLoader'), + array('ini', 'IniFileLoader'), + array('mo', 'MoFileLoader'), + array('po', 'PoFileLoader'), + array('php', 'PhpFileLoader'), + array('ts', 'QtFileLoader'), + array('xlf', 'XliffFileLoader'), + array('yml', 'YamlFileLoader'), + array('json', 'JsonFileLoader'), + ); + } + + public function getTransTests() + { + return array( + array('Symfony est super !', 'Symfony is great!', 'Symfony est super !', array(), 'fr', ''), + array('Symfony est awesome !', 'Symfony is %what%!', 'Symfony est %what% !', array('%what%' => 'awesome'), 'fr', ''), + array('Symfony est super !', new StringClass('Symfony is great!'), 'Symfony est super !', array(), 'fr', ''), + ); + } + + public function getFlattenedTransTests() + { + $messages = array( + 'symfony' => array( + 'is' => array( + 'great' => 'Symfony est super!', + ), + ), + 'foo' => array( + 'bar' => array( + 'baz' => 'Foo Bar Baz', + ), + 'baz' => 'Foo Baz', + ), + ); + + return array( + array('Symfony est super!', $messages, 'symfony.is.great'), + array('Foo Bar Baz', $messages, 'foo.bar.baz'), + array('Foo Baz', $messages, 'foo.baz'), + ); + } + + public function getTransChoiceTests() + { + return array( + array('Il y a 0 pomme', '{0} There are no appless|{1} There is one apple|]1,Inf] There is %count% apples', '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 0, array(), 'fr', ''), + array('Il y a 1 pomme', '{0} There are no appless|{1} There is one apple|]1,Inf] There is %count% apples', '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 1, array(), 'fr', ''), + array('Il y a 10 pommes', '{0} There are no appless|{1} There is one apple|]1,Inf] There is %count% apples', '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 10, array(), 'fr', ''), + + array('Il y a 0 pomme', 'There is one apple|There is %count% apples', 'Il y a %count% pomme|Il y a %count% pommes', 0, array(), 'fr', ''), + array('Il y a 1 pomme', 'There is one apple|There is %count% apples', 'Il y a %count% pomme|Il y a %count% pommes', 1, array(), 'fr', ''), + array('Il y a 10 pommes', 'There is one apple|There is %count% apples', 'Il y a %count% pomme|Il y a %count% pommes', 10, array(), 'fr', ''), + + array('Il y a 0 pomme', 'one: There is one apple|more: There is %count% apples', 'one: Il y a %count% pomme|more: Il y a %count% pommes', 0, array(), 'fr', ''), + array('Il y a 1 pomme', 'one: There is one apple|more: There is %count% apples', 'one: Il y a %count% pomme|more: Il y a %count% pommes', 1, array(), 'fr', ''), + array('Il y a 10 pommes', 'one: There is one apple|more: There is %count% apples', 'one: Il y a %count% pomme|more: Il y a %count% pommes', 10, array(), 'fr', ''), + + array('Il n\'y a aucune pomme', '{0} There are no apples|one: There is one apple|more: There is %count% apples', '{0} Il n\'y a aucune pomme|one: Il y a %count% pomme|more: Il y a %count% pommes', 0, array(), 'fr', ''), + array('Il y a 1 pomme', '{0} There are no apples|one: There is one apple|more: There is %count% apples', '{0} Il n\'y a aucune pomme|one: Il y a %count% pomme|more: Il y a %count% pommes', 1, array(), 'fr', ''), + array('Il y a 10 pommes', '{0} There are no apples|one: There is one apple|more: There is %count% apples', '{0} Il n\'y a aucune pomme|one: Il y a %count% pomme|more: Il y a %count% pommes', 10, array(), 'fr', ''), + + array('Il y a 0 pomme', new StringClass('{0} There are no appless|{1} There is one apple|]1,Inf] There is %count% apples'), '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 0, array(), 'fr', ''), + + // Override %count% with a custom value + array('Il y a quelques pommes', 'one: There is one apple|more: There are %count% apples', 'one: Il y a %count% pomme|more: Il y a %count% pommes', 2, array('%count%' => 'quelques'), 'fr', ''), + ); + } + + public function getInvalidLocalesTests() + { + return array( + array('fr FR'), + array('français'), + array('fr+en'), + array('utf#8'), + array('fr&en'), + array('fr~FR'), + array(' fr'), + array('fr '), + array('fr*'), + array('fr/FR'), + array('fr\\FR'), + ); + } + + public function getValidLocalesTests() + { + return array( + array(''), + array(null), + array('fr'), + array('francais'), + array('FR'), + array('frFR'), + array('fr-FR'), + array('fr_FR'), + array('fr.FR'), + array('fr-FR.UTF8'), + array('sr@latin'), + ); + } + + public function testTransChoiceFallback() + { + $translator = new Translator('ru'); + $translator->setFallbackLocales(array('en')); + $translator->addLoader('array', new ArrayLoader()); + $translator->addResource('array', array('some_message2' => 'one thing|%count% things'), 'en'); + + $this->assertEquals('10 things', $translator->transChoice('some_message2', 10, array('%count%' => 10))); + } + + public function testTransChoiceFallbackBis() + { + $translator = new Translator('ru'); + $translator->setFallbackLocales(array('en_US', 'en')); + $translator->addLoader('array', new ArrayLoader()); + $translator->addResource('array', array('some_message2' => 'one thing|%count% things'), 'en_US'); + + $this->assertEquals('10 things', $translator->transChoice('some_message2', 10, array('%count%' => 10))); + } + + public function testTransChoiceFallbackWithNoTranslation() + { + $translator = new Translator('ru'); + $translator->setFallbackLocales(array('en')); + $translator->addLoader('array', new ArrayLoader()); + + // consistent behavior with Translator::trans(), which returns the string + // unchanged if it can't be found + $this->assertEquals('some_message2', $translator->transChoice('some_message2', 10, array('%count%' => 10))); + } +} + +class StringClass +{ + protected $str; + + public function __construct($str) + { + $this->str = $str; + } + + public function __toString() + { + return $this->str; + } +} diff --git a/vendor/symfony/translation/Tests/Util/ArrayConverterTest.php b/vendor/symfony/translation/Tests/Util/ArrayConverterTest.php new file mode 100644 index 000000000..dbb5424f1 --- /dev/null +++ b/vendor/symfony/translation/Tests/Util/ArrayConverterTest.php @@ -0,0 +1,74 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Tests\Util; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Translation\Util\ArrayConverter; + +class ArrayConverterTest extends TestCase +{ + /** + * @dataProvider messagesData + */ + public function testDump($input, $expectedOutput) + { + $this->assertEquals($expectedOutput, ArrayConverter::expandToTree($input)); + } + + public function messagesData() + { + return array( + array( + // input + array( + 'foo1' => 'bar', + 'foo.bar' => 'value', + ), + // expected output + array( + 'foo1' => 'bar', + 'foo' => array('bar' => 'value'), + ), + ), + array( + // input + array( + 'foo.bar' => 'value1', + 'foo.bar.test' => 'value2', + ), + // expected output + array( + 'foo' => array( + 'bar' => 'value1', + 'bar.test' => 'value2', + ), + ), + ), + array( + // input + array( + 'foo.level2.level3.level4' => 'value1', + 'foo.level2' => 'value2', + 'foo.bar' => 'value3', + ), + // expected output + array( + 'foo' => array( + 'level2' => 'value2', + 'level2.level3.level4' => 'value1', + 'bar' => 'value3', + ), + ), + ), + ); + } +} diff --git a/vendor/symfony/translation/Tests/Writer/TranslationWriterTest.php b/vendor/symfony/translation/Tests/Writer/TranslationWriterTest.php new file mode 100644 index 000000000..ab66af13e --- /dev/null +++ b/vendor/symfony/translation/Tests/Writer/TranslationWriterTest.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Tests\Writer; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Translation\Dumper\DumperInterface; +use Symfony\Component\Translation\MessageCatalogue; +use Symfony\Component\Translation\Writer\TranslationWriter; + +class TranslationWriterTest extends TestCase +{ + public function testWrite() + { + $dumper = $this->getMockBuilder('Symfony\Component\Translation\Dumper\DumperInterface')->getMock(); + $dumper + ->expects($this->once()) + ->method('dump'); + + $writer = new TranslationWriter(); + $writer->addDumper('test', $dumper); + $writer->write(new MessageCatalogue('en'), 'test'); + } + + /** + * @group legacy + */ + public function testDisableBackup() + { + $nonBackupDumper = new NonBackupDumper(); + $backupDumper = new BackupDumper(); + + $writer = new TranslationWriter(); + $writer->addDumper('non_backup', $nonBackupDumper); + $writer->addDumper('backup', $backupDumper); + $writer->disableBackup(); + + $this->assertFalse($backupDumper->backup, 'backup can be disabled if setBackup() method does exist'); + } +} + +class NonBackupDumper implements DumperInterface +{ + public function dump(MessageCatalogue $messages, $options = array()) + { + } +} + +class BackupDumper implements DumperInterface +{ + public $backup = true; + + public function dump(MessageCatalogue $messages, $options = array()) + { + } + + public function setBackup($backup) + { + $this->backup = $backup; + } +} diff --git a/vendor/symfony/translation/Tests/fixtures/empty-translation.mo b/vendor/symfony/translation/Tests/fixtures/empty-translation.mo new file mode 100644 index 0000000000000000000000000000000000000000..ed01000c9b1b3fdf40fc2369a7799e72e41a1edf GIT binary patch literal 49 ncmca7#4?ou2pEA_28dNa93apEVrC%L1#y7D5JaTq=Q98RUv2}O literal 0 HcmV?d00001 diff --git a/vendor/symfony/translation/Tests/fixtures/empty-translation.po b/vendor/symfony/translation/Tests/fixtures/empty-translation.po new file mode 100644 index 000000000..ff6f22afb --- /dev/null +++ b/vendor/symfony/translation/Tests/fixtures/empty-translation.po @@ -0,0 +1,3 @@ +msgid "foo" +msgstr "" + diff --git a/vendor/symfony/translation/Tests/fixtures/empty.csv b/vendor/symfony/translation/Tests/fixtures/empty.csv new file mode 100644 index 000000000..e69de29bb diff --git a/vendor/symfony/translation/Tests/fixtures/empty.ini b/vendor/symfony/translation/Tests/fixtures/empty.ini new file mode 100644 index 000000000..e69de29bb diff --git a/vendor/symfony/translation/Tests/fixtures/empty.json b/vendor/symfony/translation/Tests/fixtures/empty.json new file mode 100644 index 000000000..e69de29bb diff --git a/vendor/symfony/translation/Tests/fixtures/empty.mo b/vendor/symfony/translation/Tests/fixtures/empty.mo new file mode 100644 index 000000000..e69de29bb diff --git a/vendor/symfony/translation/Tests/fixtures/empty.po b/vendor/symfony/translation/Tests/fixtures/empty.po new file mode 100644 index 000000000..e69de29bb diff --git a/vendor/symfony/translation/Tests/fixtures/empty.xlf b/vendor/symfony/translation/Tests/fixtures/empty.xlf new file mode 100644 index 000000000..e69de29bb diff --git a/vendor/symfony/translation/Tests/fixtures/empty.yml b/vendor/symfony/translation/Tests/fixtures/empty.yml new file mode 100644 index 000000000..e69de29bb diff --git a/vendor/symfony/translation/Tests/fixtures/encoding.xlf b/vendor/symfony/translation/Tests/fixtures/encoding.xlf new file mode 100644 index 000000000..0a88f9265 --- /dev/null +++ b/vendor/symfony/translation/Tests/fixtures/encoding.xlf @@ -0,0 +1,16 @@ + + + + + + foo + br + bz + + + bar + f + + + + diff --git a/vendor/symfony/translation/Tests/fixtures/escaped-id-plurals.po b/vendor/symfony/translation/Tests/fixtures/escaped-id-plurals.po new file mode 100644 index 000000000..c412aa26e --- /dev/null +++ b/vendor/symfony/translation/Tests/fixtures/escaped-id-plurals.po @@ -0,0 +1,10 @@ +msgid "" +msgstr "" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: en\n" + +msgid "escaped \"foo\"" +msgid_plural "escaped \"foos\"" +msgstr[0] "escaped \"bar\"" +msgstr[1] "escaped \"bars\"" diff --git a/vendor/symfony/translation/Tests/fixtures/escaped-id.po b/vendor/symfony/translation/Tests/fixtures/escaped-id.po new file mode 100644 index 000000000..308eadd92 --- /dev/null +++ b/vendor/symfony/translation/Tests/fixtures/escaped-id.po @@ -0,0 +1,8 @@ +msgid "" +msgstr "" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: en\n" + +msgid "escaped \"foo\"" +msgstr "escaped \"bar\"" diff --git a/vendor/symfony/translation/Tests/fixtures/extractor/resource.format.engine b/vendor/symfony/translation/Tests/fixtures/extractor/resource.format.engine new file mode 100644 index 000000000..e69de29bb diff --git a/vendor/symfony/translation/Tests/fixtures/extractor/this.is.a.template.format.engine b/vendor/symfony/translation/Tests/fixtures/extractor/this.is.a.template.format.engine new file mode 100644 index 000000000..e69de29bb diff --git a/vendor/symfony/translation/Tests/fixtures/extractor/translation.html.php b/vendor/symfony/translation/Tests/fixtures/extractor/translation.html.php new file mode 100644 index 000000000..1ce8ea94f --- /dev/null +++ b/vendor/symfony/translation/Tests/fixtures/extractor/translation.html.php @@ -0,0 +1,49 @@ +This template is used for translation message extraction tests +trans('single-quoted key'); ?> +trans('double-quoted key'); ?> +trans(<<<'EOF' +heredoc key +EOF +); ?> +trans(<<<'EOF' +nowdoc key +EOF +); ?> +trans( + "double-quoted key with whitespace and escaped \$\n\" sequences" +); ?> +trans( + 'single-quoted key with whitespace and nonescaped \$\n\' sequences' +); ?> +trans(<< +trans(<<<'EOF' +nowdoc key with whitespace and nonescaped \$\n sequences +EOF +); ?> + +trans('single-quoted key with "quote mark at the end"'); ?> + +transChoice( + '{0} There is no apples|{1} There is one apple|]1,Inf[ There are %count% apples', + 10, + array('%count%' => 10) +); ?> + +trans('other-domain-test-no-params-short-array', array(), 'not_messages'); ?> + +trans('other-domain-test-no-params-long-array', array(), 'not_messages'); ?> + +trans('other-domain-test-params-short-array', array('foo' => 'bar'), 'not_messages'); ?> + +trans('other-domain-test-params-long-array', array('foo' => 'bar'), 'not_messages'); ?> + +transChoice('other-domain-test-trans-choice-short-array-%count%', 10, array('%count%' => 10), 'not_messages'); ?> + +transChoice('other-domain-test-trans-choice-long-array-%count%', 10, array('%count%' => 10), 'not_messages'); ?> + +trans('typecast', array('a' => (int) '123'), 'not_messages'); ?> +transChoice('msg1', 10 + 1, array(), 'not_messages'); ?> +transChoice('msg2', ceil(4.5), array(), 'not_messages'); ?> diff --git a/vendor/symfony/translation/Tests/fixtures/fuzzy-translations.po b/vendor/symfony/translation/Tests/fixtures/fuzzy-translations.po new file mode 100644 index 000000000..04d4047aa --- /dev/null +++ b/vendor/symfony/translation/Tests/fixtures/fuzzy-translations.po @@ -0,0 +1,10 @@ +#, php-format +msgid "foo1" +msgstr "bar1" + +#, fuzzy, php-format +msgid "foo2" +msgstr "fuzzy bar2" + +msgid "foo3" +msgstr "bar3" diff --git a/vendor/symfony/translation/Tests/fixtures/invalid-xml-resources.xlf b/vendor/symfony/translation/Tests/fixtures/invalid-xml-resources.xlf new file mode 100644 index 000000000..7bf6c98ba --- /dev/null +++ b/vendor/symfony/translation/Tests/fixtures/invalid-xml-resources.xlf @@ -0,0 +1,23 @@ + + + + + + foo + bar + + + extra + + + key + + + + test + with + note + + + + diff --git a/vendor/symfony/translation/Tests/fixtures/malformed.json b/vendor/symfony/translation/Tests/fixtures/malformed.json new file mode 100644 index 000000000..4563ec6da --- /dev/null +++ b/vendor/symfony/translation/Tests/fixtures/malformed.json @@ -0,0 +1,3 @@ +{ + "foo" "bar" +} \ No newline at end of file diff --git a/vendor/symfony/translation/Tests/fixtures/messages.yml b/vendor/symfony/translation/Tests/fixtures/messages.yml new file mode 100644 index 000000000..d4f82d781 --- /dev/null +++ b/vendor/symfony/translation/Tests/fixtures/messages.yml @@ -0,0 +1,3 @@ +foo: + bar1: value1 + bar2: value2 diff --git a/vendor/symfony/translation/Tests/fixtures/messages_linear.yml b/vendor/symfony/translation/Tests/fixtures/messages_linear.yml new file mode 100644 index 000000000..6c1687d0a --- /dev/null +++ b/vendor/symfony/translation/Tests/fixtures/messages_linear.yml @@ -0,0 +1,2 @@ +foo.bar1: value1 +foo.bar2: value2 diff --git a/vendor/symfony/translation/Tests/fixtures/non-valid.xlf b/vendor/symfony/translation/Tests/fixtures/non-valid.xlf new file mode 100644 index 000000000..734fc97e4 --- /dev/null +++ b/vendor/symfony/translation/Tests/fixtures/non-valid.xlf @@ -0,0 +1,11 @@ + + + + + + foo + bar + + + + diff --git a/vendor/symfony/translation/Tests/fixtures/non-valid.yml b/vendor/symfony/translation/Tests/fixtures/non-valid.yml new file mode 100644 index 000000000..257cc5642 --- /dev/null +++ b/vendor/symfony/translation/Tests/fixtures/non-valid.yml @@ -0,0 +1 @@ +foo diff --git a/vendor/symfony/translation/Tests/fixtures/plurals.mo b/vendor/symfony/translation/Tests/fixtures/plurals.mo new file mode 100644 index 0000000000000000000000000000000000000000..6445e77beab595289cd154ea253c4e49dfd6af47 GIT binary patch literal 74 zcmca7#4?ou2pEA_28dOFm>Gz5fS3b_Eugd`kOrxNfwcU51|TkGNJ=aM;bH~=*B}U7 literal 0 HcmV?d00001 diff --git a/vendor/symfony/translation/Tests/fixtures/plurals.po b/vendor/symfony/translation/Tests/fixtures/plurals.po new file mode 100644 index 000000000..439c41ad7 --- /dev/null +++ b/vendor/symfony/translation/Tests/fixtures/plurals.po @@ -0,0 +1,5 @@ +msgid "foo" +msgid_plural "foos" +msgstr[0] "bar" +msgstr[1] "bars" + diff --git a/vendor/symfony/translation/Tests/fixtures/resname.xlf b/vendor/symfony/translation/Tests/fixtures/resname.xlf new file mode 100644 index 000000000..2df16af94 --- /dev/null +++ b/vendor/symfony/translation/Tests/fixtures/resname.xlf @@ -0,0 +1,19 @@ + + + + + + + bar + + + bar source + baz + + + baz + foo + + + + diff --git a/vendor/symfony/translation/Tests/fixtures/resourcebundle/corrupted/resources.dat b/vendor/symfony/translation/Tests/fixtures/resourcebundle/corrupted/resources.dat new file mode 100644 index 000000000..391250caa --- /dev/null +++ b/vendor/symfony/translation/Tests/fixtures/resourcebundle/corrupted/resources.dat @@ -0,0 +1 @@ +XXX \ No newline at end of file diff --git a/vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/en.res b/vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/en.res new file mode 100644 index 0000000000000000000000000000000000000000..1fc1436d6641b7290ad5d9f4d331111e4a0419dd GIT binary patch literal 120 zcmY#jxTP+_00K-5L8-+~j7$s+j4WUQFaeZPU<0x^fmjTR8No6P48@hXY594T3_?JD sFheCnE<+kaK0_XmrNCeW#F-4mKr)@7h#{3Bk)Z^rYSk)61{ttf0N1AuGXMYp literal 0 HcmV?d00001 diff --git a/vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/en.txt b/vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/en.txt new file mode 100644 index 000000000..3d9e9eae1 --- /dev/null +++ b/vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/en.txt @@ -0,0 +1,3 @@ +en{ + symfony{"Symfony is great"} +} \ No newline at end of file diff --git a/vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/fr.res b/vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/fr.res new file mode 100644 index 0000000000000000000000000000000000000000..f58416094be89f5fb64f78f6c3b873c6458696d0 GIT binary patch literal 124 zcmY#jxTP+_00K-5L8-+~j7$s+j4WUQFd@popuh%XaRRY86f=Tl7#NBxbJOzkDj7if wgBdCrav9PX@)`1gECmK5AWmf{W+(yD=?pJ{qL~bd3^_oRt5z{G$biiQ03-qri2wiq literal 0 HcmV?d00001 diff --git a/vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/fr.txt b/vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/fr.txt new file mode 100644 index 000000000..182d0a0de --- /dev/null +++ b/vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/fr.txt @@ -0,0 +1,3 @@ +fr{ + symfony{"Symfony est génial"} +} \ No newline at end of file diff --git a/vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/packagelist.txt b/vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/packagelist.txt new file mode 100644 index 000000000..c5783ed43 --- /dev/null +++ b/vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/packagelist.txt @@ -0,0 +1,2 @@ +en.res +fr.res diff --git a/vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/resources.dat b/vendor/symfony/translation/Tests/fixtures/resourcebundle/dat/resources.dat new file mode 100644 index 0000000000000000000000000000000000000000..563b0eaef2e5a0a6e9623bf7b93b44beb0a7dc27 GIT binary patch literal 352 zcmY#jxTP+_00K-5&bfImj6fDMm=7VCfD}mH0f<$B_y7!;@F0Xawl zX+>axRdAqyWPVU;u@fWEKt>jzAy5D`TY(M8<^*CfC^BI_d>?DRn Nh9V%%$RGn&2LK231)%@{ literal 0 HcmV?d00001 diff --git a/vendor/symfony/translation/Tests/fixtures/resources-2.0-clean.xlf b/vendor/symfony/translation/Tests/fixtures/resources-2.0-clean.xlf new file mode 100644 index 000000000..efa69b27f --- /dev/null +++ b/vendor/symfony/translation/Tests/fixtures/resources-2.0-clean.xlf @@ -0,0 +1,23 @@ + + + + + + foo + bar + + + + + key + + + + + + key.with.cdata + & ]]> + + + + diff --git a/vendor/symfony/translation/Tests/fixtures/resources-2.0-multi-segment-unit.xlf b/vendor/symfony/translation/Tests/fixtures/resources-2.0-multi-segment-unit.xlf new file mode 100644 index 000000000..d0dc2a8af --- /dev/null +++ b/vendor/symfony/translation/Tests/fixtures/resources-2.0-multi-segment-unit.xlf @@ -0,0 +1,17 @@ + + + + + true + + + foo + foo (translated) + + + bar + bar (translated) + + + + diff --git a/vendor/symfony/translation/Tests/fixtures/resources-2.0.xlf b/vendor/symfony/translation/Tests/fixtures/resources-2.0.xlf new file mode 100644 index 000000000..166172a84 --- /dev/null +++ b/vendor/symfony/translation/Tests/fixtures/resources-2.0.xlf @@ -0,0 +1,25 @@ + + + + + + Quetzal + Quetzal + + + + + + foo + XLIFF 文書を編集、または処理 するアプリケーションです。 + + + + + bar + XLIFF データ・マネージャ + + + + + diff --git a/vendor/symfony/translation/Tests/fixtures/resources-clean.xlf b/vendor/symfony/translation/Tests/fixtures/resources-clean.xlf new file mode 100644 index 000000000..00c8a5c24 --- /dev/null +++ b/vendor/symfony/translation/Tests/fixtures/resources-clean.xlf @@ -0,0 +1,25 @@ + + + +
+ +
+ + + foo + bar + baz + + + key + + baz + qux + + + key.with.cdata + & ]]> + + +
+
diff --git a/vendor/symfony/translation/Tests/fixtures/resources-notes-meta.xlf b/vendor/symfony/translation/Tests/fixtures/resources-notes-meta.xlf new file mode 100644 index 000000000..7d5bbd40f --- /dev/null +++ b/vendor/symfony/translation/Tests/fixtures/resources-notes-meta.xlf @@ -0,0 +1,26 @@ + + + + + + new + true + user login + + + foo + bar + + + + + x_content + Fuzzy + + + baz + biz + + + + diff --git a/vendor/symfony/translation/Tests/fixtures/resources-target-attributes.xlf b/vendor/symfony/translation/Tests/fixtures/resources-target-attributes.xlf new file mode 100644 index 000000000..700d28186 --- /dev/null +++ b/vendor/symfony/translation/Tests/fixtures/resources-target-attributes.xlf @@ -0,0 +1,14 @@ + + + +
+ +
+ + + foo + bar + + +
+
diff --git a/vendor/symfony/translation/Tests/fixtures/resources-tool-info.xlf b/vendor/symfony/translation/Tests/fixtures/resources-tool-info.xlf new file mode 100644 index 000000000..1c2ae954e --- /dev/null +++ b/vendor/symfony/translation/Tests/fixtures/resources-tool-info.xlf @@ -0,0 +1,14 @@ + + + +
+ +
+ + + foo + bar + + +
+
diff --git a/vendor/symfony/translation/Tests/fixtures/resources.csv b/vendor/symfony/translation/Tests/fixtures/resources.csv new file mode 100644 index 000000000..374b9eb5e --- /dev/null +++ b/vendor/symfony/translation/Tests/fixtures/resources.csv @@ -0,0 +1,4 @@ +"foo"; "bar" +#"bar"; "foo" +"incorrect"; "number"; "columns"; "will"; "be"; "ignored" +"incorrect" \ No newline at end of file diff --git a/vendor/symfony/translation/Tests/fixtures/resources.dump.json b/vendor/symfony/translation/Tests/fixtures/resources.dump.json new file mode 100644 index 000000000..335965d59 --- /dev/null +++ b/vendor/symfony/translation/Tests/fixtures/resources.dump.json @@ -0,0 +1 @@ +{"foo":"\u0022bar\u0022"} \ No newline at end of file diff --git a/vendor/symfony/translation/Tests/fixtures/resources.ini b/vendor/symfony/translation/Tests/fixtures/resources.ini new file mode 100644 index 000000000..4953062e6 --- /dev/null +++ b/vendor/symfony/translation/Tests/fixtures/resources.ini @@ -0,0 +1 @@ +foo="bar" diff --git a/vendor/symfony/translation/Tests/fixtures/resources.json b/vendor/symfony/translation/Tests/fixtures/resources.json new file mode 100644 index 000000000..8a7968762 --- /dev/null +++ b/vendor/symfony/translation/Tests/fixtures/resources.json @@ -0,0 +1,3 @@ +{ + "foo": "bar" +} \ No newline at end of file diff --git a/vendor/symfony/translation/Tests/fixtures/resources.mo b/vendor/symfony/translation/Tests/fixtures/resources.mo new file mode 100644 index 0000000000000000000000000000000000000000..0a9660257c07afef243a011d9806d6217e4f1379 GIT binary patch literal 52 pcmca7#4?ou2pEA_28dNa93apEVrC%Lh0=yVnjtMepCKu+2mox%1k?Zk literal 0 HcmV?d00001 diff --git a/vendor/symfony/translation/Tests/fixtures/resources.php b/vendor/symfony/translation/Tests/fixtures/resources.php new file mode 100644 index 000000000..c29139853 --- /dev/null +++ b/vendor/symfony/translation/Tests/fixtures/resources.php @@ -0,0 +1,5 @@ + 'bar', +); diff --git a/vendor/symfony/translation/Tests/fixtures/resources.po b/vendor/symfony/translation/Tests/fixtures/resources.po new file mode 100644 index 000000000..ccfce6bb9 --- /dev/null +++ b/vendor/symfony/translation/Tests/fixtures/resources.po @@ -0,0 +1,8 @@ +msgid "" +msgstr "" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: en\n" + +msgid "foo" +msgstr "bar" \ No newline at end of file diff --git a/vendor/symfony/translation/Tests/fixtures/resources.ts b/vendor/symfony/translation/Tests/fixtures/resources.ts new file mode 100644 index 000000000..40e18522c --- /dev/null +++ b/vendor/symfony/translation/Tests/fixtures/resources.ts @@ -0,0 +1,10 @@ + + + + resources + + foo + bar + + + diff --git a/vendor/symfony/translation/Tests/fixtures/resources.xlf b/vendor/symfony/translation/Tests/fixtures/resources.xlf new file mode 100644 index 000000000..b0e59880f --- /dev/null +++ b/vendor/symfony/translation/Tests/fixtures/resources.xlf @@ -0,0 +1,23 @@ + + + + + + foo + bar + + + extra + + + key + + + + test + with + note + + + + diff --git a/vendor/symfony/translation/Tests/fixtures/resources.yml b/vendor/symfony/translation/Tests/fixtures/resources.yml new file mode 100644 index 000000000..20e9ff3fe --- /dev/null +++ b/vendor/symfony/translation/Tests/fixtures/resources.yml @@ -0,0 +1 @@ +foo: bar diff --git a/vendor/symfony/translation/Tests/fixtures/valid.csv b/vendor/symfony/translation/Tests/fixtures/valid.csv new file mode 100644 index 000000000..59882e5da --- /dev/null +++ b/vendor/symfony/translation/Tests/fixtures/valid.csv @@ -0,0 +1,4 @@ +foo;bar +bar;"foo +foo" +"foo;foo";bar diff --git a/vendor/symfony/translation/Tests/fixtures/with-attributes.xlf b/vendor/symfony/translation/Tests/fixtures/with-attributes.xlf new file mode 100644 index 000000000..78730629c --- /dev/null +++ b/vendor/symfony/translation/Tests/fixtures/with-attributes.xlf @@ -0,0 +1,21 @@ + + + + + + foo + bar + + + extra + bar + + + key + + baz + qux + + + + diff --git a/vendor/symfony/translation/Tests/fixtures/withdoctype.xlf b/vendor/symfony/translation/Tests/fixtures/withdoctype.xlf new file mode 100644 index 000000000..f83e834dd --- /dev/null +++ b/vendor/symfony/translation/Tests/fixtures/withdoctype.xlf @@ -0,0 +1,12 @@ + + + + + + + foo + bar + + + + diff --git a/vendor/symfony/translation/Tests/fixtures/withnote.xlf b/vendor/symfony/translation/Tests/fixtures/withnote.xlf new file mode 100644 index 000000000..c045e21e2 --- /dev/null +++ b/vendor/symfony/translation/Tests/fixtures/withnote.xlf @@ -0,0 +1,22 @@ + + + + + + foo + bar + foo + + + extra + bar + + + key + + baz + qux + + + + diff --git a/vendor/symfony/translation/Translator.php b/vendor/symfony/translation/Translator.php new file mode 100644 index 000000000..97690410e --- /dev/null +++ b/vendor/symfony/translation/Translator.php @@ -0,0 +1,437 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation; + +use Symfony\Component\Config\ConfigCacheFactory; +use Symfony\Component\Config\ConfigCacheFactoryInterface; +use Symfony\Component\Config\ConfigCacheInterface; +use Symfony\Component\Translation\Exception\InvalidArgumentException; +use Symfony\Component\Translation\Exception\LogicException; +use Symfony\Component\Translation\Exception\NotFoundResourceException; +use Symfony\Component\Translation\Exception\RuntimeException; +use Symfony\Component\Translation\Formatter\ChoiceMessageFormatterInterface; +use Symfony\Component\Translation\Formatter\MessageFormatter; +use Symfony\Component\Translation\Formatter\MessageFormatterInterface; +use Symfony\Component\Translation\Loader\LoaderInterface; + +/** + * @author Fabien Potencier + */ +class Translator implements TranslatorInterface, TranslatorBagInterface +{ + /** + * @var MessageCatalogueInterface[] + */ + protected $catalogues = array(); + + /** + * @var string + */ + private $locale; + + /** + * @var array + */ + private $fallbackLocales = array(); + + /** + * @var LoaderInterface[] + */ + private $loaders = array(); + + /** + * @var array + */ + private $resources = array(); + + /** + * @var MessageFormatterInterface + */ + private $formatter; + + /** + * @var string + */ + private $cacheDir; + + /** + * @var bool + */ + private $debug; + + /** + * @var ConfigCacheFactoryInterface|null + */ + private $configCacheFactory; + + /** + * @throws InvalidArgumentException If a locale contains invalid characters + */ + public function __construct(?string $locale, MessageFormatterInterface $formatter = null, string $cacheDir = null, bool $debug = false) + { + $this->setLocale($locale); + + if (null === $formatter) { + $formatter = new MessageFormatter(); + } + + $this->formatter = $formatter; + $this->cacheDir = $cacheDir; + $this->debug = $debug; + } + + public function setConfigCacheFactory(ConfigCacheFactoryInterface $configCacheFactory) + { + $this->configCacheFactory = $configCacheFactory; + } + + /** + * Adds a Loader. + * + * @param string $format The name of the loader (@see addResource()) + * @param LoaderInterface $loader A LoaderInterface instance + */ + public function addLoader($format, LoaderInterface $loader) + { + $this->loaders[$format] = $loader; + } + + /** + * Adds a Resource. + * + * @param string $format The name of the loader (@see addLoader()) + * @param mixed $resource The resource name + * @param string $locale The locale + * @param string $domain The domain + * + * @throws InvalidArgumentException If the locale contains invalid characters + */ + public function addResource($format, $resource, $locale, $domain = null) + { + if (null === $domain) { + $domain = 'messages'; + } + + $this->assertValidLocale($locale); + + $this->resources[$locale][] = array($format, $resource, $domain); + + if (\in_array($locale, $this->fallbackLocales)) { + $this->catalogues = array(); + } else { + unset($this->catalogues[$locale]); + } + } + + /** + * {@inheritdoc} + */ + public function setLocale($locale) + { + $this->assertValidLocale($locale); + $this->locale = $locale; + } + + /** + * {@inheritdoc} + */ + public function getLocale() + { + return $this->locale; + } + + /** + * Sets the fallback locales. + * + * @param array $locales The fallback locales + * + * @throws InvalidArgumentException If a locale contains invalid characters + */ + public function setFallbackLocales(array $locales) + { + // needed as the fallback locales are linked to the already loaded catalogues + $this->catalogues = array(); + + foreach ($locales as $locale) { + $this->assertValidLocale($locale); + } + + $this->fallbackLocales = $locales; + } + + /** + * Gets the fallback locales. + * + * @return array $locales The fallback locales + */ + public function getFallbackLocales() + { + return $this->fallbackLocales; + } + + /** + * {@inheritdoc} + */ + public function trans($id, array $parameters = array(), $domain = null, $locale = null) + { + if (null === $domain) { + $domain = 'messages'; + } + + return $this->formatter->format($this->getCatalogue($locale)->get((string) $id, $domain), $locale, $parameters); + } + + /** + * {@inheritdoc} + */ + public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null) + { + if (!$this->formatter instanceof ChoiceMessageFormatterInterface) { + throw new LogicException(sprintf('The formatter "%s" does not support plural translations.', \get_class($this->formatter))); + } + + if (null === $domain) { + $domain = 'messages'; + } + + $id = (string) $id; + $catalogue = $this->getCatalogue($locale); + $locale = $catalogue->getLocale(); + while (!$catalogue->defines($id, $domain)) { + if ($cat = $catalogue->getFallbackCatalogue()) { + $catalogue = $cat; + $locale = $catalogue->getLocale(); + } else { + break; + } + } + + return $this->formatter->choiceFormat($catalogue->get($id, $domain), $number, $locale, $parameters); + } + + /** + * {@inheritdoc} + */ + public function getCatalogue($locale = null) + { + if (null === $locale) { + $locale = $this->getLocale(); + } else { + $this->assertValidLocale($locale); + } + + if (!isset($this->catalogues[$locale])) { + $this->loadCatalogue($locale); + } + + return $this->catalogues[$locale]; + } + + /** + * Gets the loaders. + * + * @return array LoaderInterface[] + */ + protected function getLoaders() + { + return $this->loaders; + } + + /** + * @param string $locale + */ + protected function loadCatalogue($locale) + { + if (null === $this->cacheDir) { + $this->initializeCatalogue($locale); + } else { + $this->initializeCacheCatalogue($locale); + } + } + + /** + * @param string $locale + */ + protected function initializeCatalogue($locale) + { + $this->assertValidLocale($locale); + + try { + $this->doLoadCatalogue($locale); + } catch (NotFoundResourceException $e) { + if (!$this->computeFallbackLocales($locale)) { + throw $e; + } + } + $this->loadFallbackCatalogues($locale); + } + + private function initializeCacheCatalogue(string $locale): void + { + if (isset($this->catalogues[$locale])) { + /* Catalogue already initialized. */ + return; + } + + $this->assertValidLocale($locale); + $cache = $this->getConfigCacheFactory()->cache($this->getCatalogueCachePath($locale), + function (ConfigCacheInterface $cache) use ($locale) { + $this->dumpCatalogue($locale, $cache); + } + ); + + if (isset($this->catalogues[$locale])) { + /* Catalogue has been initialized as it was written out to cache. */ + return; + } + + /* Read catalogue from cache. */ + $this->catalogues[$locale] = include $cache->getPath(); + } + + private function dumpCatalogue($locale, ConfigCacheInterface $cache): void + { + $this->initializeCatalogue($locale); + $fallbackContent = $this->getFallbackContent($this->catalogues[$locale]); + + $content = sprintf(<<catalogues[$locale]->all(), true), + $fallbackContent + ); + + $cache->write($content, $this->catalogues[$locale]->getResources()); + } + + private function getFallbackContent(MessageCatalogue $catalogue): string + { + $fallbackContent = ''; + $current = ''; + $replacementPattern = '/[^a-z0-9_]/i'; + $fallbackCatalogue = $catalogue->getFallbackCatalogue(); + while ($fallbackCatalogue) { + $fallback = $fallbackCatalogue->getLocale(); + $fallbackSuffix = ucfirst(preg_replace($replacementPattern, '_', $fallback)); + $currentSuffix = ucfirst(preg_replace($replacementPattern, '_', $current)); + + $fallbackContent .= sprintf(<<<'EOF' +$catalogue%s = new MessageCatalogue('%s', %s); +$catalogue%s->addFallbackCatalogue($catalogue%s); + +EOF + , + $fallbackSuffix, + $fallback, + var_export($fallbackCatalogue->all(), true), + $currentSuffix, + $fallbackSuffix + ); + $current = $fallbackCatalogue->getLocale(); + $fallbackCatalogue = $fallbackCatalogue->getFallbackCatalogue(); + } + + return $fallbackContent; + } + + private function getCatalogueCachePath($locale) + { + return $this->cacheDir.'/catalogue.'.$locale.'.'.strtr(substr(base64_encode(hash('sha256', serialize($this->fallbackLocales), true)), 0, 7), '/', '_').'.php'; + } + + private function doLoadCatalogue($locale): void + { + $this->catalogues[$locale] = new MessageCatalogue($locale); + + if (isset($this->resources[$locale])) { + foreach ($this->resources[$locale] as $resource) { + if (!isset($this->loaders[$resource[0]])) { + throw new RuntimeException(sprintf('The "%s" translation loader is not registered.', $resource[0])); + } + $this->catalogues[$locale]->addCatalogue($this->loaders[$resource[0]]->load($resource[1], $locale, $resource[2])); + } + } + } + + private function loadFallbackCatalogues($locale): void + { + $current = $this->catalogues[$locale]; + + foreach ($this->computeFallbackLocales($locale) as $fallback) { + if (!isset($this->catalogues[$fallback])) { + $this->initializeCatalogue($fallback); + } + + $fallbackCatalogue = new MessageCatalogue($fallback, $this->catalogues[$fallback]->all()); + foreach ($this->catalogues[$fallback]->getResources() as $resource) { + $fallbackCatalogue->addResource($resource); + } + $current->addFallbackCatalogue($fallbackCatalogue); + $current = $fallbackCatalogue; + } + } + + protected function computeFallbackLocales($locale) + { + $locales = array(); + foreach ($this->fallbackLocales as $fallback) { + if ($fallback === $locale) { + continue; + } + + $locales[] = $fallback; + } + + if (false !== strrchr($locale, '_')) { + array_unshift($locales, substr($locale, 0, -\strlen(strrchr($locale, '_')))); + } + + return array_unique($locales); + } + + /** + * Asserts that the locale is valid, throws an Exception if not. + * + * @param string $locale Locale to tests + * + * @throws InvalidArgumentException If the locale contains invalid characters + */ + protected function assertValidLocale($locale) + { + if (1 !== preg_match('/^[a-z0-9@_\\.\\-]*$/i', $locale)) { + throw new InvalidArgumentException(sprintf('Invalid "%s" locale.', $locale)); + } + } + + /** + * Provides the ConfigCache factory implementation, falling back to a + * default implementation if necessary. + */ + private function getConfigCacheFactory(): ConfigCacheFactoryInterface + { + if (!$this->configCacheFactory) { + $this->configCacheFactory = new ConfigCacheFactory($this->debug); + } + + return $this->configCacheFactory; + } +} diff --git a/vendor/symfony/translation/TranslatorBagInterface.php b/vendor/symfony/translation/TranslatorBagInterface.php new file mode 100644 index 000000000..5e49e2ddc --- /dev/null +++ b/vendor/symfony/translation/TranslatorBagInterface.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation; + +use Symfony\Component\Translation\Exception\InvalidArgumentException; + +/** + * TranslatorBagInterface. + * + * @author Abdellatif Ait boudad + */ +interface TranslatorBagInterface +{ + /** + * Gets the catalogue by locale. + * + * @param string|null $locale The locale or null to use the default + * + * @return MessageCatalogueInterface + * + * @throws InvalidArgumentException If the locale contains invalid characters + */ + public function getCatalogue($locale = null); +} diff --git a/vendor/symfony/translation/TranslatorInterface.php b/vendor/symfony/translation/TranslatorInterface.php new file mode 100644 index 000000000..9fcfd5bcf --- /dev/null +++ b/vendor/symfony/translation/TranslatorInterface.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation; + +use Symfony\Component\Translation\Exception\InvalidArgumentException; + +/** + * TranslatorInterface. + * + * @author Fabien Potencier + */ +interface TranslatorInterface +{ + /** + * Translates the given message. + * + * @param string $id The message id (may also be an object that can be cast to string) + * @param array $parameters An array of parameters for the message + * @param string|null $domain The domain for the message or null to use the default + * @param string|null $locale The locale or null to use the default + * + * @return string The translated string + * + * @throws InvalidArgumentException If the locale contains invalid characters + */ + public function trans($id, array $parameters = array(), $domain = null, $locale = null); + + /** + * Translates the given choice message by choosing a translation according to a number. + * + * @param string $id The message id (may also be an object that can be cast to string) + * @param int $number The number to use to find the indice of the message + * @param array $parameters An array of parameters for the message + * @param string|null $domain The domain for the message or null to use the default + * @param string|null $locale The locale or null to use the default + * + * @return string The translated string + * + * @throws InvalidArgumentException If the locale contains invalid characters + */ + public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null); + + /** + * Sets the current locale. + * + * @param string $locale The locale + * + * @throws InvalidArgumentException If the locale contains invalid characters + */ + public function setLocale($locale); + + /** + * Returns the current locale. + * + * @return string The locale + */ + public function getLocale(); +} diff --git a/vendor/symfony/translation/Util/ArrayConverter.php b/vendor/symfony/translation/Util/ArrayConverter.php new file mode 100644 index 000000000..b98e7ce82 --- /dev/null +++ b/vendor/symfony/translation/Util/ArrayConverter.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\Translation\Util; + +/** + * ArrayConverter generates tree like structure from a message catalogue. + * e.g. this + * 'foo.bar1' => 'test1', + * 'foo.bar2' => 'test2' + * converts to follows: + * foo: + * bar1: test1 + * bar2: test2. + * + * @author Gennady Telegin + */ +class ArrayConverter +{ + /** + * Converts linear messages array to tree-like array. + * For example this rray('foo.bar' => 'value') will be converted to array('foo' => array('bar' => 'value')). + * + * @param array $messages Linear messages array + * + * @return array Tree-like messages array + */ + public static function expandToTree(array $messages) + { + $tree = array(); + + foreach ($messages as $id => $value) { + $referenceToElement = &self::getElementByPath($tree, explode('.', $id)); + + $referenceToElement = $value; + + unset($referenceToElement); + } + + return $tree; + } + + private static function &getElementByPath(array &$tree, array $parts) + { + $elem = &$tree; + $parentOfElem = null; + + foreach ($parts as $i => $part) { + if (isset($elem[$part]) && \is_string($elem[$part])) { + /* Process next case: + * 'foo': 'test1', + * 'foo.bar': 'test2' + * + * $tree['foo'] was string before we found array {bar: test2}. + * Treat new element as string too, e.g. add $tree['foo.bar'] = 'test2'; + */ + $elem = &$elem[implode('.', \array_slice($parts, $i))]; + break; + } + $parentOfElem = &$elem; + $elem = &$elem[$part]; + } + + if (\is_array($elem) && \count($elem) > 0 && $parentOfElem) { + /* Process next case: + * 'foo.bar': 'test1' + * 'foo': 'test2' + * + * $tree['foo'] was array = {bar: 'test1'} before we found string constant `foo`. + * Cancel treating $tree['foo'] as array and cancel back it expansion, + * e.g. make it $tree['foo.bar'] = 'test1' again. + */ + self::cancelExpand($parentOfElem, $part, $elem); + } + + return $elem; + } + + private static function cancelExpand(array &$tree, $prefix, array $node) + { + $prefix .= '.'; + + foreach ($node as $id => $value) { + if (\is_string($value)) { + $tree[$prefix.$id] = $value; + } else { + self::cancelExpand($tree, $prefix.$id, $value); + } + } + } +} diff --git a/vendor/symfony/translation/Writer/TranslationWriter.php b/vendor/symfony/translation/Writer/TranslationWriter.php new file mode 100644 index 000000000..762733b4f --- /dev/null +++ b/vendor/symfony/translation/Writer/TranslationWriter.php @@ -0,0 +1,90 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Writer; + +use Symfony\Component\Translation\Dumper\DumperInterface; +use Symfony\Component\Translation\Exception\InvalidArgumentException; +use Symfony\Component\Translation\Exception\RuntimeException; +use Symfony\Component\Translation\MessageCatalogue; + +/** + * TranslationWriter writes translation messages. + * + * @author Michel Salib + */ +class TranslationWriter implements TranslationWriterInterface +{ + private $dumpers = array(); + + /** + * Adds a dumper to the writer. + * + * @param string $format The format of the dumper + * @param DumperInterface $dumper The dumper + */ + public function addDumper($format, DumperInterface $dumper) + { + $this->dumpers[$format] = $dumper; + } + + /** + * Disables dumper backup. + * + * @deprecated since Symfony 4.1 + */ + public function disableBackup() + { + @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1.', __METHOD__), E_USER_DEPRECATED); + + foreach ($this->dumpers as $dumper) { + if (method_exists($dumper, 'setBackup')) { + $dumper->setBackup(false); + } + } + } + + /** + * Obtains the list of supported formats. + * + * @return array + */ + public function getFormats() + { + return array_keys($this->dumpers); + } + + /** + * Writes translation from the catalogue according to the selected format. + * + * @param MessageCatalogue $catalogue The message catalogue to write + * @param string $format The format to use to dump the messages + * @param array $options Options that are passed to the dumper + * + * @throws InvalidArgumentException + */ + public function write(MessageCatalogue $catalogue, $format, $options = array()) + { + if (!isset($this->dumpers[$format])) { + throw new InvalidArgumentException(sprintf('There is no dumper associated with format "%s".', $format)); + } + + // get the right dumper + $dumper = $this->dumpers[$format]; + + if (isset($options['path']) && !is_dir($options['path']) && !@mkdir($options['path'], 0777, true) && !is_dir($options['path'])) { + throw new RuntimeException(sprintf('Translation Writer was not able to create directory "%s"', $options['path'])); + } + + // save + $dumper->dump($catalogue, $options); + } +} diff --git a/vendor/symfony/translation/Writer/TranslationWriterInterface.php b/vendor/symfony/translation/Writer/TranslationWriterInterface.php new file mode 100644 index 000000000..992ab769a --- /dev/null +++ b/vendor/symfony/translation/Writer/TranslationWriterInterface.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Writer; + +use Symfony\Component\Translation\Exception\InvalidArgumentException; +use Symfony\Component\Translation\MessageCatalogue; + +/** + * TranslationWriter writes translation messages. + * + * @author Michel Salib + */ +interface TranslationWriterInterface +{ + /** + * Writes translation from the catalogue according to the selected format. + * + * @param MessageCatalogue $catalogue The message catalogue to write + * @param string $format The format to use to dump the messages + * @param array $options Options that are passed to the dumper + * + * @throws InvalidArgumentException + */ + public function write(MessageCatalogue $catalogue, $format, $options = array()); +} diff --git a/vendor/symfony/translation/composer.json b/vendor/symfony/translation/composer.json new file mode 100644 index 000000000..64ab46b31 --- /dev/null +++ b/vendor/symfony/translation/composer.json @@ -0,0 +1,53 @@ +{ + "name": "symfony/translation", + "type": "library", + "description": "Symfony Translation Component", + "keywords": [], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": "^7.1.3", + "symfony/polyfill-mbstring": "~1.0" + }, + "require-dev": { + "symfony/config": "~3.4|~4.0", + "symfony/console": "~3.4|~4.0", + "symfony/dependency-injection": "~3.4|~4.0", + "symfony/intl": "~3.4|~4.0", + "symfony/yaml": "~3.4|~4.0", + "symfony/finder": "~2.8|~3.0|~4.0", + "psr/log": "~1.0" + }, + "conflict": { + "symfony/config": "<3.4", + "symfony/dependency-injection": "<3.4", + "symfony/yaml": "<3.4" + }, + "suggest": { + "symfony/config": "", + "symfony/yaml": "", + "psr/log-implementation": "To use logging capability in translator" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\Translation\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + } +} diff --git a/vendor/symfony/translation/phpunit.xml.dist b/vendor/symfony/translation/phpunit.xml.dist new file mode 100644 index 000000000..1fafa4691 --- /dev/null +++ b/vendor/symfony/translation/phpunit.xml.dist @@ -0,0 +1,30 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Tests + ./vendor + + + +