├── .dockerignore ├── .gitignore ├── .php_cs ├── CHANGELOG.md ├── Dockerfile ├── LICENSE ├── README.md ├── behat.yml ├── bin └── xmllint ├── composer.json ├── composer.lock ├── phpunit.xml.dist ├── src └── php │ ├── bootstrap.php │ ├── console │ ├── application │ │ └── Application.php │ └── command │ │ ├── Command.php │ │ ├── HelpCommand.php │ │ └── LintCommand.php │ ├── data │ ├── FileReport.php │ └── ValidationProblem.php │ └── validator │ ├── LintValidation.php │ ├── ValidationCollection.php │ ├── ValidationFactory.php │ ├── ValidationInterface.php │ ├── XsdValidation.php │ └── helper │ └── LibXmlErrorFormatter.php ├── tests ├── functional │ ├── _testdata │ │ ├── broken.xml │ │ ├── fourtytwo.xml │ │ ├── logo.svg │ │ ├── schema.xsd │ │ ├── with_xsd.xml │ │ └── with_xsd_broken.xml │ ├── contexts │ │ └── FeatureContext.php │ └── xml-lint.feature └── unit │ ├── _testdata │ ├── comment_first.xml │ ├── empty.xml │ ├── empty.xsd │ ├── with_bad_url_xsd.xml │ ├── with_empty_xsd.xml │ └── with_not_existing_xsd.xml │ ├── bootstrap.inc.php │ ├── check-coverage.php │ ├── data │ ├── FileReportTest.php │ └── ValidationProblemTest.php │ └── validator │ ├── LintValidationTest.php │ ├── ValidationCollectionTest.php │ ├── ValidationFactoryTest.php │ ├── XsdValidationTest.php │ └── helper │ └── LibXmlErrorFormatterTest.php └── tools └── php-cs-fixer ├── .gitignore ├── composer.json └── composer.lock /.dockerignore: -------------------------------------------------------------------------------- 1 | vendor 2 | *.cache 3 | composer.lock 4 | tools/php-cs-fixer/composer.lock 5 | .git 6 | .idea 7 | Dockerfile 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | build/ 3 | .idea/ 4 | .php_cs.cache 5 | .phpunit.result.cache 6 | -------------------------------------------------------------------------------- /.php_cs: -------------------------------------------------------------------------------- 1 | true, 5 | 'array_syntax' => array('syntax' => 'short'), 6 | 'combine_consecutive_unsets' => true, 7 | 'no_useless_else' => true, 8 | 'no_useless_return' => true, 9 | 'ordered_imports' => ['sortAlgorithm' => 'length'], 10 | 'concat_space' => [ 11 | 'spacing' => 'one', 12 | ], 13 | 'yoda_style' => false, 14 | 'dir_constant' => false, 15 | 'phpdoc_indent' => false, 16 | 'phpdoc_annotation_without_dot' => false, 17 | 'phpdoc_no_empty_return' => true, 18 | 'phpdoc_add_missing_param_annotation' => true, 19 | 'phpdoc_order' => true, 20 | 'phpdoc_types_order' => true, 21 | 'general_phpdoc_annotation_remove' => [ 22 | 'author' 23 | ], 24 | 'void_return' => false, 25 | 'single_trait_insert_per_statement' => false, 26 | 'ternary_to_null_coalescing' => true, 27 | 'pow_to_exponentiation' => false, 28 | 'random_api_migration' => false, 29 | 'declare_strict_types' => false, 30 | 'phpdoc_no_alias_tag' => [ 31 | 'replacements' => [ 32 | 'type' => 'var', 33 | 'link' => 'see', 34 | ], 35 | ], 36 | 'header_comment' => [ 37 | 'commentType' => 'PHPDoc', 38 | 'header' => 'This file is part of the Sclable Xml Lint Package. 39 | 40 | @copyright (c) ' . date('Y') . ' Sclable Business Solutions GmbH 41 | 42 | For the full copyright and license information, please view the LICENSE 43 | file that was distributed with this source code.', 44 | 'location' => 'after_declare_strict', 45 | //'separate' => 'bottom', 46 | ], 47 | ]; 48 | 49 | return PhpCsFixer\Config::create() 50 | ->setFinder( 51 | PhpCsFixer\Finder::create() 52 | ->in(__DIR__ . '/src') 53 | ->in(__DIR__ . '/tests') 54 | ->in(__DIR__ . '/bin') 55 | // Note: The pattern is seen relative from one of the `->in()` 56 | // directories. And works for files too this way. 57 | ->notPath('bootstrap.php') 58 | ) 59 | ->setRules($fixers); 60 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Sclable XML Lint - Changelog 2 | ============================ 3 | 4 | 0.8.0 5 | ----- 6 | 7 | * Add support for Symfony 7 8 | * Drop support for Symfony 4 9 | ============================ 10 | 11 | 0.7.0 12 | ----- 13 | 14 | * Add support for PHP 8.3 15 | 16 | 0.6.0 17 | ----- 18 | 19 | * Add support for PHP 8.2 20 | * Drop support for PHP 7.4 21 | 22 | 0.5.0 23 | ----- 24 | 25 | * Add support for PHP 8.1 26 | * Drop support for PHP 7.3 27 | * Chores: remove Travis integration 28 | 29 | 0.4.0 30 | ----- 31 | 32 | * Add support for PHP 8 33 | * Drop support for PHP 7.2 34 | * Chores: Travis integration update 35 | 36 | 0.3.0 37 | ----- 38 | 39 | * Add support for symfony console / finder 5.* 40 | * Drop compatibility for PHP <7.2 and eol symfony versions 41 | * Update test frameworks to latest versions 42 | 43 | 44 | 0.2.4 45 | ----- 46 | 47 | * update dependencies to symfony/finder & symfony/console (include 4.*) 48 | 49 | 50 | 0.2.3 51 | ----- 52 | 53 | * clean up composer file (move tests to autoload-dev) 54 | 55 | 0.2.2 56 | ----- 57 | 58 | * update dependencies to symfony/finder & symfony/console 59 | 60 | 61 | 0.2.1 62 | ----- 63 | 64 | * fix a bug where patterns were empty 65 | 66 | 0.2.0 67 | ----- 68 | 69 | * validate xml files against XSD schemata (with skip option) 70 | * pattern option 71 | 72 | 73 | 0.1.0 74 | ----- 75 | 76 | * cli interface 77 | * lint xml file 78 | * lint files in a directory 79 | * recursive option (default is 'yes') 80 | * exclude option 81 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | ARG PHP_VERSION 2 | FROM php:${PHP_VERSION}-cli-alpine AS dependencies 3 | ARG PHP_CS_FIXER 4 | RUN apk add --update --no-cache zip unzip php-zip 5 | COPY --from=composer /usr/bin/composer /usr/bin/composer 6 | RUN addgroup -S php && adduser -S php -G php \ 7 | && mkdir -p /usr/src/xml-lint \ 8 | && chown php:php -R /usr/src/xml-lint 9 | WORKDIR /usr/src/xml-lint 10 | COPY --chown=php:php . ./ 11 | USER php 12 | RUN composer install --prefer-dist -o -a -n --no-progress \ 13 | && \ 14 | if [[ -n "${PHP_CS_FIXER}" ]]; then \ 15 | composer install --working-dir=tools/php-cs-fixer --prefer-dist -o -a -n --no-progress; \ 16 | fi 17 | 18 | FROM php:${PHP_VERSION}-cli-alpine AS test 19 | ARG PHP_CS_FIXER 20 | RUN addgroup -S php && adduser -S php -G php \ 21 | && mkdir -p /usr/src/xml-lint \ 22 | && chown php:php -R /usr/src/xml-lint 23 | 24 | WORKDIR /usr/src/xml-lint 25 | COPY --from=dependencies --chown=php:php /usr/src/xml-lint ./ 26 | USER php 27 | 28 | RUN if [[ -n "${PHP_CS_FIXER}" ]]; then \ 29 | php tools/php-cs-fixer/vendor/bin/php-cs-fixer fix --dry-run -v; \ 30 | fi 31 | RUN php vendor/bin/phpunit 32 | RUN php vendor/bin/behat 33 | 34 | FROM dependencies AS build_production 35 | WORKDIR /usr/src/xml-lint 36 | 37 | RUN rm -rf tools/ tests/ \ 38 | && composer install --prefer-dist -o -a -n --no-progress --no-dev 39 | 40 | FROM php:${PHP_VERSION}-cli-alpine AS production 41 | WORKDIR /usr/src/xml-lint 42 | COPY --from=build_production /usr/src/xml-lint ./ 43 | RUN ln -s /usr/src/xml-lint/bin/xmllint /usr/bin/xml-lint 44 | WORKDIR /usr/src 45 | ENTRYPOINT ["php", "/usr/src/xml-lint/bin/xmllint"] 46 | CMD ["--help"] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Sclable Business Solutions GmbH 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Sclable XML Lint 2 | ================ 3 | 4 | A php tool to lint and validate xml files from the commandline. 5 | 6 | [![Build Status](https://travis-ci.com/sclable/xml-lint.svg?branch=main)](https://travis-ci.com/sclable/xml-lint) [![Latest Stable Version](https://poser.pugx.org/sclable/xml-lint/v)](//packagist.org/packages/sclable/xml-lint) [![Total Downloads](https://poser.pugx.org/sclable/xml-lint/downloads)](//packagist.org/packages/sclable/xml-lint) [![License](https://poser.pugx.org/sclable/xml-lint/license)](//packagist.org/packages/sclable/xml-lint) 7 | 8 | XML Lint checks the syntax of any xml files and validates the file against the XSD schema defined in the file. 9 | 10 | Usage 11 | ----- 12 | 13 | ### Installation with Composer 14 | 15 | If you'd like to include this library in your project with [composer](https://getcomposer.org/), simply run: 16 | 17 | composer require "sclable/xml-lint" 18 | 19 | ### Command Line Usage 20 | 21 | To lint a single xml file: 22 | 23 | vendor/bin/xmllint path/to/file.xml 24 | 25 | To lint a directory and all its subdirectories: 26 | 27 | vendor/bin/xmllint path/to/dir 28 | 29 | #### Help 30 | 31 | `xmllint` has built in cli help screen: 32 | 33 | vendor/bin/xmllint --help 34 | 35 | #### Options 36 | 37 | * `-v` be verbose, display the filename of the current file to lint 38 | * `-r 0` don't search recursive (if the argument is a directory) 39 | * `-e name` exclude files or directories containing 'name' 40 | * `-s` skip the xsd validation 41 | 42 | 43 | Development 44 | ----------- 45 | 46 | ### Run tests 47 | 48 | ```shell 49 | # check code style 50 | php tools/php-cs-fixer/vendor/bin/php-cs-fixer fix --dry-run -v 51 | 52 | # run tests 53 | php vendor/bin/phpunit 54 | php vendor/bin/behat 55 | ``` 56 | 57 | Using docker: 58 | 59 | ```shell 60 | # Example 61 | docker build -t xml-lint:php-8.3 --build-arg=PHP_VERSION="8.3" . 62 | 63 | # PHP_VERSION: choose between 8.0, 8.1, 8.2 and 8.3 64 | docker build -t xml-lint:php-8.0 --build-arg=PHP_VERSION="8.0" . 65 | docker build -t xml-lint:php-8.1 --build-arg=PHP_VERSION="8.1" . 66 | docker build -t xml-lint:php-8.2 --build-arg=PHP_VERSION="8.2" . 67 | docker build -t xml-lint:php-8.3 --build-arg=PHP_VERSION="8.3" . 68 | 69 | # Run with code style check 70 | docker build -t xml-lint:php-8.3 --build-arg=PHP_VERSION="8.3" --build-arg=PHP_CS_FIXER=true . 71 | 72 | # Use this image to run xml-lint: 73 | cd tests/functional/_testdata 74 | docker run -it --rm -v "$PWD":/var/src -w /var/src xml-lint:php-8.3 -r -v -- ./ 75 | ``` 76 | 77 | 78 | Changelog 79 | --------- 80 | 81 | For the changelog see the [CHANGELOG](CHANGELOG) file 82 | 83 | License 84 | ------- 85 | 86 | For the license and copyright see the [LICENSE](LICENSE) file 87 | -------------------------------------------------------------------------------- /behat.yml: -------------------------------------------------------------------------------- 1 | # behat configuration 2 | default: 3 | suites: 4 | default: 5 | paths: [ "%paths.base%/tests/functional" ] 6 | contexts: [ sclable\xmlLint\tests\functional\contexts\FeatureContext ] 7 | -------------------------------------------------------------------------------- /bin/xmllint: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | run(); -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sclable/xml-lint", 3 | "description": "A php cli tool to lint and validate xml files.", 4 | "license": "MIT", 5 | "authors": [ 6 | { 7 | "name": "Michael Rutz", 8 | "email": "michael.rutz@sclable.com" 9 | } 10 | ], 11 | "keywords": [ 12 | "xml", 13 | "lint", 14 | "cli", 15 | "xsd", 16 | "xml schema" 17 | ], 18 | "bin": ["bin/xmllint"], 19 | "require": { 20 | "php": "8.0.*|8.1.*|8.2.*|8.3.*", 21 | "symfony/console": "5.3.*|5.4.*|6.*|7.*", 22 | "symfony/finder": "5.3.*|5.4.*|6.*|7.*", 23 | "ext-libxml": "*", 24 | "ext-dom": "*" 25 | }, 26 | "require-dev": { 27 | "behat/behat": "^3.0", 28 | "phpunit/phpunit": "^9.1" 29 | }, 30 | "autoload": { 31 | "psr-4": { 32 | "sclable\\xmlLint\\": "src/php/" 33 | } 34 | }, 35 | "autoload-dev": { 36 | "psr-4": { 37 | "sclable\\xmlLint\\tests\\": "tests/" 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | src/php/ 6 | 7 | 8 | src/php/console/ 9 | src/php/bootstrap.php 10 | 11 | 12 | 13 | 14 | tests/unit 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/php/bootstrap.php: -------------------------------------------------------------------------------- 1 | setDefaultCommand(LintCommand::COMMAND_NAME); 40 | } 41 | 42 | /** 43 | * {@inheritdoc} 44 | */ 45 | protected function getDefaultCommands(): array 46 | { 47 | parent::getDefaultCommands(); 48 | 49 | return [ 50 | new HelpCommand(), 51 | new LintCommand(), 52 | ]; 53 | } 54 | 55 | /** 56 | * {@inheritdoc} 57 | * 58 | * @SuppressWarnings(PHPMD.Superglobals) 59 | */ 60 | public function run(InputInterface $input = null, OutputInterface $output = null): int 61 | { 62 | if (null === $input) { 63 | // rewrite the input for single command usage 64 | $argv = $_SERVER['argv']; 65 | $scriptName = array_shift($argv); 66 | array_unshift($argv, 'lint'); 67 | array_unshift($argv, $scriptName); 68 | $input = new ArgvInput($argv); 69 | } 70 | 71 | return parent::run($input, $output); 72 | } 73 | 74 | /** 75 | * {@inheritdoc} 76 | */ 77 | protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output): int 78 | { 79 | if ('version' != $command->getName()) { 80 | $output->writeln($this->getLongVersion()); 81 | } 82 | 83 | return parent::doRunCommand($command, $input, $output); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/php/console/command/Command.php: -------------------------------------------------------------------------------- 1 | synopsis[$key])) { 34 | $this->synopsis[$key] = trim(sprintf( 35 | '%s %s', 36 | $_SERVER['PHP_SELF'], 37 | $this->getDefinition()->getSynopsis($short) 38 | )); 39 | } 40 | 41 | return $this->synopsis[$key]; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/php/console/command/HelpCommand.php: -------------------------------------------------------------------------------- 1 | [--option|-o] ` 24 | * customized: `xmllint [--option|-o] ` 25 | * 26 | * @see \Symfony\Component\Console\Command\HelpCommand 27 | */ 28 | class HelpCommand extends \Symfony\Component\Console\Command\HelpCommand 29 | { 30 | /** 31 | * {@inheritdoc} 32 | */ 33 | protected function configure() 34 | { 35 | $this->ignoreValidationErrors(); 36 | 37 | $this 38 | ->setName('help') 39 | ->setDefinition([ 40 | new InputOption('xml', null, InputOption::VALUE_NONE, 'To output help as XML'), 41 | new InputOption( 42 | 'format', 43 | null, 44 | InputOption::VALUE_REQUIRED, 45 | 'The output format (txt, xml, json, or md)', 46 | 'txt' 47 | ), 48 | new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command help'), 49 | ]) 50 | ->setDescription('Display help') 51 | ->setHelp(<<%command.name% command displays the help. 53 | You can also output the help in other formats by using the --format option: 54 | 55 | php %command.full_name% --help --format=xml 56 | EOF 57 | ); 58 | } 59 | 60 | /** 61 | * {@inheritdoc} 62 | */ 63 | protected function execute(InputInterface $input, OutputInterface $output): int 64 | { 65 | $this->setCommand( 66 | $this->getApplication()->find(LintCommand::COMMAND_NAME) 67 | ); 68 | 69 | return parent::execute($input, $output); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/php/console/command/LintCommand.php: -------------------------------------------------------------------------------- 1 | setName(self::COMMAND_NAME) 62 | ->setDescription('lint an xml file') 63 | ->addArgument( 64 | self::ARGUMENT_FILE, 65 | InputArgument::REQUIRED, 66 | 'the path/to/file.xml to lint a single file or a path/to/directory to lint all xml ' . 67 | 'files in a directory.' 68 | ) 69 | ->addOption( 70 | self::OPTION_RECURSIVE, 71 | 'r', 72 | InputOption::VALUE_OPTIONAL, 73 | 'Whether to scan directories recursive.', 74 | true 75 | ) 76 | ->addOption( 77 | self::OPTION_EXCLUDE, 78 | 'e', 79 | InputOption::VALUE_REQUIRED, 80 | 'Path(s) to exclude from linting, can be several separated by comma' 81 | ) 82 | ->addOption( 83 | self::OPTION_PATTERN, 84 | 'p', 85 | InputOption::VALUE_REQUIRED, 86 | 'Filter files with one or more patterns, e.g.: *.svg,*.xml. Separate patterns by comma.' 87 | ) 88 | ->addOption( 89 | self::OPTION_NO_XSD, 90 | 's', 91 | InputOption::VALUE_NONE, 92 | 'Skip downloading and checking against XSD-files.' 93 | ) 94 | ; 95 | } 96 | 97 | /** 98 | * {@inheritdoc} 99 | */ 100 | protected function execute(InputInterface $input, OutputInterface $output): int 101 | { 102 | $this->start = microtime(true); 103 | $this->output = $output; 104 | $this->input = $input; 105 | 106 | if ($input->getOption(self::OPTION_NO_XSD)) { 107 | $this->validator = ValidationFactory::createLintOnlyValidation(); 108 | } else { 109 | $this->validator = ValidationFactory::createDefaultCollection(); 110 | } 111 | 112 | $file = $input->getArgument(self::ARGUMENT_FILE); 113 | 114 | $output->writeln('progress: '); 115 | if (is_dir($file)) { 116 | $status = $this->lintDir($file); 117 | } else { 118 | $status = $this->lintFile(new \SplFileInfo($file)); 119 | } 120 | 121 | $output->writeln(''); 122 | 123 | if (false === $status) { 124 | $this->printReportsOfFilesWithProblems(); 125 | } 126 | 127 | $this->output->writeln(sprintf( 128 | PHP_EOL . '%d files / %1.2f seconds done', 129 | count($this->reports), 130 | microtime(true) - $this->start 131 | )); 132 | 133 | return $status ? 0 : 1; 134 | } 135 | 136 | /** 137 | * lint the content of a directory, recursive if defined. 138 | * 139 | * @param string $dir path/to/dir 140 | * 141 | * @return bool 142 | */ 143 | private function lintDir($dir) 144 | { 145 | $finder = Finder::create(); 146 | $finder->files() 147 | ->in($dir); 148 | 149 | $patterns = $this->input->getOption(self::OPTION_PATTERN); 150 | if (!empty($patterns)) { 151 | $patterns = explode(',', $patterns); 152 | foreach ($patterns as $pattern) { 153 | $finder->name(trim($pattern)); 154 | } 155 | } else { 156 | $finder->name('*.xml.dist') 157 | ->name('*.xml'); 158 | } 159 | 160 | if (!$this->input->getOption(self::OPTION_RECURSIVE)) { 161 | $finder->depth(0); 162 | } 163 | 164 | if ($this->input->hasOption(self::OPTION_EXCLUDE)) { 165 | $exclude = explode(',', (string) $this->input->getOption(self::OPTION_EXCLUDE)); 166 | $finder->exclude($exclude); 167 | } 168 | 169 | $totalFiles = $finder->count(); 170 | 171 | $counter = 0; 172 | $ret = true; 173 | /** @var SplFileInfo $file */ 174 | foreach ($finder as $file) { 175 | $ret = $this->lintFile($file) && $ret; 176 | if (0 == ++$counter % 30) { 177 | $this->output->writeln(sprintf( 178 | ' %8d/%d %6.2f%%', 179 | $counter, 180 | $totalFiles, 181 | $counter / $totalFiles * 100 182 | )); 183 | } 184 | } 185 | 186 | return $ret; 187 | } 188 | 189 | /** 190 | * format and print the errors from the queue to the output. 191 | */ 192 | private function printReportsOfFilesWithProblems() 193 | { 194 | $this->output->writeln(PHP_EOL . 'errors:'); 195 | 196 | foreach ($this->reports as $report) { 197 | if (false === $report->hasProblems()) { 198 | continue; 199 | } 200 | 201 | $file = $report->getFile(); 202 | $this->output->writeln('file: ' . $file->getPath() . DIRECTORY_SEPARATOR . $file->getFilename()); 203 | 204 | foreach ($report->getProblems() as $problem) { 205 | $this->output->write( 206 | ' > ' . $problem->getMessage() . PHP_EOL 207 | ); 208 | } 209 | 210 | $this->output->write(' - - ' . PHP_EOL); 211 | } 212 | } 213 | 214 | /** 215 | * lint a file, pass errors to the queue. 216 | * 217 | * @return bool 218 | */ 219 | private function lintFile(\SplFileInfo $file) 220 | { 221 | if ($this->output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) { 222 | $this->output->write('lint file ' . $file . ' ... '); 223 | } 224 | 225 | $report = FileReport::create($file); 226 | $this->reports[] = $report; 227 | $status = $this->validator->validateFile($report); 228 | 229 | if ($this->output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) { 230 | if (false === $status) { 231 | $this->output->writeln('errors'); 232 | } else { 233 | $this->output->writeln('passed.'); 234 | } 235 | } else { 236 | if (false === $status) { 237 | $this->output->write('F'); 238 | } else { 239 | $this->output->write('.'); 240 | } 241 | } 242 | 243 | return $status; 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /src/php/data/FileReport.php: -------------------------------------------------------------------------------- 1 | file = $file; 31 | } 32 | 33 | /** 34 | * @param \SplFileInfo|string $file 35 | * 36 | * @return static 37 | */ 38 | public static function create($file) 39 | { 40 | if (false === ($file instanceof \SplFileInfo)) { 41 | $file = new \SplFileInfo($file); 42 | } 43 | 44 | return new static($file); 45 | } 46 | 47 | /** 48 | * @param string $msg 49 | * 50 | * @return $this 51 | */ 52 | public function reportProblem($msg) 53 | { 54 | $this->addProblem(ValidationProblem::create($msg)); 55 | 56 | return $this; 57 | } 58 | 59 | /** 60 | * report a problem to a file. 61 | * 62 | * @return $this 63 | */ 64 | public function addProblem(ValidationProblem $problem) 65 | { 66 | $this->problems[] = $problem; 67 | 68 | return $this; 69 | } 70 | 71 | /** 72 | * indicate whether a file has any reported problems or not. 73 | * 74 | * @return bool 75 | */ 76 | public function hasProblems() 77 | { 78 | return !empty($this->problems); 79 | } 80 | 81 | /** 82 | * @return \SplFileInfo 83 | */ 84 | public function getFile() 85 | { 86 | return $this->file; 87 | } 88 | 89 | /** 90 | * @return ValidationProblem[] 91 | */ 92 | public function getProblems() 93 | { 94 | return $this->problems; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/php/data/ValidationProblem.php: -------------------------------------------------------------------------------- 1 | msg = $message; 42 | } 43 | 44 | /** 45 | * @return string 46 | */ 47 | public function getMessage() 48 | { 49 | return $this->msg; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/php/validator/LintValidation.php: -------------------------------------------------------------------------------- 1 | formatter = $formatter; 31 | libxml_use_internal_errors(true); 32 | } 33 | 34 | /** 35 | * {@inheritdoc} 36 | */ 37 | public function validateFile(FileReport $report) 38 | { 39 | $realPath = $report->getFile()->getRealPath(); 40 | 41 | if (false === is_file($realPath)) { 42 | $report->reportProblem('file not found: ' . $realPath); 43 | 44 | return false; 45 | } 46 | 47 | if (false === is_readable($realPath)) { 48 | $report->reportProblem('file not readable: ' . $realPath); 49 | 50 | return false; 51 | } 52 | 53 | libxml_clear_errors(); 54 | $domDoc = new \DOMDocument(); 55 | $domDoc->load($realPath, LIBXML_NOERROR | LIBXML_NOWARNING | LIBXML_PEDANTIC); 56 | 57 | $errors = libxml_get_errors(); 58 | foreach ($this->formatter->formatErrors($errors) as $problem) { 59 | $report->reportProblem($problem); 60 | } 61 | 62 | return !$report->hasProblems(); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/php/validator/ValidationCollection.php: -------------------------------------------------------------------------------- 1 | collection as $validation) { 31 | $status = $validation->validateFile($report) && $status; 32 | } 33 | 34 | return $status; 35 | } 36 | 37 | public function addValidation(ValidationInterface $validation) 38 | { 39 | $this->collection[] = $validation; 40 | 41 | return $this; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/php/validator/ValidationFactory.php: -------------------------------------------------------------------------------- 1 | addValidation(new LintValidation($formatter)) 31 | ->addValidation(new XsdValidation($formatter)); 32 | 33 | return $collection; 34 | } 35 | 36 | /** 37 | * @return ValidationInterface 38 | */ 39 | public static function createLintOnlyValidation() 40 | { 41 | $formatter = new LibXmlErrorFormatter(); 42 | 43 | return new LintValidation($formatter); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/php/validator/ValidationInterface.php: -------------------------------------------------------------------------------- 1 | formatter = $formatter; 34 | libxml_use_internal_errors(true); 35 | } 36 | 37 | /** 38 | * {@inheritdoc} 39 | */ 40 | public function validateFile(FileReport $report) 41 | { 42 | $file = $report->getFile()->getRealPath(); 43 | 44 | if (empty($file)) { 45 | return false; 46 | } 47 | 48 | $domDoc = new \DOMDocument(); 49 | $loaded = $domDoc->load($file, LIBXML_NOERROR | LIBXML_NOWARNING | LIBXML_PEDANTIC); 50 | 51 | if (false === $loaded) { 52 | return false; 53 | } 54 | 55 | $validation = $this->getSchemaValidationFile($domDoc); 56 | 57 | if (false === $validation) { 58 | return true; 59 | } 60 | 61 | $validationSource = $this->getSchemaValidationSource($validation, $report); 62 | 63 | if (false === $validationSource) { 64 | return false; 65 | } 66 | 67 | libxml_clear_errors(); 68 | if (true !== $domDoc->schemaValidateSource($validationSource)) { 69 | $errors = libxml_get_errors(); 70 | foreach ($this->formatter->formatErrors($errors) as $problem) { 71 | $report->reportProblem($problem); 72 | } 73 | 74 | return false; 75 | } 76 | 77 | return true; 78 | } 79 | 80 | /** 81 | * @param string $filename 82 | * @param FileReport $report 83 | * 84 | * @return bool|string 85 | */ 86 | private function getSchemaValidationSource($filename, $report) 87 | { 88 | if ((0 === preg_match('/^(http|https|ftp):/i', $filename))) { 89 | if (false === file_exists($filename)) { 90 | $filename = $report->getFile()->getPath() . '/' . $filename; 91 | } 92 | if (!is_readable($filename)) { 93 | $report->reportProblem('unable to validate, schema file is not readable: ' . $filename); 94 | 95 | return false; 96 | } 97 | } 98 | 99 | if (isset($this->cache[$filename])) { 100 | return $this->cache[$filename]; 101 | } 102 | 103 | $validationSource = @file_get_contents($filename); 104 | 105 | if (false === $validationSource) { 106 | $report->reportProblem('unable to load schema file from: ' . $filename); 107 | 108 | return false; 109 | } 110 | 111 | if (empty($validationSource)) { 112 | $report->reportProblem(sprintf('xsd validation file is empty ("%s").', $filename)); 113 | 114 | return false; 115 | } 116 | 117 | return $this->cache[$filename] = $validationSource; 118 | } 119 | 120 | /** 121 | * @return bool|string 122 | */ 123 | private function getSchemaValidationFile(\DOMDocument $document) 124 | { 125 | $firstChild = $this->getFirstChild($document); 126 | // @codeCoverageIgnoreStart 127 | if (false === $firstChild) { 128 | return false; 129 | } 130 | // @codeCoverageIgnoreEnd 131 | 132 | $attribute = $firstChild->getAttribute('xsi:noNamespaceSchemaLocation'); 133 | 134 | if (empty($attribute)) { 135 | return false; 136 | } 137 | 138 | return $attribute; 139 | } 140 | 141 | /** 142 | * @return bool|\DOMElement 143 | */ 144 | private function getFirstChild(\DOMDocument $document) 145 | { 146 | foreach ($document->childNodes as $child) { 147 | if ($child instanceof \DOMElement) { 148 | return $child; 149 | // @codeCoverageIgnoreStart 150 | } 151 | } 152 | 153 | return false; 154 | // @codeCoverageIgnoreEnd 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/php/validator/helper/LibXmlErrorFormatter.php: -------------------------------------------------------------------------------- 1 | format($xmlError); 26 | } 27 | 28 | return array_unique($messages); 29 | } 30 | 31 | private function format($xmlError) 32 | { 33 | return sprintf( 34 | $this->format, 35 | $xmlError->line, 36 | $xmlError->code, 37 | trim($xmlError->message) 38 | ); 39 | } 40 | 41 | public function setFormat($format) 42 | { 43 | $this->format = $format; 44 | 45 | return $this; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /tests/functional/_testdata/broken.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /tests/functional/_testdata/fourtytwo.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | unknown 6 | 42 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /tests/functional/_testdata/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 16 | 17 | -------------------------------------------------------------------------------- /tests/functional/_testdata/schema.xsd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /tests/functional/_testdata/with_xsd.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | unknown 7 | 42 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /tests/functional/_testdata/with_xsd_broken.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | unknown 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /tests/functional/contexts/FeatureContext.php: -------------------------------------------------------------------------------- 1 | application = new Application(); 46 | 47 | $command = $this->application->find('lint'); 48 | 49 | $this->commandTester = new CommandTester($command); 50 | } 51 | 52 | /** 53 | * @Given the file :file 54 | * 55 | * @param $file 56 | */ 57 | public function theFile($file) 58 | { 59 | $this->file = dirname(__DIR__) . '/_testdata/' . $file; 60 | } 61 | 62 | /** 63 | * @When I run lint 64 | */ 65 | public function iRunLint() 66 | { 67 | $this->exitCode = $this->commandTester->execute([ 68 | 'file' => $this->file, 69 | ]); 70 | } 71 | 72 | /** 73 | * @Then I have a return code :code 74 | * 75 | * @param $code 76 | * 77 | * @throws \Exception 78 | */ 79 | public function iHaveAReturnCode($code) 80 | { 81 | $code = (int) $code; 82 | 83 | if (null === $this->exitCode) { 84 | echo $this->commandTester->getDisplay(); 85 | throw new \Exception('the return code was NULL.'); 86 | } 87 | 88 | if ($this->exitCode !== $code) { 89 | echo $this->commandTester->getDisplay(); 90 | throw new \Exception(sprintf("the return code does not match. \n Expected: %s\n Actual: %s", $code, $this->exitCode)); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /tests/functional/xml-lint.feature: -------------------------------------------------------------------------------- 1 | Feature: Check an xml file whether its structure seems to be ok or not. 2 | 3 | Scenario: Check and confirm a file to be ok 4 | Given the file "fourtytwo.xml" 5 | When I run lint 6 | Then I have a return code "0" 7 | 8 | Scenario: Check a file with a missing close tag 9 | Given the file "broken.xml" 10 | When I run lint 11 | Then I have a return code "1" 12 | 13 | Scenario: Check a file with an xsd schema validation 14 | Given the file "with_xsd_broken.xml" 15 | When I run lint 16 | Then I have a return code "1" 17 | 18 | -------------------------------------------------------------------------------- /tests/unit/_testdata/comment_first.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tests/unit/_testdata/empty.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tests/unit/_testdata/empty.xsd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sclable/xml-lint/8a114be04aa8cfb34aaea7dc5bb40c6ec96bcaf7/tests/unit/_testdata/empty.xsd -------------------------------------------------------------------------------- /tests/unit/_testdata/with_bad_url_xsd.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | unknown 7 | 42 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /tests/unit/_testdata/with_empty_xsd.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | unknown 7 | 42 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /tests/unit/_testdata/with_not_existing_xsd.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | unknown 7 | 42 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /tests/unit/bootstrap.inc.php: -------------------------------------------------------------------------------- 1 | xpath('//metrics'); 31 | $totalElements = 0; 32 | $checkedElements = 0; 33 | 34 | foreach ($metrics as $metric) { 35 | $totalElements += (int) $metric['elements']; 36 | $checkedElements += (int) $metric['coveredelements']; 37 | } 38 | 39 | $coverage = ($checkedElements / $totalElements) * 100; 40 | 41 | if ($coverage < $percentage) { 42 | echo 'Code coverage is ' . $coverage . '%, which is below the accepted ' . $percentage . '%' . PHP_EOL; 43 | exit(1); 44 | } 45 | 46 | echo 'Code coverage is ' . $coverage . '% - OK!' . PHP_EOL; 47 | -------------------------------------------------------------------------------- /tests/unit/data/FileReportTest.php: -------------------------------------------------------------------------------- 1 | assertFalse($report->hasProblems()); 27 | } 28 | 29 | public function testHasProblemsReturnsTrueOnProblems() 30 | { 31 | $report = new FileReport(new \SplFileInfo('no_file.xml')); 32 | $problem = $this->getMockBuilder(ValidationProblem::class) 33 | ->disableOriginalConstructor() 34 | ->getMock(); 35 | 36 | /* @var ValidationProblem $problem */ 37 | $report->addProblem($problem); 38 | $this->assertTrue($report->hasProblems()); 39 | } 40 | 41 | public function testCreateFromString() 42 | { 43 | $report = FileReport::create('no_file.xml'); 44 | $this->assertInstanceOf(FileReport::class, $report); 45 | } 46 | 47 | public function testReportProblemCreatesValidationProblem() 48 | { 49 | $report = FileReport::create('test_file.xml'); 50 | $report->reportProblem('my_message'); 51 | $result = $report->getProblems(); 52 | $problem = reset($result); 53 | 54 | $this->assertInstanceOf(ValidationProblem::class, $problem); 55 | $this->assertEquals('my_message', $problem->getMessage()); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /tests/unit/data/ValidationProblemTest.php: -------------------------------------------------------------------------------- 1 | assertEquals($msg, $problem->getMessage()); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tests/unit/validator/LintValidationTest.php: -------------------------------------------------------------------------------- 1 | getMockBuilder(FileReport::class) 32 | ->onlyMethods(['reportProblem']) 33 | ->setConstructorArgs([$file]) 34 | ->getMock(); 35 | $mock->expects($this->exactly(0)) 36 | ->method('reportProblem'); 37 | 38 | /** @var FileReport $mock */ 39 | $return = $validator->validateFile($mock); 40 | $this->assertTrue($return); 41 | } 42 | 43 | public function testReportProblemsForInvalidFile() 44 | { 45 | $validator = new LintValidation(new LibXmlErrorFormatter()); 46 | 47 | $filename = dirname(dirname(__DIR__)) . '/functional/_testdata/broken.xml'; 48 | $file = new \SplFileInfo($filename); 49 | 50 | $mock = $this->getMockBuilder(FileReport::class) 51 | ->onlyMethods(['reportProblem', 'hasProblems']) 52 | ->setConstructorArgs([$file]) 53 | ->getMock(); 54 | $mock->method('hasProblems')->willReturn(true); 55 | $mock->expects($this->exactly(3)) 56 | ->method('reportProblem'); 57 | 58 | /** @var FileReport $mock */ 59 | $return = $validator->validateFile($mock); 60 | $this->assertFalse($return); 61 | } 62 | 63 | public function testReportExceptionTextIfNoErrorsAvailable() 64 | { 65 | $validator = new LintValidation(new LibXmlErrorFormatter()); 66 | 67 | $file = new \SplFileInfo('does_not_exist.xml'); 68 | 69 | $mock = $this->getMockBuilder(FileReport::class) 70 | ->onlyMethods(['reportProblem']) 71 | ->setConstructorArgs([$file]) 72 | ->getMock(); 73 | $mock->expects($this->exactly(1)) 74 | ->method('reportProblem'); 75 | 76 | /** @var FileReport $mock */ 77 | $return = $validator->validateFile($mock); 78 | $this->assertFalse($return); 79 | } 80 | 81 | public function testReportXmlFileNotReadable() 82 | { 83 | $validator = new LintValidation(new LibXmlErrorFormatter()); 84 | $filename = dirname(dirname(__DIR__)) . '/functional/_testdata/fourtytwo.xml'; 85 | $file = new \SplFileInfo($filename); 86 | $fileMod = $file->getPerms(); 87 | try { 88 | chmod($filename, 0333); 89 | $mock = $this->getMockBuilder(FileReport::class) 90 | ->onlyMethods(['reportProblem']) 91 | ->setConstructorArgs([$file]) 92 | ->getMock(); 93 | $mock->expects($this->once()) 94 | ->method('reportProblem') 95 | ->with('file not readable: ' . $file->getRealPath()); 96 | 97 | /** @var FileReport $mock */ 98 | $return = $validator->validateFile($mock); 99 | $this->assertFalse($return); 100 | } finally { 101 | chmod($filename, $fileMod); 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /tests/unit/validator/ValidationCollectionTest.php: -------------------------------------------------------------------------------- 1 | getMockBuilder(ValidationInterface::class) 34 | ->getMock(); 35 | $mock2 = $this->getMockBuilder(ValidationInterface::class) 36 | ->getMock(); 37 | 38 | $mock1->method('validateFile') 39 | ->willReturn($return1); 40 | $mock2->method('validateFile') 41 | ->willReturn($return2); 42 | 43 | $collection = new ValidationCollection(); 44 | /* @var ValidationInterface $mock1 */ 45 | /* @var ValidationInterface $mock2 */ 46 | $collection->addValidation($mock1) 47 | ->addValidation($mock2); 48 | 49 | $this->assertEquals($expected, $collection->validateFile(FileReport::create('some_file.xml'))); 50 | } 51 | 52 | public function getMockReturnValues() 53 | { 54 | return [ 55 | [true, true, true], 56 | [false, false, false], 57 | [false, true, false], 58 | [true, false, false], 59 | ]; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /tests/unit/validator/ValidationFactoryTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf(ValidationInterface::class, $collection); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tests/unit/validator/XsdValidationTest.php: -------------------------------------------------------------------------------- 1 | validateFile($report); 35 | 36 | $this->assertFalse($report->hasProblems()); 37 | } 38 | 39 | public function testValidateCorruptFileWithXsd() 40 | { 41 | $file = new \SplFileInfo( 42 | dirname(dirname(__DIR__)) . '/functional/_testdata/with_xsd_broken.xml' 43 | ); 44 | $report = new FileReport($file); 45 | 46 | $validator = new XsdValidation(new LibXmlErrorFormatter()); 47 | $validator->validateFile($report); 48 | 49 | $this->assertTrue($report->hasProblems()); 50 | } 51 | 52 | public function testSkipNotExistingFile() 53 | { 54 | $file = new \SplFileInfo('not_exists.xml'); 55 | 56 | $mock = $this->getMockBuilder(FileReport::class) 57 | ->onlyMethods(['reportProblem']) 58 | ->setConstructorArgs([$file]) 59 | ->getMock(); 60 | $mock->expects($this->exactly(0)) 61 | ->method('reportProblem'); 62 | 63 | $validator = new XsdValidation(new LibXmlErrorFormatter()); 64 | /* @var FileReport $mock */ 65 | $this->assertFalse($validator->validateFile($mock)); 66 | } 67 | 68 | public function testSkipFileWithoutXsd() 69 | { 70 | $file = new \SplFileInfo( 71 | dirname(dirname(__DIR__)) . '/functional/_testdata/fourtytwo.xml' 72 | ); 73 | $report = new FileReport($file); 74 | 75 | $validator = new XsdValidation(new LibXmlErrorFormatter()); 76 | $return = $validator->validateFile($report); 77 | 78 | $this->assertTrue($return); 79 | $this->assertFalse($report->hasProblems()); 80 | } 81 | 82 | public function testReportNonExistingSchemaFile() 83 | { 84 | $file = new \SplFileInfo( 85 | dirname(__DIR__) . '/_testdata/with_not_existing_xsd.xml' 86 | ); 87 | 88 | $mock = $this->getMockBuilder(FileReport::class) 89 | ->onlyMethods(['reportProblem', 'hasProblems']) 90 | ->setConstructorArgs([$file]) 91 | ->getMock(); 92 | $mock->method('hasProblems')->willReturn(true); 93 | $mock->expects($this->once()) 94 | ->method('reportProblem') 95 | ->with( 96 | 'unable to validate, schema file is not readable: ' 97 | . dirname(__DIR__) . '/_testdata/i_dont_exist.xsd' 98 | ); 99 | 100 | $validator = new XsdValidation(new LibXmlErrorFormatter()); 101 | /* @var FileReport $mock */ 102 | $this->assertFalse($validator->validateFile($mock)); 103 | } 104 | 105 | /** 106 | * @covers :: 107 | */ 108 | public function testUseInternalCache() 109 | { 110 | $file = new \SplFileInfo( 111 | dirname(dirname(__DIR__)) . '/functional/_testdata/with_xsd.xml' 112 | ); 113 | $report = new FileReport($file); 114 | $validator = new XsdValidation(new LibXmlErrorFormatter()); 115 | $this->assertTrue($validator->validateFile($report)); 116 | $this->assertFalse($report->hasProblems()); 117 | $this->assertTrue($validator->validateFile($report)); 118 | } 119 | 120 | /** 121 | * @covers :: 122 | */ 123 | public function testEmptyXml() 124 | { 125 | $file = new \SplFileInfo( 126 | dirname(__DIR__) . '/_testdata/empty.xml' 127 | ); 128 | 129 | $report = new FileReport($file); 130 | $validator = new XsdValidation(new LibXmlErrorFormatter()); 131 | $this->assertFalse($validator->validateFile($report)); 132 | } 133 | 134 | /** 135 | * @covers :: 136 | */ 137 | public function testEmptyXsd() 138 | { 139 | $file = new \SplFileInfo( 140 | dirname(__DIR__) . '/_testdata/with_empty_xsd.xml' 141 | ); 142 | 143 | $mock = $this->getMockBuilder(FileReport::class) 144 | ->onlyMethods(['reportProblem', 'hasProblems']) 145 | ->setConstructorArgs([$file]) 146 | ->getMock(); 147 | $mock->method('hasProblems')->willReturn(true); 148 | $mock->expects($this->once()) 149 | ->method('reportProblem') 150 | ->with($this->stringContains('xsd validation file is empty')); 151 | 152 | $validator = new XsdValidation(new LibXmlErrorFormatter()); 153 | /* @var FileReport $mock */ 154 | $this->assertFalse($validator->validateFile($mock)); 155 | } 156 | 157 | /** 158 | * @covers :: 159 | */ 160 | public function testDeadUrlXsd() 161 | { 162 | $file = new \SplFileInfo( 163 | dirname(__DIR__) . '/_testdata/with_bad_url_xsd.xml' 164 | ); 165 | 166 | $mock = $this->getMockBuilder(FileReport::class) 167 | ->onlyMethods(['reportProblem', 'hasProblems']) 168 | ->setConstructorArgs([$file]) 169 | ->getMock(); 170 | $mock->method('hasProblems')->willReturn(true); 171 | $mock->expects($this->once()) 172 | ->method('reportProblem') 173 | ->with($this->stringContains('unable to load schema file from')); 174 | 175 | $validator = new XsdValidation(new LibXmlErrorFormatter()); 176 | /* @var FileReport $mock */ 177 | $this->assertFalse($validator->validateFile($mock)); 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /tests/unit/validator/helper/LibXmlErrorFormatterTest.php: -------------------------------------------------------------------------------- 1 | createLibXmlError('test', 1, 2)]; 25 | $this->assertEquals( 26 | [ 27 | 'Line 2: [1] test', 28 | ], 29 | (new LibXmlErrorFormatter())->formatErrors($errors) 30 | ); 31 | } 32 | 33 | public function testCustomFormatError() 34 | { 35 | $errors = [$this->createLibXmlError('test', 1, 2)]; 36 | $formatter = new LibXmlErrorFormatter(); 37 | $formatter->setFormat('%s / %s / %s'); 38 | $this->assertEquals( 39 | [ 40 | '2 / 1 / test', 41 | ], 42 | $formatter->formatErrors($errors) 43 | ); 44 | } 45 | 46 | public function testFilterDuplicates() 47 | { 48 | $errors = [ 49 | $this->createLibXmlError('test', 1, 2), 50 | $this->createLibXmlError('test', 1, 2), 51 | ]; 52 | 53 | $this->assertCount(1, (new LibXmlErrorFormatter())->formatErrors($errors)); 54 | } 55 | 56 | /** 57 | * @param string $msg 58 | * @param int $code 59 | * @param int $line 60 | * 61 | * @return \LibXMLError 62 | */ 63 | private function createLibXmlError($msg, $code, $line) 64 | { 65 | $error = new \LibXMLError(); 66 | $error->message = $msg; 67 | $error->code = $code; 68 | $error->line = $line; 69 | 70 | return $error; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /tools/php-cs-fixer/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !composer.json 3 | !composer.lock 4 | !.gitignore 5 | -------------------------------------------------------------------------------- /tools/php-cs-fixer/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require": { 3 | "friendsofphp/php-cs-fixer": "^2.17" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /tools/php-cs-fixer/composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "bd4e716f45e67e6f741fc209581c20eb", 8 | "packages": [ 9 | { 10 | "name": "composer/semver", 11 | "version": "3.2.4", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/composer/semver.git", 15 | "reference": "a02fdf930a3c1c3ed3a49b5f63859c0c20e10464" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/composer/semver/zipball/a02fdf930a3c1c3ed3a49b5f63859c0c20e10464", 20 | "reference": "a02fdf930a3c1c3ed3a49b5f63859c0c20e10464", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "php": "^5.3.2 || ^7.0 || ^8.0" 25 | }, 26 | "require-dev": { 27 | "phpstan/phpstan": "^0.12.54", 28 | "symfony/phpunit-bridge": "^4.2 || ^5" 29 | }, 30 | "type": "library", 31 | "extra": { 32 | "branch-alias": { 33 | "dev-main": "3.x-dev" 34 | } 35 | }, 36 | "autoload": { 37 | "psr-4": { 38 | "Composer\\Semver\\": "src" 39 | } 40 | }, 41 | "notification-url": "https://packagist.org/downloads/", 42 | "license": [ 43 | "MIT" 44 | ], 45 | "authors": [ 46 | { 47 | "name": "Nils Adermann", 48 | "email": "naderman@naderman.de", 49 | "homepage": "http://www.naderman.de" 50 | }, 51 | { 52 | "name": "Jordi Boggiano", 53 | "email": "j.boggiano@seld.be", 54 | "homepage": "http://seld.be" 55 | }, 56 | { 57 | "name": "Rob Bast", 58 | "email": "rob.bast@gmail.com", 59 | "homepage": "http://robbast.nl" 60 | } 61 | ], 62 | "description": "Semver library that offers utilities, version constraint parsing and validation.", 63 | "keywords": [ 64 | "semantic", 65 | "semver", 66 | "validation", 67 | "versioning" 68 | ], 69 | "support": { 70 | "irc": "irc://irc.freenode.org/composer", 71 | "issues": "https://github.com/composer/semver/issues", 72 | "source": "https://github.com/composer/semver/tree/3.2.4" 73 | }, 74 | "funding": [ 75 | { 76 | "url": "https://packagist.com", 77 | "type": "custom" 78 | }, 79 | { 80 | "url": "https://github.com/composer", 81 | "type": "github" 82 | }, 83 | { 84 | "url": "https://tidelift.com/funding/github/packagist/composer/composer", 85 | "type": "tidelift" 86 | } 87 | ], 88 | "time": "2020-11-13T08:59:24+00:00" 89 | }, 90 | { 91 | "name": "composer/xdebug-handler", 92 | "version": "1.4.5", 93 | "source": { 94 | "type": "git", 95 | "url": "https://github.com/composer/xdebug-handler.git", 96 | "reference": "f28d44c286812c714741478d968104c5e604a1d4" 97 | }, 98 | "dist": { 99 | "type": "zip", 100 | "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/f28d44c286812c714741478d968104c5e604a1d4", 101 | "reference": "f28d44c286812c714741478d968104c5e604a1d4", 102 | "shasum": "" 103 | }, 104 | "require": { 105 | "php": "^5.3.2 || ^7.0 || ^8.0", 106 | "psr/log": "^1.0" 107 | }, 108 | "require-dev": { 109 | "phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 8" 110 | }, 111 | "type": "library", 112 | "autoload": { 113 | "psr-4": { 114 | "Composer\\XdebugHandler\\": "src" 115 | } 116 | }, 117 | "notification-url": "https://packagist.org/downloads/", 118 | "license": [ 119 | "MIT" 120 | ], 121 | "authors": [ 122 | { 123 | "name": "John Stevenson", 124 | "email": "john-stevenson@blueyonder.co.uk" 125 | } 126 | ], 127 | "description": "Restarts a process without Xdebug.", 128 | "keywords": [ 129 | "Xdebug", 130 | "performance" 131 | ], 132 | "support": { 133 | "irc": "irc://irc.freenode.org/composer", 134 | "issues": "https://github.com/composer/xdebug-handler/issues", 135 | "source": "https://github.com/composer/xdebug-handler/tree/1.4.5" 136 | }, 137 | "funding": [ 138 | { 139 | "url": "https://packagist.com", 140 | "type": "custom" 141 | }, 142 | { 143 | "url": "https://github.com/composer", 144 | "type": "github" 145 | }, 146 | { 147 | "url": "https://tidelift.com/funding/github/packagist/composer/composer", 148 | "type": "tidelift" 149 | } 150 | ], 151 | "time": "2020-11-13T08:04:11+00:00" 152 | }, 153 | { 154 | "name": "doctrine/annotations", 155 | "version": "1.11.1", 156 | "source": { 157 | "type": "git", 158 | "url": "https://github.com/doctrine/annotations.git", 159 | "reference": "ce77a7ba1770462cd705a91a151b6c3746f9c6ad" 160 | }, 161 | "dist": { 162 | "type": "zip", 163 | "url": "https://api.github.com/repos/doctrine/annotations/zipball/ce77a7ba1770462cd705a91a151b6c3746f9c6ad", 164 | "reference": "ce77a7ba1770462cd705a91a151b6c3746f9c6ad", 165 | "shasum": "" 166 | }, 167 | "require": { 168 | "doctrine/lexer": "1.*", 169 | "ext-tokenizer": "*", 170 | "php": "^7.1 || ^8.0" 171 | }, 172 | "require-dev": { 173 | "doctrine/cache": "1.*", 174 | "doctrine/coding-standard": "^6.0 || ^8.1", 175 | "phpstan/phpstan": "^0.12.20", 176 | "phpunit/phpunit": "^7.5 || ^9.1.5" 177 | }, 178 | "type": "library", 179 | "extra": { 180 | "branch-alias": { 181 | "dev-master": "1.11.x-dev" 182 | } 183 | }, 184 | "autoload": { 185 | "psr-4": { 186 | "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" 187 | } 188 | }, 189 | "notification-url": "https://packagist.org/downloads/", 190 | "license": [ 191 | "MIT" 192 | ], 193 | "authors": [ 194 | { 195 | "name": "Guilherme Blanco", 196 | "email": "guilhermeblanco@gmail.com" 197 | }, 198 | { 199 | "name": "Roman Borschel", 200 | "email": "roman@code-factory.org" 201 | }, 202 | { 203 | "name": "Benjamin Eberlei", 204 | "email": "kontakt@beberlei.de" 205 | }, 206 | { 207 | "name": "Jonathan Wage", 208 | "email": "jonwage@gmail.com" 209 | }, 210 | { 211 | "name": "Johannes Schmitt", 212 | "email": "schmittjoh@gmail.com" 213 | } 214 | ], 215 | "description": "Docblock Annotations Parser", 216 | "homepage": "https://www.doctrine-project.org/projects/annotations.html", 217 | "keywords": [ 218 | "annotations", 219 | "docblock", 220 | "parser" 221 | ], 222 | "support": { 223 | "issues": "https://github.com/doctrine/annotations/issues", 224 | "source": "https://github.com/doctrine/annotations/tree/1.11.1" 225 | }, 226 | "time": "2020-10-26T10:28:16+00:00" 227 | }, 228 | { 229 | "name": "doctrine/lexer", 230 | "version": "1.2.1", 231 | "source": { 232 | "type": "git", 233 | "url": "https://github.com/doctrine/lexer.git", 234 | "reference": "e864bbf5904cb8f5bb334f99209b48018522f042" 235 | }, 236 | "dist": { 237 | "type": "zip", 238 | "url": "https://api.github.com/repos/doctrine/lexer/zipball/e864bbf5904cb8f5bb334f99209b48018522f042", 239 | "reference": "e864bbf5904cb8f5bb334f99209b48018522f042", 240 | "shasum": "" 241 | }, 242 | "require": { 243 | "php": "^7.2 || ^8.0" 244 | }, 245 | "require-dev": { 246 | "doctrine/coding-standard": "^6.0", 247 | "phpstan/phpstan": "^0.11.8", 248 | "phpunit/phpunit": "^8.2" 249 | }, 250 | "type": "library", 251 | "extra": { 252 | "branch-alias": { 253 | "dev-master": "1.2.x-dev" 254 | } 255 | }, 256 | "autoload": { 257 | "psr-4": { 258 | "Doctrine\\Common\\Lexer\\": "lib/Doctrine/Common/Lexer" 259 | } 260 | }, 261 | "notification-url": "https://packagist.org/downloads/", 262 | "license": [ 263 | "MIT" 264 | ], 265 | "authors": [ 266 | { 267 | "name": "Guilherme Blanco", 268 | "email": "guilhermeblanco@gmail.com" 269 | }, 270 | { 271 | "name": "Roman Borschel", 272 | "email": "roman@code-factory.org" 273 | }, 274 | { 275 | "name": "Johannes Schmitt", 276 | "email": "schmittjoh@gmail.com" 277 | } 278 | ], 279 | "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", 280 | "homepage": "https://www.doctrine-project.org/projects/lexer.html", 281 | "keywords": [ 282 | "annotations", 283 | "docblock", 284 | "lexer", 285 | "parser", 286 | "php" 287 | ], 288 | "support": { 289 | "issues": "https://github.com/doctrine/lexer/issues", 290 | "source": "https://github.com/doctrine/lexer/tree/1.2.1" 291 | }, 292 | "funding": [ 293 | { 294 | "url": "https://www.doctrine-project.org/sponsorship.html", 295 | "type": "custom" 296 | }, 297 | { 298 | "url": "https://www.patreon.com/phpdoctrine", 299 | "type": "patreon" 300 | }, 301 | { 302 | "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", 303 | "type": "tidelift" 304 | } 305 | ], 306 | "time": "2020-05-25T17:44:05+00:00" 307 | }, 308 | { 309 | "name": "friendsofphp/php-cs-fixer", 310 | "version": "v2.17.1", 311 | "source": { 312 | "type": "git", 313 | "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", 314 | "reference": "5198b7308ed63f26799387fd7f3901c3db6bd7fd" 315 | }, 316 | "dist": { 317 | "type": "zip", 318 | "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/5198b7308ed63f26799387fd7f3901c3db6bd7fd", 319 | "reference": "5198b7308ed63f26799387fd7f3901c3db6bd7fd", 320 | "shasum": "" 321 | }, 322 | "require": { 323 | "composer/semver": "^1.4 || ^2.0 || ^3.0", 324 | "composer/xdebug-handler": "^1.2", 325 | "doctrine/annotations": "^1.2", 326 | "ext-json": "*", 327 | "ext-tokenizer": "*", 328 | "php": "^5.6 || ^7.0 || ^8.0", 329 | "php-cs-fixer/diff": "^1.3", 330 | "symfony/console": "^3.4.43 || ^4.1.6 || ^5.0", 331 | "symfony/event-dispatcher": "^3.0 || ^4.0 || ^5.0", 332 | "symfony/filesystem": "^3.0 || ^4.0 || ^5.0", 333 | "symfony/finder": "^3.0 || ^4.0 || ^5.0", 334 | "symfony/options-resolver": "^3.0 || ^4.0 || ^5.0", 335 | "symfony/polyfill-php70": "^1.0", 336 | "symfony/polyfill-php72": "^1.4", 337 | "symfony/process": "^3.0 || ^4.0 || ^5.0", 338 | "symfony/stopwatch": "^3.0 || ^4.0 || ^5.0" 339 | }, 340 | "require-dev": { 341 | "johnkary/phpunit-speedtrap": "^1.1 || ^2.0 || ^3.0", 342 | "justinrainbow/json-schema": "^5.0", 343 | "keradus/cli-executor": "^1.4", 344 | "mikey179/vfsstream": "^1.6", 345 | "php-coveralls/php-coveralls": "^2.4.1", 346 | "php-cs-fixer/accessible-object": "^1.0", 347 | "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.2", 348 | "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.2.1", 349 | "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.1", 350 | "phpunitgoodpractices/polyfill": "^1.5", 351 | "phpunitgoodpractices/traits": "^1.9.1", 352 | "symfony/phpunit-bridge": "^5.1", 353 | "symfony/yaml": "^3.0 || ^4.0 || ^5.0" 354 | }, 355 | "suggest": { 356 | "ext-dom": "For handling output formats in XML", 357 | "ext-mbstring": "For handling non-UTF8 characters.", 358 | "php-cs-fixer/phpunit-constraint-isidenticalstring": "For IsIdenticalString constraint.", 359 | "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "For XmlMatchesXsd constraint.", 360 | "symfony/polyfill-mbstring": "When enabling `ext-mbstring` is not possible." 361 | }, 362 | "bin": [ 363 | "php-cs-fixer" 364 | ], 365 | "type": "application", 366 | "autoload": { 367 | "psr-4": { 368 | "PhpCsFixer\\": "src/" 369 | }, 370 | "classmap": [ 371 | "tests/Test/AbstractFixerTestCase.php", 372 | "tests/Test/AbstractIntegrationCaseFactory.php", 373 | "tests/Test/AbstractIntegrationTestCase.php", 374 | "tests/Test/Assert/AssertTokensTrait.php", 375 | "tests/Test/IntegrationCase.php", 376 | "tests/Test/IntegrationCaseFactory.php", 377 | "tests/Test/IntegrationCaseFactoryInterface.php", 378 | "tests/Test/InternalIntegrationCaseFactory.php", 379 | "tests/Test/IsIdenticalConstraint.php", 380 | "tests/TestCase.php" 381 | ] 382 | }, 383 | "notification-url": "https://packagist.org/downloads/", 384 | "license": [ 385 | "MIT" 386 | ], 387 | "authors": [ 388 | { 389 | "name": "Fabien Potencier", 390 | "email": "fabien@symfony.com" 391 | }, 392 | { 393 | "name": "Dariusz Rumiński", 394 | "email": "dariusz.ruminski@gmail.com" 395 | } 396 | ], 397 | "description": "A tool to automatically fix PHP code style", 398 | "support": { 399 | "issues": "https://github.com/FriendsOfPHP/PHP-CS-Fixer/issues", 400 | "source": "https://github.com/FriendsOfPHP/PHP-CS-Fixer/tree/v2.17.1" 401 | }, 402 | "funding": [ 403 | { 404 | "url": "https://github.com/keradus", 405 | "type": "github" 406 | } 407 | ], 408 | "time": "2020-12-08T13:47:02+00:00" 409 | }, 410 | { 411 | "name": "php-cs-fixer/diff", 412 | "version": "v1.3.1", 413 | "source": { 414 | "type": "git", 415 | "url": "https://github.com/PHP-CS-Fixer/diff.git", 416 | "reference": "dbd31aeb251639ac0b9e7e29405c1441907f5759" 417 | }, 418 | "dist": { 419 | "type": "zip", 420 | "url": "https://api.github.com/repos/PHP-CS-Fixer/diff/zipball/dbd31aeb251639ac0b9e7e29405c1441907f5759", 421 | "reference": "dbd31aeb251639ac0b9e7e29405c1441907f5759", 422 | "shasum": "" 423 | }, 424 | "require": { 425 | "php": "^5.6 || ^7.0 || ^8.0" 426 | }, 427 | "require-dev": { 428 | "phpunit/phpunit": "^5.7.23 || ^6.4.3 || ^7.0", 429 | "symfony/process": "^3.3" 430 | }, 431 | "type": "library", 432 | "autoload": { 433 | "classmap": [ 434 | "src/" 435 | ] 436 | }, 437 | "notification-url": "https://packagist.org/downloads/", 438 | "license": [ 439 | "BSD-3-Clause" 440 | ], 441 | "authors": [ 442 | { 443 | "name": "Sebastian Bergmann", 444 | "email": "sebastian@phpunit.de" 445 | }, 446 | { 447 | "name": "Kore Nordmann", 448 | "email": "mail@kore-nordmann.de" 449 | }, 450 | { 451 | "name": "SpacePossum" 452 | } 453 | ], 454 | "description": "sebastian/diff v2 backport support for PHP5.6", 455 | "homepage": "https://github.com/PHP-CS-Fixer", 456 | "keywords": [ 457 | "diff" 458 | ], 459 | "support": { 460 | "issues": "https://github.com/PHP-CS-Fixer/diff/issues", 461 | "source": "https://github.com/PHP-CS-Fixer/diff/tree/v1.3.1" 462 | }, 463 | "time": "2020-10-14T08:39:05+00:00" 464 | }, 465 | { 466 | "name": "psr/container", 467 | "version": "1.0.0", 468 | "source": { 469 | "type": "git", 470 | "url": "https://github.com/php-fig/container.git", 471 | "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" 472 | }, 473 | "dist": { 474 | "type": "zip", 475 | "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", 476 | "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", 477 | "shasum": "" 478 | }, 479 | "require": { 480 | "php": ">=5.3.0" 481 | }, 482 | "type": "library", 483 | "extra": { 484 | "branch-alias": { 485 | "dev-master": "1.0.x-dev" 486 | } 487 | }, 488 | "autoload": { 489 | "psr-4": { 490 | "Psr\\Container\\": "src/" 491 | } 492 | }, 493 | "notification-url": "https://packagist.org/downloads/", 494 | "license": [ 495 | "MIT" 496 | ], 497 | "authors": [ 498 | { 499 | "name": "PHP-FIG", 500 | "homepage": "http://www.php-fig.org/" 501 | } 502 | ], 503 | "description": "Common Container Interface (PHP FIG PSR-11)", 504 | "homepage": "https://github.com/php-fig/container", 505 | "keywords": [ 506 | "PSR-11", 507 | "container", 508 | "container-interface", 509 | "container-interop", 510 | "psr" 511 | ], 512 | "support": { 513 | "issues": "https://github.com/php-fig/container/issues", 514 | "source": "https://github.com/php-fig/container/tree/master" 515 | }, 516 | "time": "2017-02-14T16:28:37+00:00" 517 | }, 518 | { 519 | "name": "psr/event-dispatcher", 520 | "version": "1.0.0", 521 | "source": { 522 | "type": "git", 523 | "url": "https://github.com/php-fig/event-dispatcher.git", 524 | "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" 525 | }, 526 | "dist": { 527 | "type": "zip", 528 | "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", 529 | "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", 530 | "shasum": "" 531 | }, 532 | "require": { 533 | "php": ">=7.2.0" 534 | }, 535 | "type": "library", 536 | "extra": { 537 | "branch-alias": { 538 | "dev-master": "1.0.x-dev" 539 | } 540 | }, 541 | "autoload": { 542 | "psr-4": { 543 | "Psr\\EventDispatcher\\": "src/" 544 | } 545 | }, 546 | "notification-url": "https://packagist.org/downloads/", 547 | "license": [ 548 | "MIT" 549 | ], 550 | "authors": [ 551 | { 552 | "name": "PHP-FIG", 553 | "homepage": "http://www.php-fig.org/" 554 | } 555 | ], 556 | "description": "Standard interfaces for event handling.", 557 | "keywords": [ 558 | "events", 559 | "psr", 560 | "psr-14" 561 | ], 562 | "support": { 563 | "issues": "https://github.com/php-fig/event-dispatcher/issues", 564 | "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" 565 | }, 566 | "time": "2019-01-08T18:20:26+00:00" 567 | }, 568 | { 569 | "name": "psr/log", 570 | "version": "1.1.3", 571 | "source": { 572 | "type": "git", 573 | "url": "https://github.com/php-fig/log.git", 574 | "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc" 575 | }, 576 | "dist": { 577 | "type": "zip", 578 | "url": "https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc", 579 | "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc", 580 | "shasum": "" 581 | }, 582 | "require": { 583 | "php": ">=5.3.0" 584 | }, 585 | "type": "library", 586 | "extra": { 587 | "branch-alias": { 588 | "dev-master": "1.1.x-dev" 589 | } 590 | }, 591 | "autoload": { 592 | "psr-4": { 593 | "Psr\\Log\\": "Psr/Log/" 594 | } 595 | }, 596 | "notification-url": "https://packagist.org/downloads/", 597 | "license": [ 598 | "MIT" 599 | ], 600 | "authors": [ 601 | { 602 | "name": "PHP-FIG", 603 | "homepage": "http://www.php-fig.org/" 604 | } 605 | ], 606 | "description": "Common interface for logging libraries", 607 | "homepage": "https://github.com/php-fig/log", 608 | "keywords": [ 609 | "log", 610 | "psr", 611 | "psr-3" 612 | ], 613 | "support": { 614 | "source": "https://github.com/php-fig/log/tree/1.1.3" 615 | }, 616 | "time": "2020-03-23T09:12:05+00:00" 617 | }, 618 | { 619 | "name": "symfony/console", 620 | "version": "v5.2.0", 621 | "source": { 622 | "type": "git", 623 | "url": "https://github.com/symfony/console.git", 624 | "reference": "3e0564fb08d44a98bd5f1960204c958e57bd586b" 625 | }, 626 | "dist": { 627 | "type": "zip", 628 | "url": "https://api.github.com/repos/symfony/console/zipball/3e0564fb08d44a98bd5f1960204c958e57bd586b", 629 | "reference": "3e0564fb08d44a98bd5f1960204c958e57bd586b", 630 | "shasum": "" 631 | }, 632 | "require": { 633 | "php": ">=7.2.5", 634 | "symfony/polyfill-mbstring": "~1.0", 635 | "symfony/polyfill-php73": "^1.8", 636 | "symfony/polyfill-php80": "^1.15", 637 | "symfony/service-contracts": "^1.1|^2", 638 | "symfony/string": "^5.1" 639 | }, 640 | "conflict": { 641 | "symfony/dependency-injection": "<4.4", 642 | "symfony/dotenv": "<5.1", 643 | "symfony/event-dispatcher": "<4.4", 644 | "symfony/lock": "<4.4", 645 | "symfony/process": "<4.4" 646 | }, 647 | "provide": { 648 | "psr/log-implementation": "1.0" 649 | }, 650 | "require-dev": { 651 | "psr/log": "~1.0", 652 | "symfony/config": "^4.4|^5.0", 653 | "symfony/dependency-injection": "^4.4|^5.0", 654 | "symfony/event-dispatcher": "^4.4|^5.0", 655 | "symfony/lock": "^4.4|^5.0", 656 | "symfony/process": "^4.4|^5.0", 657 | "symfony/var-dumper": "^4.4|^5.0" 658 | }, 659 | "suggest": { 660 | "psr/log": "For using the console logger", 661 | "symfony/event-dispatcher": "", 662 | "symfony/lock": "", 663 | "symfony/process": "" 664 | }, 665 | "type": "library", 666 | "autoload": { 667 | "psr-4": { 668 | "Symfony\\Component\\Console\\": "" 669 | }, 670 | "exclude-from-classmap": [ 671 | "/Tests/" 672 | ] 673 | }, 674 | "notification-url": "https://packagist.org/downloads/", 675 | "license": [ 676 | "MIT" 677 | ], 678 | "authors": [ 679 | { 680 | "name": "Fabien Potencier", 681 | "email": "fabien@symfony.com" 682 | }, 683 | { 684 | "name": "Symfony Community", 685 | "homepage": "https://symfony.com/contributors" 686 | } 687 | ], 688 | "description": "Symfony Console Component", 689 | "homepage": "https://symfony.com", 690 | "keywords": [ 691 | "cli", 692 | "command line", 693 | "console", 694 | "terminal" 695 | ], 696 | "support": { 697 | "source": "https://github.com/symfony/console/tree/v5.2.0" 698 | }, 699 | "funding": [ 700 | { 701 | "url": "https://symfony.com/sponsor", 702 | "type": "custom" 703 | }, 704 | { 705 | "url": "https://github.com/fabpot", 706 | "type": "github" 707 | }, 708 | { 709 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 710 | "type": "tidelift" 711 | } 712 | ], 713 | "time": "2020-11-28T11:24:18+00:00" 714 | }, 715 | { 716 | "name": "symfony/deprecation-contracts", 717 | "version": "v2.2.0", 718 | "source": { 719 | "type": "git", 720 | "url": "https://github.com/symfony/deprecation-contracts.git", 721 | "reference": "5fa56b4074d1ae755beb55617ddafe6f5d78f665" 722 | }, 723 | "dist": { 724 | "type": "zip", 725 | "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/5fa56b4074d1ae755beb55617ddafe6f5d78f665", 726 | "reference": "5fa56b4074d1ae755beb55617ddafe6f5d78f665", 727 | "shasum": "" 728 | }, 729 | "require": { 730 | "php": ">=7.1" 731 | }, 732 | "type": "library", 733 | "extra": { 734 | "branch-alias": { 735 | "dev-master": "2.2-dev" 736 | }, 737 | "thanks": { 738 | "name": "symfony/contracts", 739 | "url": "https://github.com/symfony/contracts" 740 | } 741 | }, 742 | "autoload": { 743 | "files": [ 744 | "function.php" 745 | ] 746 | }, 747 | "notification-url": "https://packagist.org/downloads/", 748 | "license": [ 749 | "MIT" 750 | ], 751 | "authors": [ 752 | { 753 | "name": "Nicolas Grekas", 754 | "email": "p@tchwork.com" 755 | }, 756 | { 757 | "name": "Symfony Community", 758 | "homepage": "https://symfony.com/contributors" 759 | } 760 | ], 761 | "description": "A generic function and convention to trigger deprecation notices", 762 | "homepage": "https://symfony.com", 763 | "support": { 764 | "source": "https://github.com/symfony/deprecation-contracts/tree/master" 765 | }, 766 | "funding": [ 767 | { 768 | "url": "https://symfony.com/sponsor", 769 | "type": "custom" 770 | }, 771 | { 772 | "url": "https://github.com/fabpot", 773 | "type": "github" 774 | }, 775 | { 776 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 777 | "type": "tidelift" 778 | } 779 | ], 780 | "time": "2020-09-07T11:33:47+00:00" 781 | }, 782 | { 783 | "name": "symfony/event-dispatcher", 784 | "version": "v5.2.0", 785 | "source": { 786 | "type": "git", 787 | "url": "https://github.com/symfony/event-dispatcher.git", 788 | "reference": "aa13a09811e6d2ad43f8fb336bebdb7691d85d3c" 789 | }, 790 | "dist": { 791 | "type": "zip", 792 | "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/aa13a09811e6d2ad43f8fb336bebdb7691d85d3c", 793 | "reference": "aa13a09811e6d2ad43f8fb336bebdb7691d85d3c", 794 | "shasum": "" 795 | }, 796 | "require": { 797 | "php": ">=7.2.5", 798 | "symfony/deprecation-contracts": "^2.1", 799 | "symfony/event-dispatcher-contracts": "^2", 800 | "symfony/polyfill-php80": "^1.15" 801 | }, 802 | "conflict": { 803 | "symfony/dependency-injection": "<4.4" 804 | }, 805 | "provide": { 806 | "psr/event-dispatcher-implementation": "1.0", 807 | "symfony/event-dispatcher-implementation": "2.0" 808 | }, 809 | "require-dev": { 810 | "psr/log": "~1.0", 811 | "symfony/config": "^4.4|^5.0", 812 | "symfony/dependency-injection": "^4.4|^5.0", 813 | "symfony/error-handler": "^4.4|^5.0", 814 | "symfony/expression-language": "^4.4|^5.0", 815 | "symfony/http-foundation": "^4.4|^5.0", 816 | "symfony/service-contracts": "^1.1|^2", 817 | "symfony/stopwatch": "^4.4|^5.0" 818 | }, 819 | "suggest": { 820 | "symfony/dependency-injection": "", 821 | "symfony/http-kernel": "" 822 | }, 823 | "type": "library", 824 | "autoload": { 825 | "psr-4": { 826 | "Symfony\\Component\\EventDispatcher\\": "" 827 | }, 828 | "exclude-from-classmap": [ 829 | "/Tests/" 830 | ] 831 | }, 832 | "notification-url": "https://packagist.org/downloads/", 833 | "license": [ 834 | "MIT" 835 | ], 836 | "authors": [ 837 | { 838 | "name": "Fabien Potencier", 839 | "email": "fabien@symfony.com" 840 | }, 841 | { 842 | "name": "Symfony Community", 843 | "homepage": "https://symfony.com/contributors" 844 | } 845 | ], 846 | "description": "Symfony EventDispatcher Component", 847 | "homepage": "https://symfony.com", 848 | "support": { 849 | "source": "https://github.com/symfony/event-dispatcher/tree/v5.2.0" 850 | }, 851 | "funding": [ 852 | { 853 | "url": "https://symfony.com/sponsor", 854 | "type": "custom" 855 | }, 856 | { 857 | "url": "https://github.com/fabpot", 858 | "type": "github" 859 | }, 860 | { 861 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 862 | "type": "tidelift" 863 | } 864 | ], 865 | "time": "2020-11-01T16:14:45+00:00" 866 | }, 867 | { 868 | "name": "symfony/event-dispatcher-contracts", 869 | "version": "v2.2.0", 870 | "source": { 871 | "type": "git", 872 | "url": "https://github.com/symfony/event-dispatcher-contracts.git", 873 | "reference": "0ba7d54483095a198fa51781bc608d17e84dffa2" 874 | }, 875 | "dist": { 876 | "type": "zip", 877 | "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/0ba7d54483095a198fa51781bc608d17e84dffa2", 878 | "reference": "0ba7d54483095a198fa51781bc608d17e84dffa2", 879 | "shasum": "" 880 | }, 881 | "require": { 882 | "php": ">=7.2.5", 883 | "psr/event-dispatcher": "^1" 884 | }, 885 | "suggest": { 886 | "symfony/event-dispatcher-implementation": "" 887 | }, 888 | "type": "library", 889 | "extra": { 890 | "branch-alias": { 891 | "dev-master": "2.2-dev" 892 | }, 893 | "thanks": { 894 | "name": "symfony/contracts", 895 | "url": "https://github.com/symfony/contracts" 896 | } 897 | }, 898 | "autoload": { 899 | "psr-4": { 900 | "Symfony\\Contracts\\EventDispatcher\\": "" 901 | } 902 | }, 903 | "notification-url": "https://packagist.org/downloads/", 904 | "license": [ 905 | "MIT" 906 | ], 907 | "authors": [ 908 | { 909 | "name": "Nicolas Grekas", 910 | "email": "p@tchwork.com" 911 | }, 912 | { 913 | "name": "Symfony Community", 914 | "homepage": "https://symfony.com/contributors" 915 | } 916 | ], 917 | "description": "Generic abstractions related to dispatching event", 918 | "homepage": "https://symfony.com", 919 | "keywords": [ 920 | "abstractions", 921 | "contracts", 922 | "decoupling", 923 | "interfaces", 924 | "interoperability", 925 | "standards" 926 | ], 927 | "support": { 928 | "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v2.2.0" 929 | }, 930 | "funding": [ 931 | { 932 | "url": "https://symfony.com/sponsor", 933 | "type": "custom" 934 | }, 935 | { 936 | "url": "https://github.com/fabpot", 937 | "type": "github" 938 | }, 939 | { 940 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 941 | "type": "tidelift" 942 | } 943 | ], 944 | "time": "2020-09-07T11:33:47+00:00" 945 | }, 946 | { 947 | "name": "symfony/filesystem", 948 | "version": "v5.2.0", 949 | "source": { 950 | "type": "git", 951 | "url": "https://github.com/symfony/filesystem.git", 952 | "reference": "bb92ba7f38b037e531908590a858a04d85c0e238" 953 | }, 954 | "dist": { 955 | "type": "zip", 956 | "url": "https://api.github.com/repos/symfony/filesystem/zipball/bb92ba7f38b037e531908590a858a04d85c0e238", 957 | "reference": "bb92ba7f38b037e531908590a858a04d85c0e238", 958 | "shasum": "" 959 | }, 960 | "require": { 961 | "php": ">=7.2.5", 962 | "symfony/polyfill-ctype": "~1.8" 963 | }, 964 | "type": "library", 965 | "autoload": { 966 | "psr-4": { 967 | "Symfony\\Component\\Filesystem\\": "" 968 | }, 969 | "exclude-from-classmap": [ 970 | "/Tests/" 971 | ] 972 | }, 973 | "notification-url": "https://packagist.org/downloads/", 974 | "license": [ 975 | "MIT" 976 | ], 977 | "authors": [ 978 | { 979 | "name": "Fabien Potencier", 980 | "email": "fabien@symfony.com" 981 | }, 982 | { 983 | "name": "Symfony Community", 984 | "homepage": "https://symfony.com/contributors" 985 | } 986 | ], 987 | "description": "Symfony Filesystem Component", 988 | "homepage": "https://symfony.com", 989 | "support": { 990 | "source": "https://github.com/symfony/filesystem/tree/v5.2.0" 991 | }, 992 | "funding": [ 993 | { 994 | "url": "https://symfony.com/sponsor", 995 | "type": "custom" 996 | }, 997 | { 998 | "url": "https://github.com/fabpot", 999 | "type": "github" 1000 | }, 1001 | { 1002 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1003 | "type": "tidelift" 1004 | } 1005 | ], 1006 | "time": "2020-11-12T09:58:18+00:00" 1007 | }, 1008 | { 1009 | "name": "symfony/finder", 1010 | "version": "v5.2.0", 1011 | "source": { 1012 | "type": "git", 1013 | "url": "https://github.com/symfony/finder.git", 1014 | "reference": "fd8305521692f27eae3263895d1ef1571c71a78d" 1015 | }, 1016 | "dist": { 1017 | "type": "zip", 1018 | "url": "https://api.github.com/repos/symfony/finder/zipball/fd8305521692f27eae3263895d1ef1571c71a78d", 1019 | "reference": "fd8305521692f27eae3263895d1ef1571c71a78d", 1020 | "shasum": "" 1021 | }, 1022 | "require": { 1023 | "php": ">=7.2.5" 1024 | }, 1025 | "type": "library", 1026 | "autoload": { 1027 | "psr-4": { 1028 | "Symfony\\Component\\Finder\\": "" 1029 | }, 1030 | "exclude-from-classmap": [ 1031 | "/Tests/" 1032 | ] 1033 | }, 1034 | "notification-url": "https://packagist.org/downloads/", 1035 | "license": [ 1036 | "MIT" 1037 | ], 1038 | "authors": [ 1039 | { 1040 | "name": "Fabien Potencier", 1041 | "email": "fabien@symfony.com" 1042 | }, 1043 | { 1044 | "name": "Symfony Community", 1045 | "homepage": "https://symfony.com/contributors" 1046 | } 1047 | ], 1048 | "description": "Symfony Finder Component", 1049 | "homepage": "https://symfony.com", 1050 | "support": { 1051 | "source": "https://github.com/symfony/finder/tree/v5.2.0" 1052 | }, 1053 | "funding": [ 1054 | { 1055 | "url": "https://symfony.com/sponsor", 1056 | "type": "custom" 1057 | }, 1058 | { 1059 | "url": "https://github.com/fabpot", 1060 | "type": "github" 1061 | }, 1062 | { 1063 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1064 | "type": "tidelift" 1065 | } 1066 | ], 1067 | "time": "2020-11-18T09:42:36+00:00" 1068 | }, 1069 | { 1070 | "name": "symfony/options-resolver", 1071 | "version": "v5.2.0", 1072 | "source": { 1073 | "type": "git", 1074 | "url": "https://github.com/symfony/options-resolver.git", 1075 | "reference": "87a2a4a766244e796dd9cb9d6f58c123358cd986" 1076 | }, 1077 | "dist": { 1078 | "type": "zip", 1079 | "url": "https://api.github.com/repos/symfony/options-resolver/zipball/87a2a4a766244e796dd9cb9d6f58c123358cd986", 1080 | "reference": "87a2a4a766244e796dd9cb9d6f58c123358cd986", 1081 | "shasum": "" 1082 | }, 1083 | "require": { 1084 | "php": ">=7.2.5", 1085 | "symfony/deprecation-contracts": "^2.1", 1086 | "symfony/polyfill-php73": "~1.0", 1087 | "symfony/polyfill-php80": "^1.15" 1088 | }, 1089 | "type": "library", 1090 | "autoload": { 1091 | "psr-4": { 1092 | "Symfony\\Component\\OptionsResolver\\": "" 1093 | }, 1094 | "exclude-from-classmap": [ 1095 | "/Tests/" 1096 | ] 1097 | }, 1098 | "notification-url": "https://packagist.org/downloads/", 1099 | "license": [ 1100 | "MIT" 1101 | ], 1102 | "authors": [ 1103 | { 1104 | "name": "Fabien Potencier", 1105 | "email": "fabien@symfony.com" 1106 | }, 1107 | { 1108 | "name": "Symfony Community", 1109 | "homepage": "https://symfony.com/contributors" 1110 | } 1111 | ], 1112 | "description": "Symfony OptionsResolver Component", 1113 | "homepage": "https://symfony.com", 1114 | "keywords": [ 1115 | "config", 1116 | "configuration", 1117 | "options" 1118 | ], 1119 | "support": { 1120 | "source": "https://github.com/symfony/options-resolver/tree/v5.2.0" 1121 | }, 1122 | "funding": [ 1123 | { 1124 | "url": "https://symfony.com/sponsor", 1125 | "type": "custom" 1126 | }, 1127 | { 1128 | "url": "https://github.com/fabpot", 1129 | "type": "github" 1130 | }, 1131 | { 1132 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1133 | "type": "tidelift" 1134 | } 1135 | ], 1136 | "time": "2020-10-24T12:08:07+00:00" 1137 | }, 1138 | { 1139 | "name": "symfony/polyfill-ctype", 1140 | "version": "v1.20.0", 1141 | "source": { 1142 | "type": "git", 1143 | "url": "https://github.com/symfony/polyfill-ctype.git", 1144 | "reference": "f4ba089a5b6366e453971d3aad5fe8e897b37f41" 1145 | }, 1146 | "dist": { 1147 | "type": "zip", 1148 | "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/f4ba089a5b6366e453971d3aad5fe8e897b37f41", 1149 | "reference": "f4ba089a5b6366e453971d3aad5fe8e897b37f41", 1150 | "shasum": "" 1151 | }, 1152 | "require": { 1153 | "php": ">=7.1" 1154 | }, 1155 | "suggest": { 1156 | "ext-ctype": "For best performance" 1157 | }, 1158 | "type": "library", 1159 | "extra": { 1160 | "branch-alias": { 1161 | "dev-main": "1.20-dev" 1162 | }, 1163 | "thanks": { 1164 | "name": "symfony/polyfill", 1165 | "url": "https://github.com/symfony/polyfill" 1166 | } 1167 | }, 1168 | "autoload": { 1169 | "psr-4": { 1170 | "Symfony\\Polyfill\\Ctype\\": "" 1171 | }, 1172 | "files": [ 1173 | "bootstrap.php" 1174 | ] 1175 | }, 1176 | "notification-url": "https://packagist.org/downloads/", 1177 | "license": [ 1178 | "MIT" 1179 | ], 1180 | "authors": [ 1181 | { 1182 | "name": "Gert de Pagter", 1183 | "email": "BackEndTea@gmail.com" 1184 | }, 1185 | { 1186 | "name": "Symfony Community", 1187 | "homepage": "https://symfony.com/contributors" 1188 | } 1189 | ], 1190 | "description": "Symfony polyfill for ctype functions", 1191 | "homepage": "https://symfony.com", 1192 | "keywords": [ 1193 | "compatibility", 1194 | "ctype", 1195 | "polyfill", 1196 | "portable" 1197 | ], 1198 | "support": { 1199 | "source": "https://github.com/symfony/polyfill-ctype/tree/v1.20.0" 1200 | }, 1201 | "funding": [ 1202 | { 1203 | "url": "https://symfony.com/sponsor", 1204 | "type": "custom" 1205 | }, 1206 | { 1207 | "url": "https://github.com/fabpot", 1208 | "type": "github" 1209 | }, 1210 | { 1211 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1212 | "type": "tidelift" 1213 | } 1214 | ], 1215 | "time": "2020-10-23T14:02:19+00:00" 1216 | }, 1217 | { 1218 | "name": "symfony/polyfill-intl-grapheme", 1219 | "version": "v1.20.0", 1220 | "source": { 1221 | "type": "git", 1222 | "url": "https://github.com/symfony/polyfill-intl-grapheme.git", 1223 | "reference": "c7cf3f858ec7d70b89559d6e6eb1f7c2517d479c" 1224 | }, 1225 | "dist": { 1226 | "type": "zip", 1227 | "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/c7cf3f858ec7d70b89559d6e6eb1f7c2517d479c", 1228 | "reference": "c7cf3f858ec7d70b89559d6e6eb1f7c2517d479c", 1229 | "shasum": "" 1230 | }, 1231 | "require": { 1232 | "php": ">=7.1" 1233 | }, 1234 | "suggest": { 1235 | "ext-intl": "For best performance" 1236 | }, 1237 | "type": "library", 1238 | "extra": { 1239 | "branch-alias": { 1240 | "dev-main": "1.20-dev" 1241 | }, 1242 | "thanks": { 1243 | "name": "symfony/polyfill", 1244 | "url": "https://github.com/symfony/polyfill" 1245 | } 1246 | }, 1247 | "autoload": { 1248 | "psr-4": { 1249 | "Symfony\\Polyfill\\Intl\\Grapheme\\": "" 1250 | }, 1251 | "files": [ 1252 | "bootstrap.php" 1253 | ] 1254 | }, 1255 | "notification-url": "https://packagist.org/downloads/", 1256 | "license": [ 1257 | "MIT" 1258 | ], 1259 | "authors": [ 1260 | { 1261 | "name": "Nicolas Grekas", 1262 | "email": "p@tchwork.com" 1263 | }, 1264 | { 1265 | "name": "Symfony Community", 1266 | "homepage": "https://symfony.com/contributors" 1267 | } 1268 | ], 1269 | "description": "Symfony polyfill for intl's grapheme_* functions", 1270 | "homepage": "https://symfony.com", 1271 | "keywords": [ 1272 | "compatibility", 1273 | "grapheme", 1274 | "intl", 1275 | "polyfill", 1276 | "portable", 1277 | "shim" 1278 | ], 1279 | "support": { 1280 | "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.20.0" 1281 | }, 1282 | "funding": [ 1283 | { 1284 | "url": "https://symfony.com/sponsor", 1285 | "type": "custom" 1286 | }, 1287 | { 1288 | "url": "https://github.com/fabpot", 1289 | "type": "github" 1290 | }, 1291 | { 1292 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1293 | "type": "tidelift" 1294 | } 1295 | ], 1296 | "time": "2020-10-23T14:02:19+00:00" 1297 | }, 1298 | { 1299 | "name": "symfony/polyfill-intl-normalizer", 1300 | "version": "v1.20.0", 1301 | "source": { 1302 | "type": "git", 1303 | "url": "https://github.com/symfony/polyfill-intl-normalizer.git", 1304 | "reference": "727d1096295d807c309fb01a851577302394c897" 1305 | }, 1306 | "dist": { 1307 | "type": "zip", 1308 | "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/727d1096295d807c309fb01a851577302394c897", 1309 | "reference": "727d1096295d807c309fb01a851577302394c897", 1310 | "shasum": "" 1311 | }, 1312 | "require": { 1313 | "php": ">=7.1" 1314 | }, 1315 | "suggest": { 1316 | "ext-intl": "For best performance" 1317 | }, 1318 | "type": "library", 1319 | "extra": { 1320 | "branch-alias": { 1321 | "dev-main": "1.20-dev" 1322 | }, 1323 | "thanks": { 1324 | "name": "symfony/polyfill", 1325 | "url": "https://github.com/symfony/polyfill" 1326 | } 1327 | }, 1328 | "autoload": { 1329 | "psr-4": { 1330 | "Symfony\\Polyfill\\Intl\\Normalizer\\": "" 1331 | }, 1332 | "files": [ 1333 | "bootstrap.php" 1334 | ], 1335 | "classmap": [ 1336 | "Resources/stubs" 1337 | ] 1338 | }, 1339 | "notification-url": "https://packagist.org/downloads/", 1340 | "license": [ 1341 | "MIT" 1342 | ], 1343 | "authors": [ 1344 | { 1345 | "name": "Nicolas Grekas", 1346 | "email": "p@tchwork.com" 1347 | }, 1348 | { 1349 | "name": "Symfony Community", 1350 | "homepage": "https://symfony.com/contributors" 1351 | } 1352 | ], 1353 | "description": "Symfony polyfill for intl's Normalizer class and related functions", 1354 | "homepage": "https://symfony.com", 1355 | "keywords": [ 1356 | "compatibility", 1357 | "intl", 1358 | "normalizer", 1359 | "polyfill", 1360 | "portable", 1361 | "shim" 1362 | ], 1363 | "support": { 1364 | "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.20.0" 1365 | }, 1366 | "funding": [ 1367 | { 1368 | "url": "https://symfony.com/sponsor", 1369 | "type": "custom" 1370 | }, 1371 | { 1372 | "url": "https://github.com/fabpot", 1373 | "type": "github" 1374 | }, 1375 | { 1376 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1377 | "type": "tidelift" 1378 | } 1379 | ], 1380 | "time": "2020-10-23T14:02:19+00:00" 1381 | }, 1382 | { 1383 | "name": "symfony/polyfill-mbstring", 1384 | "version": "v1.20.0", 1385 | "source": { 1386 | "type": "git", 1387 | "url": "https://github.com/symfony/polyfill-mbstring.git", 1388 | "reference": "39d483bdf39be819deabf04ec872eb0b2410b531" 1389 | }, 1390 | "dist": { 1391 | "type": "zip", 1392 | "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/39d483bdf39be819deabf04ec872eb0b2410b531", 1393 | "reference": "39d483bdf39be819deabf04ec872eb0b2410b531", 1394 | "shasum": "" 1395 | }, 1396 | "require": { 1397 | "php": ">=7.1" 1398 | }, 1399 | "suggest": { 1400 | "ext-mbstring": "For best performance" 1401 | }, 1402 | "type": "library", 1403 | "extra": { 1404 | "branch-alias": { 1405 | "dev-main": "1.20-dev" 1406 | }, 1407 | "thanks": { 1408 | "name": "symfony/polyfill", 1409 | "url": "https://github.com/symfony/polyfill" 1410 | } 1411 | }, 1412 | "autoload": { 1413 | "psr-4": { 1414 | "Symfony\\Polyfill\\Mbstring\\": "" 1415 | }, 1416 | "files": [ 1417 | "bootstrap.php" 1418 | ] 1419 | }, 1420 | "notification-url": "https://packagist.org/downloads/", 1421 | "license": [ 1422 | "MIT" 1423 | ], 1424 | "authors": [ 1425 | { 1426 | "name": "Nicolas Grekas", 1427 | "email": "p@tchwork.com" 1428 | }, 1429 | { 1430 | "name": "Symfony Community", 1431 | "homepage": "https://symfony.com/contributors" 1432 | } 1433 | ], 1434 | "description": "Symfony polyfill for the Mbstring extension", 1435 | "homepage": "https://symfony.com", 1436 | "keywords": [ 1437 | "compatibility", 1438 | "mbstring", 1439 | "polyfill", 1440 | "portable", 1441 | "shim" 1442 | ], 1443 | "support": { 1444 | "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.20.0" 1445 | }, 1446 | "funding": [ 1447 | { 1448 | "url": "https://symfony.com/sponsor", 1449 | "type": "custom" 1450 | }, 1451 | { 1452 | "url": "https://github.com/fabpot", 1453 | "type": "github" 1454 | }, 1455 | { 1456 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1457 | "type": "tidelift" 1458 | } 1459 | ], 1460 | "time": "2020-10-23T14:02:19+00:00" 1461 | }, 1462 | { 1463 | "name": "symfony/polyfill-php70", 1464 | "version": "v1.20.0", 1465 | "source": { 1466 | "type": "git", 1467 | "url": "https://github.com/symfony/polyfill-php70.git", 1468 | "reference": "5f03a781d984aae42cebd18e7912fa80f02ee644" 1469 | }, 1470 | "dist": { 1471 | "type": "zip", 1472 | "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/5f03a781d984aae42cebd18e7912fa80f02ee644", 1473 | "reference": "5f03a781d984aae42cebd18e7912fa80f02ee644", 1474 | "shasum": "" 1475 | }, 1476 | "require": { 1477 | "php": ">=7.1" 1478 | }, 1479 | "type": "metapackage", 1480 | "extra": { 1481 | "branch-alias": { 1482 | "dev-main": "1.20-dev" 1483 | }, 1484 | "thanks": { 1485 | "name": "symfony/polyfill", 1486 | "url": "https://github.com/symfony/polyfill" 1487 | } 1488 | }, 1489 | "notification-url": "https://packagist.org/downloads/", 1490 | "license": [ 1491 | "MIT" 1492 | ], 1493 | "authors": [ 1494 | { 1495 | "name": "Nicolas Grekas", 1496 | "email": "p@tchwork.com" 1497 | }, 1498 | { 1499 | "name": "Symfony Community", 1500 | "homepage": "https://symfony.com/contributors" 1501 | } 1502 | ], 1503 | "description": "Symfony polyfill backporting some PHP 7.0+ features to lower PHP versions", 1504 | "homepage": "https://symfony.com", 1505 | "keywords": [ 1506 | "compatibility", 1507 | "polyfill", 1508 | "portable", 1509 | "shim" 1510 | ], 1511 | "support": { 1512 | "source": "https://github.com/symfony/polyfill-php70/tree/v1.20.0" 1513 | }, 1514 | "funding": [ 1515 | { 1516 | "url": "https://symfony.com/sponsor", 1517 | "type": "custom" 1518 | }, 1519 | { 1520 | "url": "https://github.com/fabpot", 1521 | "type": "github" 1522 | }, 1523 | { 1524 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1525 | "type": "tidelift" 1526 | } 1527 | ], 1528 | "time": "2020-10-23T14:02:19+00:00" 1529 | }, 1530 | { 1531 | "name": "symfony/polyfill-php72", 1532 | "version": "v1.20.0", 1533 | "source": { 1534 | "type": "git", 1535 | "url": "https://github.com/symfony/polyfill-php72.git", 1536 | "reference": "cede45fcdfabdd6043b3592e83678e42ec69e930" 1537 | }, 1538 | "dist": { 1539 | "type": "zip", 1540 | "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/cede45fcdfabdd6043b3592e83678e42ec69e930", 1541 | "reference": "cede45fcdfabdd6043b3592e83678e42ec69e930", 1542 | "shasum": "" 1543 | }, 1544 | "require": { 1545 | "php": ">=7.1" 1546 | }, 1547 | "type": "library", 1548 | "extra": { 1549 | "branch-alias": { 1550 | "dev-main": "1.20-dev" 1551 | }, 1552 | "thanks": { 1553 | "name": "symfony/polyfill", 1554 | "url": "https://github.com/symfony/polyfill" 1555 | } 1556 | }, 1557 | "autoload": { 1558 | "psr-4": { 1559 | "Symfony\\Polyfill\\Php72\\": "" 1560 | }, 1561 | "files": [ 1562 | "bootstrap.php" 1563 | ] 1564 | }, 1565 | "notification-url": "https://packagist.org/downloads/", 1566 | "license": [ 1567 | "MIT" 1568 | ], 1569 | "authors": [ 1570 | { 1571 | "name": "Nicolas Grekas", 1572 | "email": "p@tchwork.com" 1573 | }, 1574 | { 1575 | "name": "Symfony Community", 1576 | "homepage": "https://symfony.com/contributors" 1577 | } 1578 | ], 1579 | "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", 1580 | "homepage": "https://symfony.com", 1581 | "keywords": [ 1582 | "compatibility", 1583 | "polyfill", 1584 | "portable", 1585 | "shim" 1586 | ], 1587 | "support": { 1588 | "source": "https://github.com/symfony/polyfill-php72/tree/v1.20.0" 1589 | }, 1590 | "funding": [ 1591 | { 1592 | "url": "https://symfony.com/sponsor", 1593 | "type": "custom" 1594 | }, 1595 | { 1596 | "url": "https://github.com/fabpot", 1597 | "type": "github" 1598 | }, 1599 | { 1600 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1601 | "type": "tidelift" 1602 | } 1603 | ], 1604 | "time": "2020-10-23T14:02:19+00:00" 1605 | }, 1606 | { 1607 | "name": "symfony/polyfill-php73", 1608 | "version": "v1.20.0", 1609 | "source": { 1610 | "type": "git", 1611 | "url": "https://github.com/symfony/polyfill-php73.git", 1612 | "reference": "8ff431c517be11c78c48a39a66d37431e26a6bed" 1613 | }, 1614 | "dist": { 1615 | "type": "zip", 1616 | "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/8ff431c517be11c78c48a39a66d37431e26a6bed", 1617 | "reference": "8ff431c517be11c78c48a39a66d37431e26a6bed", 1618 | "shasum": "" 1619 | }, 1620 | "require": { 1621 | "php": ">=7.1" 1622 | }, 1623 | "type": "library", 1624 | "extra": { 1625 | "branch-alias": { 1626 | "dev-main": "1.20-dev" 1627 | }, 1628 | "thanks": { 1629 | "name": "symfony/polyfill", 1630 | "url": "https://github.com/symfony/polyfill" 1631 | } 1632 | }, 1633 | "autoload": { 1634 | "psr-4": { 1635 | "Symfony\\Polyfill\\Php73\\": "" 1636 | }, 1637 | "files": [ 1638 | "bootstrap.php" 1639 | ], 1640 | "classmap": [ 1641 | "Resources/stubs" 1642 | ] 1643 | }, 1644 | "notification-url": "https://packagist.org/downloads/", 1645 | "license": [ 1646 | "MIT" 1647 | ], 1648 | "authors": [ 1649 | { 1650 | "name": "Nicolas Grekas", 1651 | "email": "p@tchwork.com" 1652 | }, 1653 | { 1654 | "name": "Symfony Community", 1655 | "homepage": "https://symfony.com/contributors" 1656 | } 1657 | ], 1658 | "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", 1659 | "homepage": "https://symfony.com", 1660 | "keywords": [ 1661 | "compatibility", 1662 | "polyfill", 1663 | "portable", 1664 | "shim" 1665 | ], 1666 | "support": { 1667 | "source": "https://github.com/symfony/polyfill-php73/tree/v1.20.0" 1668 | }, 1669 | "funding": [ 1670 | { 1671 | "url": "https://symfony.com/sponsor", 1672 | "type": "custom" 1673 | }, 1674 | { 1675 | "url": "https://github.com/fabpot", 1676 | "type": "github" 1677 | }, 1678 | { 1679 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1680 | "type": "tidelift" 1681 | } 1682 | ], 1683 | "time": "2020-10-23T14:02:19+00:00" 1684 | }, 1685 | { 1686 | "name": "symfony/polyfill-php80", 1687 | "version": "v1.20.0", 1688 | "source": { 1689 | "type": "git", 1690 | "url": "https://github.com/symfony/polyfill-php80.git", 1691 | "reference": "e70aa8b064c5b72d3df2abd5ab1e90464ad009de" 1692 | }, 1693 | "dist": { 1694 | "type": "zip", 1695 | "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/e70aa8b064c5b72d3df2abd5ab1e90464ad009de", 1696 | "reference": "e70aa8b064c5b72d3df2abd5ab1e90464ad009de", 1697 | "shasum": "" 1698 | }, 1699 | "require": { 1700 | "php": ">=7.1" 1701 | }, 1702 | "type": "library", 1703 | "extra": { 1704 | "branch-alias": { 1705 | "dev-main": "1.20-dev" 1706 | }, 1707 | "thanks": { 1708 | "name": "symfony/polyfill", 1709 | "url": "https://github.com/symfony/polyfill" 1710 | } 1711 | }, 1712 | "autoload": { 1713 | "psr-4": { 1714 | "Symfony\\Polyfill\\Php80\\": "" 1715 | }, 1716 | "files": [ 1717 | "bootstrap.php" 1718 | ], 1719 | "classmap": [ 1720 | "Resources/stubs" 1721 | ] 1722 | }, 1723 | "notification-url": "https://packagist.org/downloads/", 1724 | "license": [ 1725 | "MIT" 1726 | ], 1727 | "authors": [ 1728 | { 1729 | "name": "Ion Bazan", 1730 | "email": "ion.bazan@gmail.com" 1731 | }, 1732 | { 1733 | "name": "Nicolas Grekas", 1734 | "email": "p@tchwork.com" 1735 | }, 1736 | { 1737 | "name": "Symfony Community", 1738 | "homepage": "https://symfony.com/contributors" 1739 | } 1740 | ], 1741 | "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", 1742 | "homepage": "https://symfony.com", 1743 | "keywords": [ 1744 | "compatibility", 1745 | "polyfill", 1746 | "portable", 1747 | "shim" 1748 | ], 1749 | "support": { 1750 | "source": "https://github.com/symfony/polyfill-php80/tree/v1.20.0" 1751 | }, 1752 | "funding": [ 1753 | { 1754 | "url": "https://symfony.com/sponsor", 1755 | "type": "custom" 1756 | }, 1757 | { 1758 | "url": "https://github.com/fabpot", 1759 | "type": "github" 1760 | }, 1761 | { 1762 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1763 | "type": "tidelift" 1764 | } 1765 | ], 1766 | "time": "2020-10-23T14:02:19+00:00" 1767 | }, 1768 | { 1769 | "name": "symfony/process", 1770 | "version": "v5.2.0", 1771 | "source": { 1772 | "type": "git", 1773 | "url": "https://github.com/symfony/process.git", 1774 | "reference": "240e74140d4d956265048f3025c0aecbbc302d54" 1775 | }, 1776 | "dist": { 1777 | "type": "zip", 1778 | "url": "https://api.github.com/repos/symfony/process/zipball/240e74140d4d956265048f3025c0aecbbc302d54", 1779 | "reference": "240e74140d4d956265048f3025c0aecbbc302d54", 1780 | "shasum": "" 1781 | }, 1782 | "require": { 1783 | "php": ">=7.2.5", 1784 | "symfony/polyfill-php80": "^1.15" 1785 | }, 1786 | "type": "library", 1787 | "autoload": { 1788 | "psr-4": { 1789 | "Symfony\\Component\\Process\\": "" 1790 | }, 1791 | "exclude-from-classmap": [ 1792 | "/Tests/" 1793 | ] 1794 | }, 1795 | "notification-url": "https://packagist.org/downloads/", 1796 | "license": [ 1797 | "MIT" 1798 | ], 1799 | "authors": [ 1800 | { 1801 | "name": "Fabien Potencier", 1802 | "email": "fabien@symfony.com" 1803 | }, 1804 | { 1805 | "name": "Symfony Community", 1806 | "homepage": "https://symfony.com/contributors" 1807 | } 1808 | ], 1809 | "description": "Symfony Process Component", 1810 | "homepage": "https://symfony.com", 1811 | "support": { 1812 | "source": "https://github.com/symfony/process/tree/v5.2.0" 1813 | }, 1814 | "funding": [ 1815 | { 1816 | "url": "https://symfony.com/sponsor", 1817 | "type": "custom" 1818 | }, 1819 | { 1820 | "url": "https://github.com/fabpot", 1821 | "type": "github" 1822 | }, 1823 | { 1824 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1825 | "type": "tidelift" 1826 | } 1827 | ], 1828 | "time": "2020-11-02T15:47:15+00:00" 1829 | }, 1830 | { 1831 | "name": "symfony/service-contracts", 1832 | "version": "v2.2.0", 1833 | "source": { 1834 | "type": "git", 1835 | "url": "https://github.com/symfony/service-contracts.git", 1836 | "reference": "d15da7ba4957ffb8f1747218be9e1a121fd298a1" 1837 | }, 1838 | "dist": { 1839 | "type": "zip", 1840 | "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d15da7ba4957ffb8f1747218be9e1a121fd298a1", 1841 | "reference": "d15da7ba4957ffb8f1747218be9e1a121fd298a1", 1842 | "shasum": "" 1843 | }, 1844 | "require": { 1845 | "php": ">=7.2.5", 1846 | "psr/container": "^1.0" 1847 | }, 1848 | "suggest": { 1849 | "symfony/service-implementation": "" 1850 | }, 1851 | "type": "library", 1852 | "extra": { 1853 | "branch-alias": { 1854 | "dev-master": "2.2-dev" 1855 | }, 1856 | "thanks": { 1857 | "name": "symfony/contracts", 1858 | "url": "https://github.com/symfony/contracts" 1859 | } 1860 | }, 1861 | "autoload": { 1862 | "psr-4": { 1863 | "Symfony\\Contracts\\Service\\": "" 1864 | } 1865 | }, 1866 | "notification-url": "https://packagist.org/downloads/", 1867 | "license": [ 1868 | "MIT" 1869 | ], 1870 | "authors": [ 1871 | { 1872 | "name": "Nicolas Grekas", 1873 | "email": "p@tchwork.com" 1874 | }, 1875 | { 1876 | "name": "Symfony Community", 1877 | "homepage": "https://symfony.com/contributors" 1878 | } 1879 | ], 1880 | "description": "Generic abstractions related to writing services", 1881 | "homepage": "https://symfony.com", 1882 | "keywords": [ 1883 | "abstractions", 1884 | "contracts", 1885 | "decoupling", 1886 | "interfaces", 1887 | "interoperability", 1888 | "standards" 1889 | ], 1890 | "support": { 1891 | "source": "https://github.com/symfony/service-contracts/tree/master" 1892 | }, 1893 | "funding": [ 1894 | { 1895 | "url": "https://symfony.com/sponsor", 1896 | "type": "custom" 1897 | }, 1898 | { 1899 | "url": "https://github.com/fabpot", 1900 | "type": "github" 1901 | }, 1902 | { 1903 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1904 | "type": "tidelift" 1905 | } 1906 | ], 1907 | "time": "2020-09-07T11:33:47+00:00" 1908 | }, 1909 | { 1910 | "name": "symfony/stopwatch", 1911 | "version": "v5.2.0", 1912 | "source": { 1913 | "type": "git", 1914 | "url": "https://github.com/symfony/stopwatch.git", 1915 | "reference": "2b105c0354f39a63038a1d8bf776ee92852813af" 1916 | }, 1917 | "dist": { 1918 | "type": "zip", 1919 | "url": "https://api.github.com/repos/symfony/stopwatch/zipball/2b105c0354f39a63038a1d8bf776ee92852813af", 1920 | "reference": "2b105c0354f39a63038a1d8bf776ee92852813af", 1921 | "shasum": "" 1922 | }, 1923 | "require": { 1924 | "php": ">=7.2.5", 1925 | "symfony/service-contracts": "^1.0|^2" 1926 | }, 1927 | "type": "library", 1928 | "autoload": { 1929 | "psr-4": { 1930 | "Symfony\\Component\\Stopwatch\\": "" 1931 | }, 1932 | "exclude-from-classmap": [ 1933 | "/Tests/" 1934 | ] 1935 | }, 1936 | "notification-url": "https://packagist.org/downloads/", 1937 | "license": [ 1938 | "MIT" 1939 | ], 1940 | "authors": [ 1941 | { 1942 | "name": "Fabien Potencier", 1943 | "email": "fabien@symfony.com" 1944 | }, 1945 | { 1946 | "name": "Symfony Community", 1947 | "homepage": "https://symfony.com/contributors" 1948 | } 1949 | ], 1950 | "description": "Symfony Stopwatch Component", 1951 | "homepage": "https://symfony.com", 1952 | "support": { 1953 | "source": "https://github.com/symfony/stopwatch/tree/v5.2.0" 1954 | }, 1955 | "funding": [ 1956 | { 1957 | "url": "https://symfony.com/sponsor", 1958 | "type": "custom" 1959 | }, 1960 | { 1961 | "url": "https://github.com/fabpot", 1962 | "type": "github" 1963 | }, 1964 | { 1965 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1966 | "type": "tidelift" 1967 | } 1968 | ], 1969 | "time": "2020-11-01T16:14:45+00:00" 1970 | }, 1971 | { 1972 | "name": "symfony/string", 1973 | "version": "v5.2.0", 1974 | "source": { 1975 | "type": "git", 1976 | "url": "https://github.com/symfony/string.git", 1977 | "reference": "40e975edadd4e32cd16f3753b3bad65d9ac48242" 1978 | }, 1979 | "dist": { 1980 | "type": "zip", 1981 | "url": "https://api.github.com/repos/symfony/string/zipball/40e975edadd4e32cd16f3753b3bad65d9ac48242", 1982 | "reference": "40e975edadd4e32cd16f3753b3bad65d9ac48242", 1983 | "shasum": "" 1984 | }, 1985 | "require": { 1986 | "php": ">=7.2.5", 1987 | "symfony/polyfill-ctype": "~1.8", 1988 | "symfony/polyfill-intl-grapheme": "~1.0", 1989 | "symfony/polyfill-intl-normalizer": "~1.0", 1990 | "symfony/polyfill-mbstring": "~1.0", 1991 | "symfony/polyfill-php80": "~1.15" 1992 | }, 1993 | "require-dev": { 1994 | "symfony/error-handler": "^4.4|^5.0", 1995 | "symfony/http-client": "^4.4|^5.0", 1996 | "symfony/translation-contracts": "^1.1|^2", 1997 | "symfony/var-exporter": "^4.4|^5.0" 1998 | }, 1999 | "type": "library", 2000 | "autoload": { 2001 | "psr-4": { 2002 | "Symfony\\Component\\String\\": "" 2003 | }, 2004 | "files": [ 2005 | "Resources/functions.php" 2006 | ], 2007 | "exclude-from-classmap": [ 2008 | "/Tests/" 2009 | ] 2010 | }, 2011 | "notification-url": "https://packagist.org/downloads/", 2012 | "license": [ 2013 | "MIT" 2014 | ], 2015 | "authors": [ 2016 | { 2017 | "name": "Nicolas Grekas", 2018 | "email": "p@tchwork.com" 2019 | }, 2020 | { 2021 | "name": "Symfony Community", 2022 | "homepage": "https://symfony.com/contributors" 2023 | } 2024 | ], 2025 | "description": "Symfony String component", 2026 | "homepage": "https://symfony.com", 2027 | "keywords": [ 2028 | "grapheme", 2029 | "i18n", 2030 | "string", 2031 | "unicode", 2032 | "utf-8", 2033 | "utf8" 2034 | ], 2035 | "support": { 2036 | "source": "https://github.com/symfony/string/tree/v5.2.0" 2037 | }, 2038 | "funding": [ 2039 | { 2040 | "url": "https://symfony.com/sponsor", 2041 | "type": "custom" 2042 | }, 2043 | { 2044 | "url": "https://github.com/fabpot", 2045 | "type": "github" 2046 | }, 2047 | { 2048 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 2049 | "type": "tidelift" 2050 | } 2051 | ], 2052 | "time": "2020-10-24T12:08:07+00:00" 2053 | } 2054 | ], 2055 | "packages-dev": [], 2056 | "aliases": [], 2057 | "minimum-stability": "stable", 2058 | "stability-flags": [], 2059 | "prefer-stable": false, 2060 | "prefer-lowest": false, 2061 | "platform": [], 2062 | "platform-dev": [], 2063 | "plugin-api-version": "2.0.0" 2064 | } 2065 | --------------------------------------------------------------------------------