├── .dockerignore ├── .gitignore ├── .php-cs-fixer.php ├── 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-fixer.cache 5 | .phpunit.result.cache 6 | -------------------------------------------------------------------------------- /.php-cs-fixer.php: -------------------------------------------------------------------------------- 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' => ['sort_algorithm' => '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 | 'annotations' => [ 23 | 'author', 24 | ], 25 | ], 26 | 'void_return' => false, 27 | 'single_trait_insert_per_statement' => false, 28 | 'ternary_to_null_coalescing' => true, 29 | 'pow_to_exponentiation' => false, 30 | 'random_api_migration' => false, 31 | 'declare_strict_types' => false, 32 | 'phpdoc_no_alias_tag' => [ 33 | 'replacements' => [ 34 | 'type' => 'var', 35 | 'link' => 'see', 36 | ], 37 | ], 38 | 'header_comment' => [ 39 | 'comment_type' => 'PHPDoc', 40 | 'header' => 'This file is part of the Sclable Xml Lint Package. 41 | 42 | @copyright (c) ' . date('Y') . ' Sclable Business Solutions GmbH 43 | 44 | For the full copyright and license information, please view the LICENSE 45 | file that was distributed with this source code.', 46 | 'location' => 'after_declare_strict', 47 | //'separate' => 'bottom', 48 | ], 49 | ]; 50 | 51 | return (new PhpCsFixer\Config()) 52 | ->setFinder( 53 | PhpCsFixer\Finder::create() 54 | ->in(__DIR__ . '/src') 55 | ->in(__DIR__ . '/tests') 56 | ->in(__DIR__ . '/bin') 57 | // Note: The pattern is seen relative from one of the `->in()` 58 | // directories. And works for files too this way. 59 | ->notPath('bootstrap.php') 60 | ) 61 | ->setRules($fixers); 62 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Sclable XML Lint - Changelog 2 | ============================ 3 | 4 | 0.9.0 5 | ----- 6 | 7 | * Add support for PHP 8.4 8 | * Drop support for PHP 8.0 9 | * Update dependencies 10 | ============================ 11 | 12 | 0.8.0 13 | ----- 14 | 15 | * Add support for Symfony 7 16 | * Drop support for Symfony 4 17 | ============================ 18 | 19 | 0.7.0 20 | ----- 21 | 22 | * Add support for PHP 8.3 23 | 24 | 0.6.0 25 | ----- 26 | 27 | * Add support for PHP 8.2 28 | * Drop support for PHP 7.4 29 | 30 | 0.5.0 31 | ----- 32 | 33 | * Add support for PHP 8.1 34 | * Drop support for PHP 7.3 35 | * Chores: remove Travis integration 36 | 37 | 0.4.0 38 | ----- 39 | 40 | * Add support for PHP 8 41 | * Drop support for PHP 7.2 42 | * Chores: Travis integration update 43 | 44 | 0.3.0 45 | ----- 46 | 47 | * Add support for symfony console / finder 5.* 48 | * Drop compatibility for PHP <7.2 and eol symfony versions 49 | * Update test frameworks to latest versions 50 | 51 | 52 | 0.2.4 53 | ----- 54 | 55 | * update dependencies to symfony/finder & symfony/console (include 4.*) 56 | 57 | 58 | 0.2.3 59 | ----- 60 | 61 | * clean up composer file (move tests to autoload-dev) 62 | 63 | 0.2.2 64 | ----- 65 | 66 | * update dependencies to symfony/finder & symfony/console 67 | 68 | 69 | 0.2.1 70 | ----- 71 | 72 | * fix a bug where patterns were empty 73 | 74 | 0.2.0 75 | ----- 76 | 77 | * validate xml files against XSD schemata (with skip option) 78 | * pattern option 79 | 80 | 81 | 0.1.0 82 | ----- 83 | 84 | * cli interface 85 | * lint xml file 86 | * lint files in a directory 87 | * recursive option (default is 'yes') 88 | * exclude option 89 | -------------------------------------------------------------------------------- /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.4 --build-arg=PHP_VERSION="8.4" . 62 | 63 | # PHP_VERSION: choose between 8.1, 8.2, 8.3 and 8.4 64 | docker build -t xml-lint:php-8.1 --build-arg=PHP_VERSION="8.1" . 65 | docker build -t xml-lint:php-8.2 --build-arg=PHP_VERSION="8.2" . 66 | docker build -t xml-lint:php-8.3 --build-arg=PHP_VERSION="8.3" . 67 | docker build -t xml-lint:php-8.4 --build-arg=PHP_VERSION="8.4" . 68 | 69 | # Run with code style check 70 | docker build -t xml-lint:php-8.4 --build-arg=PHP_VERSION="8.4" --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.4 -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 | "name": "Hakan Özkan", 12 | "email": "hakan.ozkan@sclable.com" 13 | } 14 | ], 15 | "keywords": [ 16 | "xml", 17 | "lint", 18 | "cli", 19 | "xsd", 20 | "xml schema" 21 | ], 22 | "bin": ["bin/xmllint"], 23 | "require": { 24 | "php": "8.1.*|8.2.*|8.3.*|8.4.*", 25 | "symfony/console": "5.3.*|5.4.*|6.*|7.*", 26 | "symfony/finder": "5.3.*|5.4.*|6.*|7.*", 27 | "ext-libxml": "*", 28 | "ext-dom": "*" 29 | }, 30 | "require-dev": { 31 | "behat/behat": "^3.0", 32 | "phpunit/phpunit": "^10" 33 | }, 34 | "autoload": { 35 | "psr-4": { 36 | "sclable\\xmlLint\\": "src/php/" 37 | } 38 | }, 39 | "autoload-dev": { 40 | "psr-4": { 41 | "sclable\\xmlLint\\tests\\": "tests/" 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | tests/unit 6 | 7 | 8 | 9 | 10 | src/php/ 11 | 12 | 13 | src/php/console/ 14 | src/php/bootstrap.php 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/php/bootstrap.php: -------------------------------------------------------------------------------- 1 | setDefaultCommand(LintCommand::COMMAND_NAME); 37 | } 38 | 39 | protected function getDefaultCommands(): array 40 | { 41 | parent::getDefaultCommands(); 42 | 43 | return [ 44 | new HelpCommand(), 45 | new LintCommand(), 46 | ]; 47 | } 48 | 49 | /** 50 | * @SuppressWarnings(PHPMD.Superglobals) 51 | */ 52 | public function run(?InputInterface $input = null, ?OutputInterface $output = null): int 53 | { 54 | if (null === $input) { 55 | // rewrite the input for single command usage 56 | $argv = $_SERVER['argv']; 57 | $scriptName = array_shift($argv); 58 | array_unshift($argv, 'lint'); 59 | array_unshift($argv, $scriptName); 60 | $input = new ArgvInput($argv); 61 | } 62 | 63 | return parent::run($input, $output); 64 | } 65 | 66 | protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output): int 67 | { 68 | if ('version' != $command->getName()) { 69 | $output->writeln($this->getLongVersion()); 70 | } 71 | 72 | return parent::doRunCommand($command, $input, $output); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/php/console/command/Command.php: -------------------------------------------------------------------------------- 1 | synopsis[$key])) { 32 | $this->synopsis[$key] = trim(sprintf( 33 | '%s %s', 34 | $_SERVER['PHP_SELF'], 35 | $this->getDefinition()->getSynopsis($short) 36 | )); 37 | } 38 | 39 | return $this->synopsis[$key]; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /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 | protected function configure() 31 | { 32 | $this->ignoreValidationErrors(); 33 | 34 | $this 35 | ->setName('help') 36 | ->setDefinition([ 37 | new InputOption('xml', null, InputOption::VALUE_NONE, 'To output help as XML'), 38 | new InputOption( 39 | 'format', 40 | null, 41 | InputOption::VALUE_REQUIRED, 42 | 'The output format (txt, xml, json, or md)', 43 | 'txt' 44 | ), 45 | new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command help'), 46 | ]) 47 | ->setDescription('Display help') 48 | ->setHelp(<<%command.name% command displays the help. 50 | You can also output the help in other formats by using the --format option: 51 | 52 | php %command.full_name% --help --format=xml 53 | EOF 54 | ); 55 | } 56 | 57 | protected function execute(InputInterface $input, OutputInterface $output): int 58 | { 59 | $this->setCommand( 60 | $this->getApplication()->find(LintCommand::COMMAND_NAME) 61 | ); 62 | 63 | return parent::execute($input, $output); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/php/console/command/LintCommand.php: -------------------------------------------------------------------------------- 1 | setName(self::COMMAND_NAME) 59 | ->setDescription('lint an xml file') 60 | ->addArgument( 61 | self::ARGUMENT_FILE, 62 | InputArgument::REQUIRED, 63 | 'the path/to/file.xml to lint a single file or a path/to/directory to lint all xml ' . 64 | 'files in a directory.' 65 | ) 66 | ->addOption( 67 | self::OPTION_RECURSIVE, 68 | 'r', 69 | InputOption::VALUE_OPTIONAL, 70 | 'Whether to scan directories recursive.', 71 | true 72 | ) 73 | ->addOption( 74 | self::OPTION_EXCLUDE, 75 | 'e', 76 | InputOption::VALUE_REQUIRED, 77 | 'Path(s) to exclude from linting, can be several separated by comma' 78 | ) 79 | ->addOption( 80 | self::OPTION_PATTERN, 81 | 'p', 82 | InputOption::VALUE_REQUIRED, 83 | 'Filter files with one or more patterns, e.g.: *.svg,*.xml. Separate patterns by comma.' 84 | ) 85 | ->addOption( 86 | self::OPTION_NO_XSD, 87 | 's', 88 | InputOption::VALUE_NONE, 89 | 'Skip downloading and checking against XSD-files.' 90 | ) 91 | ; 92 | } 93 | 94 | protected function execute(InputInterface $input, OutputInterface $output): int 95 | { 96 | $this->start = microtime(true); 97 | $this->output = $output; 98 | $this->input = $input; 99 | 100 | if ($input->getOption(self::OPTION_NO_XSD)) { 101 | $this->validator = ValidationFactory::createLintOnlyValidation(); 102 | } else { 103 | $this->validator = ValidationFactory::createDefaultCollection(); 104 | } 105 | 106 | $file = $input->getArgument(self::ARGUMENT_FILE); 107 | 108 | $output->writeln('progress: '); 109 | if (is_dir($file)) { 110 | $status = $this->lintDir($file); 111 | } else { 112 | $status = $this->lintFile(new \SplFileInfo($file)); 113 | } 114 | 115 | $output->writeln(''); 116 | 117 | if (false === $status) { 118 | $this->printReportsOfFilesWithProblems(); 119 | } 120 | 121 | $this->output->writeln(sprintf( 122 | PHP_EOL . '%d files / %1.2f seconds done', 123 | count($this->reports), 124 | microtime(true) - $this->start 125 | )); 126 | 127 | return $status ? 0 : 1; 128 | } 129 | 130 | /** 131 | * lint the content of a directory, recursive if defined. 132 | * 133 | * @param string $dir path/to/dir 134 | * 135 | * @return bool 136 | */ 137 | private function lintDir($dir) 138 | { 139 | $finder = Finder::create(); 140 | $finder->files() 141 | ->in($dir); 142 | 143 | $patterns = $this->input->getOption(self::OPTION_PATTERN); 144 | if (!empty($patterns)) { 145 | $patterns = explode(',', $patterns); 146 | foreach ($patterns as $pattern) { 147 | $finder->name(trim($pattern)); 148 | } 149 | } else { 150 | $finder->name('*.xml.dist') 151 | ->name('*.xml'); 152 | } 153 | 154 | if (!$this->input->getOption(self::OPTION_RECURSIVE)) { 155 | $finder->depth(0); 156 | } 157 | 158 | if ($this->input->hasOption(self::OPTION_EXCLUDE)) { 159 | $exclude = explode(',', (string) $this->input->getOption(self::OPTION_EXCLUDE)); 160 | $finder->exclude($exclude); 161 | } 162 | 163 | $totalFiles = $finder->count(); 164 | 165 | $counter = 0; 166 | $ret = true; 167 | /** @var SplFileInfo $file */ 168 | foreach ($finder as $file) { 169 | $ret = $this->lintFile($file) && $ret; 170 | if (0 == ++$counter % 30) { 171 | $this->output->writeln(sprintf( 172 | ' %8d/%d %6.2f%%', 173 | $counter, 174 | $totalFiles, 175 | $counter / $totalFiles * 100 176 | )); 177 | } 178 | } 179 | 180 | return $ret; 181 | } 182 | 183 | /** 184 | * format and print the errors from the queue to the output. 185 | */ 186 | private function printReportsOfFilesWithProblems() 187 | { 188 | $this->output->writeln(PHP_EOL . 'errors:'); 189 | 190 | foreach ($this->reports as $report) { 191 | if (false === $report->hasProblems()) { 192 | continue; 193 | } 194 | 195 | $file = $report->getFile(); 196 | $this->output->writeln('file: ' . $file->getPath() . DIRECTORY_SEPARATOR . $file->getFilename()); 197 | 198 | foreach ($report->getProblems() as $problem) { 199 | $this->output->write( 200 | ' > ' . $problem->getMessage() . PHP_EOL 201 | ); 202 | } 203 | 204 | $this->output->write(' - - ' . PHP_EOL); 205 | } 206 | } 207 | 208 | /** 209 | * lint a file, pass errors to the queue. 210 | * 211 | * @return bool 212 | */ 213 | private function lintFile(\SplFileInfo $file) 214 | { 215 | if ($this->output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) { 216 | $this->output->write('lint file ' . $file . ' ... '); 217 | } 218 | 219 | $report = FileReport::create($file); 220 | $this->reports[] = $report; 221 | $status = $this->validator->validateFile($report); 222 | 223 | if ($this->output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) { 224 | if (false === $status) { 225 | $this->output->writeln('errors'); 226 | } else { 227 | $this->output->writeln('passed.'); 228 | } 229 | } else { 230 | if (false === $status) { 231 | $this->output->write('F'); 232 | } else { 233 | $this->output->write('.'); 234 | } 235 | } 236 | 237 | return $status; 238 | } 239 | } 240 | -------------------------------------------------------------------------------- /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 | public function validateFile(FileReport $report) 35 | { 36 | $realPath = $report->getFile()->getRealPath(); 37 | 38 | if (false === is_file($realPath)) { 39 | $report->reportProblem('file not found: ' . $realPath); 40 | 41 | return false; 42 | } 43 | 44 | if (false === is_readable($realPath)) { 45 | $report->reportProblem('file not readable: ' . $realPath); 46 | 47 | return false; 48 | } 49 | 50 | libxml_clear_errors(); 51 | $domDoc = new \DOMDocument(); 52 | $domDoc->load($realPath, LIBXML_NOERROR | LIBXML_NOWARNING | LIBXML_PEDANTIC); 53 | 54 | $errors = libxml_get_errors(); 55 | foreach ($this->formatter->formatErrors($errors) as $problem) { 56 | $report->reportProblem($problem); 57 | } 58 | 59 | return !$report->hasProblems(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/php/validator/ValidationCollection.php: -------------------------------------------------------------------------------- 1 | collection as $validation) { 28 | $status = $validation->validateFile($report) && $status; 29 | } 30 | 31 | return $status; 32 | } 33 | 34 | public function addValidation(ValidationInterface $validation) 35 | { 36 | $this->collection[] = $validation; 37 | 38 | return $this; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /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 | public function validateFile(FileReport $report) 38 | { 39 | $file = $report->getFile()->getRealPath(); 40 | 41 | if (empty($file)) { 42 | return false; 43 | } 44 | 45 | $domDoc = new \DOMDocument(); 46 | $loaded = $domDoc->load($file, LIBXML_NOERROR | LIBXML_NOWARNING | LIBXML_PEDANTIC); 47 | 48 | if (false === $loaded) { 49 | return false; 50 | } 51 | 52 | $validation = $this->getSchemaValidationFile($domDoc); 53 | 54 | if (false === $validation) { 55 | return true; 56 | } 57 | 58 | $validationSource = $this->getSchemaValidationSource($validation, $report); 59 | 60 | if (false === $validationSource) { 61 | return false; 62 | } 63 | 64 | libxml_clear_errors(); 65 | if (true !== $domDoc->schemaValidateSource($validationSource)) { 66 | $errors = libxml_get_errors(); 67 | foreach ($this->formatter->formatErrors($errors) as $problem) { 68 | $report->reportProblem($problem); 69 | } 70 | 71 | return false; 72 | } 73 | 74 | return true; 75 | } 76 | 77 | /** 78 | * @param string $filename 79 | * @param FileReport $report 80 | * 81 | * @return bool|string 82 | */ 83 | private function getSchemaValidationSource($filename, $report) 84 | { 85 | if (0 === preg_match('/^(http|https|ftp):/i', $filename)) { 86 | if (false === file_exists($filename)) { 87 | $filename = $report->getFile()->getPath() . '/' . $filename; 88 | } 89 | if (!is_readable($filename)) { 90 | $report->reportProblem('unable to validate, schema file is not readable: ' . $filename); 91 | 92 | return false; 93 | } 94 | } 95 | 96 | if (isset($this->cache[$filename])) { 97 | return $this->cache[$filename]; 98 | } 99 | 100 | $validationSource = @file_get_contents($filename); 101 | 102 | if (false === $validationSource) { 103 | $report->reportProblem('unable to load schema file from: ' . $filename); 104 | 105 | return false; 106 | } 107 | 108 | if (empty($validationSource)) { 109 | $report->reportProblem(sprintf('xsd validation file is empty ("%s").', $filename)); 110 | 111 | return false; 112 | } 113 | 114 | return $this->cache[$filename] = $validationSource; 115 | } 116 | 117 | /** 118 | * @return bool|string 119 | */ 120 | private function getSchemaValidationFile(\DOMDocument $document) 121 | { 122 | $firstChild = $this->getFirstChild($document); 123 | // @codeCoverageIgnoreStart 124 | if (false === $firstChild) { 125 | return false; 126 | } 127 | // @codeCoverageIgnoreEnd 128 | 129 | $attribute = $firstChild->getAttribute('xsi:noNamespaceSchemaLocation'); 130 | 131 | if (empty($attribute)) { 132 | return false; 133 | } 134 | 135 | return $attribute; 136 | } 137 | 138 | /** 139 | * @return bool|\DOMElement 140 | */ 141 | private function getFirstChild(\DOMDocument $document) 142 | { 143 | foreach ($document->childNodes as $child) { 144 | if ($child instanceof \DOMElement) { 145 | return $child; 146 | // @codeCoverageIgnoreStart 147 | } 148 | } 149 | 150 | return false; 151 | // @codeCoverageIgnoreEnd 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /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 | public function theFile($file) 56 | { 57 | $this->file = dirname(__DIR__) . '/_testdata/' . $file; 58 | } 59 | 60 | /** 61 | * @When I run lint 62 | */ 63 | public function iRunLint() 64 | { 65 | $this->exitCode = $this->commandTester->execute([ 66 | 'file' => $this->file, 67 | ]); 68 | } 69 | 70 | /** 71 | * @Then I have a return code :code 72 | * 73 | * @throws \Exception 74 | */ 75 | public function iHaveAReturnCode($code) 76 | { 77 | $code = (int) $code; 78 | 79 | if (null === $this->exitCode) { 80 | echo $this->commandTester->getDisplay(); 81 | throw new \Exception('the return code was NULL.'); 82 | } 83 | 84 | if ($this->exitCode !== $code) { 85 | echo $this->commandTester->getDisplay(); 86 | throw new \Exception(sprintf("the return code does not match. \n Expected: %s\n Actual: %s", $code, $this->exitCode)); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /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/b114ace41c9b99eb291898f8b67b7063b5b8e884/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 static 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": "^3.85.1" 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": "077616d4e173da34e81d12fb9dc2e424", 8 | "packages": [ 9 | { 10 | "name": "clue/ndjson-react", 11 | "version": "v1.3.0", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/clue/reactphp-ndjson.git", 15 | "reference": "392dc165fce93b5bb5c637b67e59619223c931b0" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/clue/reactphp-ndjson/zipball/392dc165fce93b5bb5c637b67e59619223c931b0", 20 | "reference": "392dc165fce93b5bb5c637b67e59619223c931b0", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "php": ">=5.3", 25 | "react/stream": "^1.2" 26 | }, 27 | "require-dev": { 28 | "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35", 29 | "react/event-loop": "^1.2" 30 | }, 31 | "type": "library", 32 | "autoload": { 33 | "psr-4": { 34 | "Clue\\React\\NDJson\\": "src/" 35 | } 36 | }, 37 | "notification-url": "https://packagist.org/downloads/", 38 | "license": [ 39 | "MIT" 40 | ], 41 | "authors": [ 42 | { 43 | "name": "Christian Lück", 44 | "email": "christian@clue.engineering" 45 | } 46 | ], 47 | "description": "Streaming newline-delimited JSON (NDJSON) parser and encoder for ReactPHP.", 48 | "homepage": "https://github.com/clue/reactphp-ndjson", 49 | "keywords": [ 50 | "NDJSON", 51 | "json", 52 | "jsonlines", 53 | "newline", 54 | "reactphp", 55 | "streaming" 56 | ], 57 | "support": { 58 | "issues": "https://github.com/clue/reactphp-ndjson/issues", 59 | "source": "https://github.com/clue/reactphp-ndjson/tree/v1.3.0" 60 | }, 61 | "funding": [ 62 | { 63 | "url": "https://clue.engineering/support", 64 | "type": "custom" 65 | }, 66 | { 67 | "url": "https://github.com/clue", 68 | "type": "github" 69 | } 70 | ], 71 | "time": "2022-12-23T10:58:28+00:00" 72 | }, 73 | { 74 | "name": "composer/pcre", 75 | "version": "3.3.2", 76 | "source": { 77 | "type": "git", 78 | "url": "https://github.com/composer/pcre.git", 79 | "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e" 80 | }, 81 | "dist": { 82 | "type": "zip", 83 | "url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e", 84 | "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e", 85 | "shasum": "" 86 | }, 87 | "require": { 88 | "php": "^7.4 || ^8.0" 89 | }, 90 | "conflict": { 91 | "phpstan/phpstan": "<1.11.10" 92 | }, 93 | "require-dev": { 94 | "phpstan/phpstan": "^1.12 || ^2", 95 | "phpstan/phpstan-strict-rules": "^1 || ^2", 96 | "phpunit/phpunit": "^8 || ^9" 97 | }, 98 | "type": "library", 99 | "extra": { 100 | "phpstan": { 101 | "includes": [ 102 | "extension.neon" 103 | ] 104 | }, 105 | "branch-alias": { 106 | "dev-main": "3.x-dev" 107 | } 108 | }, 109 | "autoload": { 110 | "psr-4": { 111 | "Composer\\Pcre\\": "src" 112 | } 113 | }, 114 | "notification-url": "https://packagist.org/downloads/", 115 | "license": [ 116 | "MIT" 117 | ], 118 | "authors": [ 119 | { 120 | "name": "Jordi Boggiano", 121 | "email": "j.boggiano@seld.be", 122 | "homepage": "http://seld.be" 123 | } 124 | ], 125 | "description": "PCRE wrapping library that offers type-safe preg_* replacements.", 126 | "keywords": [ 127 | "PCRE", 128 | "preg", 129 | "regex", 130 | "regular expression" 131 | ], 132 | "support": { 133 | "issues": "https://github.com/composer/pcre/issues", 134 | "source": "https://github.com/composer/pcre/tree/3.3.2" 135 | }, 136 | "funding": [ 137 | { 138 | "url": "https://packagist.com", 139 | "type": "custom" 140 | }, 141 | { 142 | "url": "https://github.com/composer", 143 | "type": "github" 144 | }, 145 | { 146 | "url": "https://tidelift.com/funding/github/packagist/composer/composer", 147 | "type": "tidelift" 148 | } 149 | ], 150 | "time": "2024-11-12T16:29:46+00:00" 151 | }, 152 | { 153 | "name": "composer/semver", 154 | "version": "3.4.3", 155 | "source": { 156 | "type": "git", 157 | "url": "https://github.com/composer/semver.git", 158 | "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12" 159 | }, 160 | "dist": { 161 | "type": "zip", 162 | "url": "https://api.github.com/repos/composer/semver/zipball/4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", 163 | "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", 164 | "shasum": "" 165 | }, 166 | "require": { 167 | "php": "^5.3.2 || ^7.0 || ^8.0" 168 | }, 169 | "require-dev": { 170 | "phpstan/phpstan": "^1.11", 171 | "symfony/phpunit-bridge": "^3 || ^7" 172 | }, 173 | "type": "library", 174 | "extra": { 175 | "branch-alias": { 176 | "dev-main": "3.x-dev" 177 | } 178 | }, 179 | "autoload": { 180 | "psr-4": { 181 | "Composer\\Semver\\": "src" 182 | } 183 | }, 184 | "notification-url": "https://packagist.org/downloads/", 185 | "license": [ 186 | "MIT" 187 | ], 188 | "authors": [ 189 | { 190 | "name": "Nils Adermann", 191 | "email": "naderman@naderman.de", 192 | "homepage": "http://www.naderman.de" 193 | }, 194 | { 195 | "name": "Jordi Boggiano", 196 | "email": "j.boggiano@seld.be", 197 | "homepage": "http://seld.be" 198 | }, 199 | { 200 | "name": "Rob Bast", 201 | "email": "rob.bast@gmail.com", 202 | "homepage": "http://robbast.nl" 203 | } 204 | ], 205 | "description": "Semver library that offers utilities, version constraint parsing and validation.", 206 | "keywords": [ 207 | "semantic", 208 | "semver", 209 | "validation", 210 | "versioning" 211 | ], 212 | "support": { 213 | "irc": "ircs://irc.libera.chat:6697/composer", 214 | "issues": "https://github.com/composer/semver/issues", 215 | "source": "https://github.com/composer/semver/tree/3.4.3" 216 | }, 217 | "funding": [ 218 | { 219 | "url": "https://packagist.com", 220 | "type": "custom" 221 | }, 222 | { 223 | "url": "https://github.com/composer", 224 | "type": "github" 225 | }, 226 | { 227 | "url": "https://tidelift.com/funding/github/packagist/composer/composer", 228 | "type": "tidelift" 229 | } 230 | ], 231 | "time": "2024-09-19T14:15:21+00:00" 232 | }, 233 | { 234 | "name": "composer/xdebug-handler", 235 | "version": "3.0.5", 236 | "source": { 237 | "type": "git", 238 | "url": "https://github.com/composer/xdebug-handler.git", 239 | "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef" 240 | }, 241 | "dist": { 242 | "type": "zip", 243 | "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/6c1925561632e83d60a44492e0b344cf48ab85ef", 244 | "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef", 245 | "shasum": "" 246 | }, 247 | "require": { 248 | "composer/pcre": "^1 || ^2 || ^3", 249 | "php": "^7.2.5 || ^8.0", 250 | "psr/log": "^1 || ^2 || ^3" 251 | }, 252 | "require-dev": { 253 | "phpstan/phpstan": "^1.0", 254 | "phpstan/phpstan-strict-rules": "^1.1", 255 | "phpunit/phpunit": "^8.5 || ^9.6 || ^10.5" 256 | }, 257 | "type": "library", 258 | "autoload": { 259 | "psr-4": { 260 | "Composer\\XdebugHandler\\": "src" 261 | } 262 | }, 263 | "notification-url": "https://packagist.org/downloads/", 264 | "license": [ 265 | "MIT" 266 | ], 267 | "authors": [ 268 | { 269 | "name": "John Stevenson", 270 | "email": "john-stevenson@blueyonder.co.uk" 271 | } 272 | ], 273 | "description": "Restarts a process without Xdebug.", 274 | "keywords": [ 275 | "Xdebug", 276 | "performance" 277 | ], 278 | "support": { 279 | "irc": "ircs://irc.libera.chat:6697/composer", 280 | "issues": "https://github.com/composer/xdebug-handler/issues", 281 | "source": "https://github.com/composer/xdebug-handler/tree/3.0.5" 282 | }, 283 | "funding": [ 284 | { 285 | "url": "https://packagist.com", 286 | "type": "custom" 287 | }, 288 | { 289 | "url": "https://github.com/composer", 290 | "type": "github" 291 | }, 292 | { 293 | "url": "https://tidelift.com/funding/github/packagist/composer/composer", 294 | "type": "tidelift" 295 | } 296 | ], 297 | "time": "2024-05-06T16:37:16+00:00" 298 | }, 299 | { 300 | "name": "evenement/evenement", 301 | "version": "v3.0.2", 302 | "source": { 303 | "type": "git", 304 | "url": "https://github.com/igorw/evenement.git", 305 | "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc" 306 | }, 307 | "dist": { 308 | "type": "zip", 309 | "url": "https://api.github.com/repos/igorw/evenement/zipball/0a16b0d71ab13284339abb99d9d2bd813640efbc", 310 | "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc", 311 | "shasum": "" 312 | }, 313 | "require": { 314 | "php": ">=7.0" 315 | }, 316 | "require-dev": { 317 | "phpunit/phpunit": "^9 || ^6" 318 | }, 319 | "type": "library", 320 | "autoload": { 321 | "psr-4": { 322 | "Evenement\\": "src/" 323 | } 324 | }, 325 | "notification-url": "https://packagist.org/downloads/", 326 | "license": [ 327 | "MIT" 328 | ], 329 | "authors": [ 330 | { 331 | "name": "Igor Wiedler", 332 | "email": "igor@wiedler.ch" 333 | } 334 | ], 335 | "description": "Événement is a very simple event dispatching library for PHP", 336 | "keywords": [ 337 | "event-dispatcher", 338 | "event-emitter" 339 | ], 340 | "support": { 341 | "issues": "https://github.com/igorw/evenement/issues", 342 | "source": "https://github.com/igorw/evenement/tree/v3.0.2" 343 | }, 344 | "time": "2023-08-08T05:53:35+00:00" 345 | }, 346 | { 347 | "name": "fidry/cpu-core-counter", 348 | "version": "1.2.0", 349 | "source": { 350 | "type": "git", 351 | "url": "https://github.com/theofidry/cpu-core-counter.git", 352 | "reference": "8520451a140d3f46ac33042715115e290cf5785f" 353 | }, 354 | "dist": { 355 | "type": "zip", 356 | "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/8520451a140d3f46ac33042715115e290cf5785f", 357 | "reference": "8520451a140d3f46ac33042715115e290cf5785f", 358 | "shasum": "" 359 | }, 360 | "require": { 361 | "php": "^7.2 || ^8.0" 362 | }, 363 | "require-dev": { 364 | "fidry/makefile": "^0.2.0", 365 | "fidry/php-cs-fixer-config": "^1.1.2", 366 | "phpstan/extension-installer": "^1.2.0", 367 | "phpstan/phpstan": "^1.9.2", 368 | "phpstan/phpstan-deprecation-rules": "^1.0.0", 369 | "phpstan/phpstan-phpunit": "^1.2.2", 370 | "phpstan/phpstan-strict-rules": "^1.4.4", 371 | "phpunit/phpunit": "^8.5.31 || ^9.5.26", 372 | "webmozarts/strict-phpunit": "^7.5" 373 | }, 374 | "type": "library", 375 | "autoload": { 376 | "psr-4": { 377 | "Fidry\\CpuCoreCounter\\": "src/" 378 | } 379 | }, 380 | "notification-url": "https://packagist.org/downloads/", 381 | "license": [ 382 | "MIT" 383 | ], 384 | "authors": [ 385 | { 386 | "name": "Théo FIDRY", 387 | "email": "theo.fidry@gmail.com" 388 | } 389 | ], 390 | "description": "Tiny utility to get the number of CPU cores.", 391 | "keywords": [ 392 | "CPU", 393 | "core" 394 | ], 395 | "support": { 396 | "issues": "https://github.com/theofidry/cpu-core-counter/issues", 397 | "source": "https://github.com/theofidry/cpu-core-counter/tree/1.2.0" 398 | }, 399 | "funding": [ 400 | { 401 | "url": "https://github.com/theofidry", 402 | "type": "github" 403 | } 404 | ], 405 | "time": "2024-08-06T10:04:20+00:00" 406 | }, 407 | { 408 | "name": "friendsofphp/php-cs-fixer", 409 | "version": "v3.85.1", 410 | "source": { 411 | "type": "git", 412 | "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", 413 | "reference": "2fb6d7f6c3398dca5786a1635b27405d73a417ba" 414 | }, 415 | "dist": { 416 | "type": "zip", 417 | "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/2fb6d7f6c3398dca5786a1635b27405d73a417ba", 418 | "reference": "2fb6d7f6c3398dca5786a1635b27405d73a417ba", 419 | "shasum": "" 420 | }, 421 | "require": { 422 | "clue/ndjson-react": "^1.3", 423 | "composer/semver": "^3.4", 424 | "composer/xdebug-handler": "^3.0.5", 425 | "ext-filter": "*", 426 | "ext-hash": "*", 427 | "ext-json": "*", 428 | "ext-tokenizer": "*", 429 | "fidry/cpu-core-counter": "^1.2", 430 | "php": "^7.4 || ^8.0", 431 | "react/child-process": "^0.6.6", 432 | "react/event-loop": "^1.5", 433 | "react/promise": "^3.2", 434 | "react/socket": "^1.16", 435 | "react/stream": "^1.4", 436 | "sebastian/diff": "^4.0.6 || ^5.1.1 || ^6.0.2 || ^7.0", 437 | "symfony/console": "^5.4.47 || ^6.4.13 || ^7.0", 438 | "symfony/event-dispatcher": "^5.4.45 || ^6.4.13 || ^7.0", 439 | "symfony/filesystem": "^5.4.45 || ^6.4.13 || ^7.0", 440 | "symfony/finder": "^5.4.45 || ^6.4.17 || ^7.0", 441 | "symfony/options-resolver": "^5.4.45 || ^6.4.16 || ^7.0", 442 | "symfony/polyfill-mbstring": "^1.32", 443 | "symfony/polyfill-php80": "^1.32", 444 | "symfony/polyfill-php81": "^1.32", 445 | "symfony/process": "^5.4.47 || ^6.4.20 || ^7.2", 446 | "symfony/stopwatch": "^5.4.45 || ^6.4.19 || ^7.0" 447 | }, 448 | "require-dev": { 449 | "facile-it/paraunit": "^1.3.1 || ^2.6", 450 | "infection/infection": "^0.29.14", 451 | "justinrainbow/json-schema": "^5.3 || ^6.4", 452 | "keradus/cli-executor": "^2.2", 453 | "mikey179/vfsstream": "^1.6.12", 454 | "php-coveralls/php-coveralls": "^2.8", 455 | "php-cs-fixer/accessible-object": "^1.1", 456 | "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.6", 457 | "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.6", 458 | "phpunit/phpunit": "^9.6.23 || ^10.5.47 || ^11.5.25", 459 | "symfony/polyfill-php84": "^1.32", 460 | "symfony/var-dumper": "^5.4.48 || ^6.4.23 || ^7.3.1", 461 | "symfony/yaml": "^5.4.45 || ^6.4.23 || ^7.3.1" 462 | }, 463 | "suggest": { 464 | "ext-dom": "For handling output formats in XML", 465 | "ext-mbstring": "For handling non-UTF8 characters." 466 | }, 467 | "bin": [ 468 | "php-cs-fixer" 469 | ], 470 | "type": "application", 471 | "autoload": { 472 | "psr-4": { 473 | "PhpCsFixer\\": "src/" 474 | }, 475 | "exclude-from-classmap": [ 476 | "src/Fixer/Internal/*" 477 | ] 478 | }, 479 | "notification-url": "https://packagist.org/downloads/", 480 | "license": [ 481 | "MIT" 482 | ], 483 | "authors": [ 484 | { 485 | "name": "Fabien Potencier", 486 | "email": "fabien@symfony.com" 487 | }, 488 | { 489 | "name": "Dariusz Rumiński", 490 | "email": "dariusz.ruminski@gmail.com" 491 | } 492 | ], 493 | "description": "A tool to automatically fix PHP code style", 494 | "keywords": [ 495 | "Static code analysis", 496 | "fixer", 497 | "standards", 498 | "static analysis" 499 | ], 500 | "support": { 501 | "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", 502 | "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.85.1" 503 | }, 504 | "funding": [ 505 | { 506 | "url": "https://github.com/keradus", 507 | "type": "github" 508 | } 509 | ], 510 | "time": "2025-07-29T22:22:50+00:00" 511 | }, 512 | { 513 | "name": "psr/container", 514 | "version": "2.0.2", 515 | "source": { 516 | "type": "git", 517 | "url": "https://github.com/php-fig/container.git", 518 | "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" 519 | }, 520 | "dist": { 521 | "type": "zip", 522 | "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", 523 | "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", 524 | "shasum": "" 525 | }, 526 | "require": { 527 | "php": ">=7.4.0" 528 | }, 529 | "type": "library", 530 | "extra": { 531 | "branch-alias": { 532 | "dev-master": "2.0.x-dev" 533 | } 534 | }, 535 | "autoload": { 536 | "psr-4": { 537 | "Psr\\Container\\": "src/" 538 | } 539 | }, 540 | "notification-url": "https://packagist.org/downloads/", 541 | "license": [ 542 | "MIT" 543 | ], 544 | "authors": [ 545 | { 546 | "name": "PHP-FIG", 547 | "homepage": "https://www.php-fig.org/" 548 | } 549 | ], 550 | "description": "Common Container Interface (PHP FIG PSR-11)", 551 | "homepage": "https://github.com/php-fig/container", 552 | "keywords": [ 553 | "PSR-11", 554 | "container", 555 | "container-interface", 556 | "container-interop", 557 | "psr" 558 | ], 559 | "support": { 560 | "issues": "https://github.com/php-fig/container/issues", 561 | "source": "https://github.com/php-fig/container/tree/2.0.2" 562 | }, 563 | "time": "2021-11-05T16:47:00+00:00" 564 | }, 565 | { 566 | "name": "psr/event-dispatcher", 567 | "version": "1.0.0", 568 | "source": { 569 | "type": "git", 570 | "url": "https://github.com/php-fig/event-dispatcher.git", 571 | "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" 572 | }, 573 | "dist": { 574 | "type": "zip", 575 | "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", 576 | "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", 577 | "shasum": "" 578 | }, 579 | "require": { 580 | "php": ">=7.2.0" 581 | }, 582 | "type": "library", 583 | "extra": { 584 | "branch-alias": { 585 | "dev-master": "1.0.x-dev" 586 | } 587 | }, 588 | "autoload": { 589 | "psr-4": { 590 | "Psr\\EventDispatcher\\": "src/" 591 | } 592 | }, 593 | "notification-url": "https://packagist.org/downloads/", 594 | "license": [ 595 | "MIT" 596 | ], 597 | "authors": [ 598 | { 599 | "name": "PHP-FIG", 600 | "homepage": "http://www.php-fig.org/" 601 | } 602 | ], 603 | "description": "Standard interfaces for event handling.", 604 | "keywords": [ 605 | "events", 606 | "psr", 607 | "psr-14" 608 | ], 609 | "support": { 610 | "issues": "https://github.com/php-fig/event-dispatcher/issues", 611 | "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" 612 | }, 613 | "time": "2019-01-08T18:20:26+00:00" 614 | }, 615 | { 616 | "name": "psr/log", 617 | "version": "3.0.2", 618 | "source": { 619 | "type": "git", 620 | "url": "https://github.com/php-fig/log.git", 621 | "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" 622 | }, 623 | "dist": { 624 | "type": "zip", 625 | "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", 626 | "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", 627 | "shasum": "" 628 | }, 629 | "require": { 630 | "php": ">=8.0.0" 631 | }, 632 | "type": "library", 633 | "extra": { 634 | "branch-alias": { 635 | "dev-master": "3.x-dev" 636 | } 637 | }, 638 | "autoload": { 639 | "psr-4": { 640 | "Psr\\Log\\": "src" 641 | } 642 | }, 643 | "notification-url": "https://packagist.org/downloads/", 644 | "license": [ 645 | "MIT" 646 | ], 647 | "authors": [ 648 | { 649 | "name": "PHP-FIG", 650 | "homepage": "https://www.php-fig.org/" 651 | } 652 | ], 653 | "description": "Common interface for logging libraries", 654 | "homepage": "https://github.com/php-fig/log", 655 | "keywords": [ 656 | "log", 657 | "psr", 658 | "psr-3" 659 | ], 660 | "support": { 661 | "source": "https://github.com/php-fig/log/tree/3.0.2" 662 | }, 663 | "time": "2024-09-11T13:17:53+00:00" 664 | }, 665 | { 666 | "name": "react/cache", 667 | "version": "v1.2.0", 668 | "source": { 669 | "type": "git", 670 | "url": "https://github.com/reactphp/cache.git", 671 | "reference": "d47c472b64aa5608225f47965a484b75c7817d5b" 672 | }, 673 | "dist": { 674 | "type": "zip", 675 | "url": "https://api.github.com/repos/reactphp/cache/zipball/d47c472b64aa5608225f47965a484b75c7817d5b", 676 | "reference": "d47c472b64aa5608225f47965a484b75c7817d5b", 677 | "shasum": "" 678 | }, 679 | "require": { 680 | "php": ">=5.3.0", 681 | "react/promise": "^3.0 || ^2.0 || ^1.1" 682 | }, 683 | "require-dev": { 684 | "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35" 685 | }, 686 | "type": "library", 687 | "autoload": { 688 | "psr-4": { 689 | "React\\Cache\\": "src/" 690 | } 691 | }, 692 | "notification-url": "https://packagist.org/downloads/", 693 | "license": [ 694 | "MIT" 695 | ], 696 | "authors": [ 697 | { 698 | "name": "Christian Lück", 699 | "email": "christian@clue.engineering", 700 | "homepage": "https://clue.engineering/" 701 | }, 702 | { 703 | "name": "Cees-Jan Kiewiet", 704 | "email": "reactphp@ceesjankiewiet.nl", 705 | "homepage": "https://wyrihaximus.net/" 706 | }, 707 | { 708 | "name": "Jan Sorgalla", 709 | "email": "jsorgalla@gmail.com", 710 | "homepage": "https://sorgalla.com/" 711 | }, 712 | { 713 | "name": "Chris Boden", 714 | "email": "cboden@gmail.com", 715 | "homepage": "https://cboden.dev/" 716 | } 717 | ], 718 | "description": "Async, Promise-based cache interface for ReactPHP", 719 | "keywords": [ 720 | "cache", 721 | "caching", 722 | "promise", 723 | "reactphp" 724 | ], 725 | "support": { 726 | "issues": "https://github.com/reactphp/cache/issues", 727 | "source": "https://github.com/reactphp/cache/tree/v1.2.0" 728 | }, 729 | "funding": [ 730 | { 731 | "url": "https://opencollective.com/reactphp", 732 | "type": "open_collective" 733 | } 734 | ], 735 | "time": "2022-11-30T15:59:55+00:00" 736 | }, 737 | { 738 | "name": "react/child-process", 739 | "version": "v0.6.6", 740 | "source": { 741 | "type": "git", 742 | "url": "https://github.com/reactphp/child-process.git", 743 | "reference": "1721e2b93d89b745664353b9cfc8f155ba8a6159" 744 | }, 745 | "dist": { 746 | "type": "zip", 747 | "url": "https://api.github.com/repos/reactphp/child-process/zipball/1721e2b93d89b745664353b9cfc8f155ba8a6159", 748 | "reference": "1721e2b93d89b745664353b9cfc8f155ba8a6159", 749 | "shasum": "" 750 | }, 751 | "require": { 752 | "evenement/evenement": "^3.0 || ^2.0 || ^1.0", 753 | "php": ">=5.3.0", 754 | "react/event-loop": "^1.2", 755 | "react/stream": "^1.4" 756 | }, 757 | "require-dev": { 758 | "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", 759 | "react/socket": "^1.16", 760 | "sebastian/environment": "^5.0 || ^3.0 || ^2.0 || ^1.0" 761 | }, 762 | "type": "library", 763 | "autoload": { 764 | "psr-4": { 765 | "React\\ChildProcess\\": "src/" 766 | } 767 | }, 768 | "notification-url": "https://packagist.org/downloads/", 769 | "license": [ 770 | "MIT" 771 | ], 772 | "authors": [ 773 | { 774 | "name": "Christian Lück", 775 | "email": "christian@clue.engineering", 776 | "homepage": "https://clue.engineering/" 777 | }, 778 | { 779 | "name": "Cees-Jan Kiewiet", 780 | "email": "reactphp@ceesjankiewiet.nl", 781 | "homepage": "https://wyrihaximus.net/" 782 | }, 783 | { 784 | "name": "Jan Sorgalla", 785 | "email": "jsorgalla@gmail.com", 786 | "homepage": "https://sorgalla.com/" 787 | }, 788 | { 789 | "name": "Chris Boden", 790 | "email": "cboden@gmail.com", 791 | "homepage": "https://cboden.dev/" 792 | } 793 | ], 794 | "description": "Event-driven library for executing child processes with ReactPHP.", 795 | "keywords": [ 796 | "event-driven", 797 | "process", 798 | "reactphp" 799 | ], 800 | "support": { 801 | "issues": "https://github.com/reactphp/child-process/issues", 802 | "source": "https://github.com/reactphp/child-process/tree/v0.6.6" 803 | }, 804 | "funding": [ 805 | { 806 | "url": "https://opencollective.com/reactphp", 807 | "type": "open_collective" 808 | } 809 | ], 810 | "time": "2025-01-01T16:37:48+00:00" 811 | }, 812 | { 813 | "name": "react/dns", 814 | "version": "v1.13.0", 815 | "source": { 816 | "type": "git", 817 | "url": "https://github.com/reactphp/dns.git", 818 | "reference": "eb8ae001b5a455665c89c1df97f6fb682f8fb0f5" 819 | }, 820 | "dist": { 821 | "type": "zip", 822 | "url": "https://api.github.com/repos/reactphp/dns/zipball/eb8ae001b5a455665c89c1df97f6fb682f8fb0f5", 823 | "reference": "eb8ae001b5a455665c89c1df97f6fb682f8fb0f5", 824 | "shasum": "" 825 | }, 826 | "require": { 827 | "php": ">=5.3.0", 828 | "react/cache": "^1.0 || ^0.6 || ^0.5", 829 | "react/event-loop": "^1.2", 830 | "react/promise": "^3.2 || ^2.7 || ^1.2.1" 831 | }, 832 | "require-dev": { 833 | "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", 834 | "react/async": "^4.3 || ^3 || ^2", 835 | "react/promise-timer": "^1.11" 836 | }, 837 | "type": "library", 838 | "autoload": { 839 | "psr-4": { 840 | "React\\Dns\\": "src/" 841 | } 842 | }, 843 | "notification-url": "https://packagist.org/downloads/", 844 | "license": [ 845 | "MIT" 846 | ], 847 | "authors": [ 848 | { 849 | "name": "Christian Lück", 850 | "email": "christian@clue.engineering", 851 | "homepage": "https://clue.engineering/" 852 | }, 853 | { 854 | "name": "Cees-Jan Kiewiet", 855 | "email": "reactphp@ceesjankiewiet.nl", 856 | "homepage": "https://wyrihaximus.net/" 857 | }, 858 | { 859 | "name": "Jan Sorgalla", 860 | "email": "jsorgalla@gmail.com", 861 | "homepage": "https://sorgalla.com/" 862 | }, 863 | { 864 | "name": "Chris Boden", 865 | "email": "cboden@gmail.com", 866 | "homepage": "https://cboden.dev/" 867 | } 868 | ], 869 | "description": "Async DNS resolver for ReactPHP", 870 | "keywords": [ 871 | "async", 872 | "dns", 873 | "dns-resolver", 874 | "reactphp" 875 | ], 876 | "support": { 877 | "issues": "https://github.com/reactphp/dns/issues", 878 | "source": "https://github.com/reactphp/dns/tree/v1.13.0" 879 | }, 880 | "funding": [ 881 | { 882 | "url": "https://opencollective.com/reactphp", 883 | "type": "open_collective" 884 | } 885 | ], 886 | "time": "2024-06-13T14:18:03+00:00" 887 | }, 888 | { 889 | "name": "react/event-loop", 890 | "version": "v1.5.0", 891 | "source": { 892 | "type": "git", 893 | "url": "https://github.com/reactphp/event-loop.git", 894 | "reference": "bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354" 895 | }, 896 | "dist": { 897 | "type": "zip", 898 | "url": "https://api.github.com/repos/reactphp/event-loop/zipball/bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354", 899 | "reference": "bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354", 900 | "shasum": "" 901 | }, 902 | "require": { 903 | "php": ">=5.3.0" 904 | }, 905 | "require-dev": { 906 | "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" 907 | }, 908 | "suggest": { 909 | "ext-pcntl": "For signal handling support when using the StreamSelectLoop" 910 | }, 911 | "type": "library", 912 | "autoload": { 913 | "psr-4": { 914 | "React\\EventLoop\\": "src/" 915 | } 916 | }, 917 | "notification-url": "https://packagist.org/downloads/", 918 | "license": [ 919 | "MIT" 920 | ], 921 | "authors": [ 922 | { 923 | "name": "Christian Lück", 924 | "email": "christian@clue.engineering", 925 | "homepage": "https://clue.engineering/" 926 | }, 927 | { 928 | "name": "Cees-Jan Kiewiet", 929 | "email": "reactphp@ceesjankiewiet.nl", 930 | "homepage": "https://wyrihaximus.net/" 931 | }, 932 | { 933 | "name": "Jan Sorgalla", 934 | "email": "jsorgalla@gmail.com", 935 | "homepage": "https://sorgalla.com/" 936 | }, 937 | { 938 | "name": "Chris Boden", 939 | "email": "cboden@gmail.com", 940 | "homepage": "https://cboden.dev/" 941 | } 942 | ], 943 | "description": "ReactPHP's core reactor event loop that libraries can use for evented I/O.", 944 | "keywords": [ 945 | "asynchronous", 946 | "event-loop" 947 | ], 948 | "support": { 949 | "issues": "https://github.com/reactphp/event-loop/issues", 950 | "source": "https://github.com/reactphp/event-loop/tree/v1.5.0" 951 | }, 952 | "funding": [ 953 | { 954 | "url": "https://opencollective.com/reactphp", 955 | "type": "open_collective" 956 | } 957 | ], 958 | "time": "2023-11-13T13:48:05+00:00" 959 | }, 960 | { 961 | "name": "react/promise", 962 | "version": "v3.2.0", 963 | "source": { 964 | "type": "git", 965 | "url": "https://github.com/reactphp/promise.git", 966 | "reference": "8a164643313c71354582dc850b42b33fa12a4b63" 967 | }, 968 | "dist": { 969 | "type": "zip", 970 | "url": "https://api.github.com/repos/reactphp/promise/zipball/8a164643313c71354582dc850b42b33fa12a4b63", 971 | "reference": "8a164643313c71354582dc850b42b33fa12a4b63", 972 | "shasum": "" 973 | }, 974 | "require": { 975 | "php": ">=7.1.0" 976 | }, 977 | "require-dev": { 978 | "phpstan/phpstan": "1.10.39 || 1.4.10", 979 | "phpunit/phpunit": "^9.6 || ^7.5" 980 | }, 981 | "type": "library", 982 | "autoload": { 983 | "files": [ 984 | "src/functions_include.php" 985 | ], 986 | "psr-4": { 987 | "React\\Promise\\": "src/" 988 | } 989 | }, 990 | "notification-url": "https://packagist.org/downloads/", 991 | "license": [ 992 | "MIT" 993 | ], 994 | "authors": [ 995 | { 996 | "name": "Jan Sorgalla", 997 | "email": "jsorgalla@gmail.com", 998 | "homepage": "https://sorgalla.com/" 999 | }, 1000 | { 1001 | "name": "Christian Lück", 1002 | "email": "christian@clue.engineering", 1003 | "homepage": "https://clue.engineering/" 1004 | }, 1005 | { 1006 | "name": "Cees-Jan Kiewiet", 1007 | "email": "reactphp@ceesjankiewiet.nl", 1008 | "homepage": "https://wyrihaximus.net/" 1009 | }, 1010 | { 1011 | "name": "Chris Boden", 1012 | "email": "cboden@gmail.com", 1013 | "homepage": "https://cboden.dev/" 1014 | } 1015 | ], 1016 | "description": "A lightweight implementation of CommonJS Promises/A for PHP", 1017 | "keywords": [ 1018 | "promise", 1019 | "promises" 1020 | ], 1021 | "support": { 1022 | "issues": "https://github.com/reactphp/promise/issues", 1023 | "source": "https://github.com/reactphp/promise/tree/v3.2.0" 1024 | }, 1025 | "funding": [ 1026 | { 1027 | "url": "https://opencollective.com/reactphp", 1028 | "type": "open_collective" 1029 | } 1030 | ], 1031 | "time": "2024-05-24T10:39:05+00:00" 1032 | }, 1033 | { 1034 | "name": "react/socket", 1035 | "version": "v1.16.0", 1036 | "source": { 1037 | "type": "git", 1038 | "url": "https://github.com/reactphp/socket.git", 1039 | "reference": "23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1" 1040 | }, 1041 | "dist": { 1042 | "type": "zip", 1043 | "url": "https://api.github.com/repos/reactphp/socket/zipball/23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1", 1044 | "reference": "23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1", 1045 | "shasum": "" 1046 | }, 1047 | "require": { 1048 | "evenement/evenement": "^3.0 || ^2.0 || ^1.0", 1049 | "php": ">=5.3.0", 1050 | "react/dns": "^1.13", 1051 | "react/event-loop": "^1.2", 1052 | "react/promise": "^3.2 || ^2.6 || ^1.2.1", 1053 | "react/stream": "^1.4" 1054 | }, 1055 | "require-dev": { 1056 | "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", 1057 | "react/async": "^4.3 || ^3.3 || ^2", 1058 | "react/promise-stream": "^1.4", 1059 | "react/promise-timer": "^1.11" 1060 | }, 1061 | "type": "library", 1062 | "autoload": { 1063 | "psr-4": { 1064 | "React\\Socket\\": "src/" 1065 | } 1066 | }, 1067 | "notification-url": "https://packagist.org/downloads/", 1068 | "license": [ 1069 | "MIT" 1070 | ], 1071 | "authors": [ 1072 | { 1073 | "name": "Christian Lück", 1074 | "email": "christian@clue.engineering", 1075 | "homepage": "https://clue.engineering/" 1076 | }, 1077 | { 1078 | "name": "Cees-Jan Kiewiet", 1079 | "email": "reactphp@ceesjankiewiet.nl", 1080 | "homepage": "https://wyrihaximus.net/" 1081 | }, 1082 | { 1083 | "name": "Jan Sorgalla", 1084 | "email": "jsorgalla@gmail.com", 1085 | "homepage": "https://sorgalla.com/" 1086 | }, 1087 | { 1088 | "name": "Chris Boden", 1089 | "email": "cboden@gmail.com", 1090 | "homepage": "https://cboden.dev/" 1091 | } 1092 | ], 1093 | "description": "Async, streaming plaintext TCP/IP and secure TLS socket server and client connections for ReactPHP", 1094 | "keywords": [ 1095 | "Connection", 1096 | "Socket", 1097 | "async", 1098 | "reactphp", 1099 | "stream" 1100 | ], 1101 | "support": { 1102 | "issues": "https://github.com/reactphp/socket/issues", 1103 | "source": "https://github.com/reactphp/socket/tree/v1.16.0" 1104 | }, 1105 | "funding": [ 1106 | { 1107 | "url": "https://opencollective.com/reactphp", 1108 | "type": "open_collective" 1109 | } 1110 | ], 1111 | "time": "2024-07-26T10:38:09+00:00" 1112 | }, 1113 | { 1114 | "name": "react/stream", 1115 | "version": "v1.4.0", 1116 | "source": { 1117 | "type": "git", 1118 | "url": "https://github.com/reactphp/stream.git", 1119 | "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d" 1120 | }, 1121 | "dist": { 1122 | "type": "zip", 1123 | "url": "https://api.github.com/repos/reactphp/stream/zipball/1e5b0acb8fe55143b5b426817155190eb6f5b18d", 1124 | "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d", 1125 | "shasum": "" 1126 | }, 1127 | "require": { 1128 | "evenement/evenement": "^3.0 || ^2.0 || ^1.0", 1129 | "php": ">=5.3.8", 1130 | "react/event-loop": "^1.2" 1131 | }, 1132 | "require-dev": { 1133 | "clue/stream-filter": "~1.2", 1134 | "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" 1135 | }, 1136 | "type": "library", 1137 | "autoload": { 1138 | "psr-4": { 1139 | "React\\Stream\\": "src/" 1140 | } 1141 | }, 1142 | "notification-url": "https://packagist.org/downloads/", 1143 | "license": [ 1144 | "MIT" 1145 | ], 1146 | "authors": [ 1147 | { 1148 | "name": "Christian Lück", 1149 | "email": "christian@clue.engineering", 1150 | "homepage": "https://clue.engineering/" 1151 | }, 1152 | { 1153 | "name": "Cees-Jan Kiewiet", 1154 | "email": "reactphp@ceesjankiewiet.nl", 1155 | "homepage": "https://wyrihaximus.net/" 1156 | }, 1157 | { 1158 | "name": "Jan Sorgalla", 1159 | "email": "jsorgalla@gmail.com", 1160 | "homepage": "https://sorgalla.com/" 1161 | }, 1162 | { 1163 | "name": "Chris Boden", 1164 | "email": "cboden@gmail.com", 1165 | "homepage": "https://cboden.dev/" 1166 | } 1167 | ], 1168 | "description": "Event-driven readable and writable streams for non-blocking I/O in ReactPHP", 1169 | "keywords": [ 1170 | "event-driven", 1171 | "io", 1172 | "non-blocking", 1173 | "pipe", 1174 | "reactphp", 1175 | "readable", 1176 | "stream", 1177 | "writable" 1178 | ], 1179 | "support": { 1180 | "issues": "https://github.com/reactphp/stream/issues", 1181 | "source": "https://github.com/reactphp/stream/tree/v1.4.0" 1182 | }, 1183 | "funding": [ 1184 | { 1185 | "url": "https://opencollective.com/reactphp", 1186 | "type": "open_collective" 1187 | } 1188 | ], 1189 | "time": "2024-06-11T12:45:25+00:00" 1190 | }, 1191 | { 1192 | "name": "sebastian/diff", 1193 | "version": "7.0.0", 1194 | "source": { 1195 | "type": "git", 1196 | "url": "https://github.com/sebastianbergmann/diff.git", 1197 | "reference": "7ab1ea946c012266ca32390913653d844ecd085f" 1198 | }, 1199 | "dist": { 1200 | "type": "zip", 1201 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7ab1ea946c012266ca32390913653d844ecd085f", 1202 | "reference": "7ab1ea946c012266ca32390913653d844ecd085f", 1203 | "shasum": "" 1204 | }, 1205 | "require": { 1206 | "php": ">=8.3" 1207 | }, 1208 | "require-dev": { 1209 | "phpunit/phpunit": "^12.0", 1210 | "symfony/process": "^7.2" 1211 | }, 1212 | "type": "library", 1213 | "extra": { 1214 | "branch-alias": { 1215 | "dev-main": "7.0-dev" 1216 | } 1217 | }, 1218 | "autoload": { 1219 | "classmap": [ 1220 | "src/" 1221 | ] 1222 | }, 1223 | "notification-url": "https://packagist.org/downloads/", 1224 | "license": [ 1225 | "BSD-3-Clause" 1226 | ], 1227 | "authors": [ 1228 | { 1229 | "name": "Sebastian Bergmann", 1230 | "email": "sebastian@phpunit.de" 1231 | }, 1232 | { 1233 | "name": "Kore Nordmann", 1234 | "email": "mail@kore-nordmann.de" 1235 | } 1236 | ], 1237 | "description": "Diff implementation", 1238 | "homepage": "https://github.com/sebastianbergmann/diff", 1239 | "keywords": [ 1240 | "diff", 1241 | "udiff", 1242 | "unidiff", 1243 | "unified diff" 1244 | ], 1245 | "support": { 1246 | "issues": "https://github.com/sebastianbergmann/diff/issues", 1247 | "security": "https://github.com/sebastianbergmann/diff/security/policy", 1248 | "source": "https://github.com/sebastianbergmann/diff/tree/7.0.0" 1249 | }, 1250 | "funding": [ 1251 | { 1252 | "url": "https://github.com/sebastianbergmann", 1253 | "type": "github" 1254 | } 1255 | ], 1256 | "time": "2025-02-07T04:55:46+00:00" 1257 | }, 1258 | { 1259 | "name": "symfony/console", 1260 | "version": "v7.3.2", 1261 | "source": { 1262 | "type": "git", 1263 | "url": "https://github.com/symfony/console.git", 1264 | "reference": "5f360ebc65c55265a74d23d7fe27f957870158a1" 1265 | }, 1266 | "dist": { 1267 | "type": "zip", 1268 | "url": "https://api.github.com/repos/symfony/console/zipball/5f360ebc65c55265a74d23d7fe27f957870158a1", 1269 | "reference": "5f360ebc65c55265a74d23d7fe27f957870158a1", 1270 | "shasum": "" 1271 | }, 1272 | "require": { 1273 | "php": ">=8.2", 1274 | "symfony/deprecation-contracts": "^2.5|^3", 1275 | "symfony/polyfill-mbstring": "~1.0", 1276 | "symfony/service-contracts": "^2.5|^3", 1277 | "symfony/string": "^7.2" 1278 | }, 1279 | "conflict": { 1280 | "symfony/dependency-injection": "<6.4", 1281 | "symfony/dotenv": "<6.4", 1282 | "symfony/event-dispatcher": "<6.4", 1283 | "symfony/lock": "<6.4", 1284 | "symfony/process": "<6.4" 1285 | }, 1286 | "provide": { 1287 | "psr/log-implementation": "1.0|2.0|3.0" 1288 | }, 1289 | "require-dev": { 1290 | "psr/log": "^1|^2|^3", 1291 | "symfony/config": "^6.4|^7.0", 1292 | "symfony/dependency-injection": "^6.4|^7.0", 1293 | "symfony/event-dispatcher": "^6.4|^7.0", 1294 | "symfony/http-foundation": "^6.4|^7.0", 1295 | "symfony/http-kernel": "^6.4|^7.0", 1296 | "symfony/lock": "^6.4|^7.0", 1297 | "symfony/messenger": "^6.4|^7.0", 1298 | "symfony/process": "^6.4|^7.0", 1299 | "symfony/stopwatch": "^6.4|^7.0", 1300 | "symfony/var-dumper": "^6.4|^7.0" 1301 | }, 1302 | "type": "library", 1303 | "autoload": { 1304 | "psr-4": { 1305 | "Symfony\\Component\\Console\\": "" 1306 | }, 1307 | "exclude-from-classmap": [ 1308 | "/Tests/" 1309 | ] 1310 | }, 1311 | "notification-url": "https://packagist.org/downloads/", 1312 | "license": [ 1313 | "MIT" 1314 | ], 1315 | "authors": [ 1316 | { 1317 | "name": "Fabien Potencier", 1318 | "email": "fabien@symfony.com" 1319 | }, 1320 | { 1321 | "name": "Symfony Community", 1322 | "homepage": "https://symfony.com/contributors" 1323 | } 1324 | ], 1325 | "description": "Eases the creation of beautiful and testable command line interfaces", 1326 | "homepage": "https://symfony.com", 1327 | "keywords": [ 1328 | "cli", 1329 | "command-line", 1330 | "console", 1331 | "terminal" 1332 | ], 1333 | "support": { 1334 | "source": "https://github.com/symfony/console/tree/v7.3.2" 1335 | }, 1336 | "funding": [ 1337 | { 1338 | "url": "https://symfony.com/sponsor", 1339 | "type": "custom" 1340 | }, 1341 | { 1342 | "url": "https://github.com/fabpot", 1343 | "type": "github" 1344 | }, 1345 | { 1346 | "url": "https://github.com/nicolas-grekas", 1347 | "type": "github" 1348 | }, 1349 | { 1350 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1351 | "type": "tidelift" 1352 | } 1353 | ], 1354 | "time": "2025-07-30T17:13:41+00:00" 1355 | }, 1356 | { 1357 | "name": "symfony/deprecation-contracts", 1358 | "version": "v3.6.0", 1359 | "source": { 1360 | "type": "git", 1361 | "url": "https://github.com/symfony/deprecation-contracts.git", 1362 | "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" 1363 | }, 1364 | "dist": { 1365 | "type": "zip", 1366 | "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", 1367 | "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", 1368 | "shasum": "" 1369 | }, 1370 | "require": { 1371 | "php": ">=8.1" 1372 | }, 1373 | "type": "library", 1374 | "extra": { 1375 | "thanks": { 1376 | "url": "https://github.com/symfony/contracts", 1377 | "name": "symfony/contracts" 1378 | }, 1379 | "branch-alias": { 1380 | "dev-main": "3.6-dev" 1381 | } 1382 | }, 1383 | "autoload": { 1384 | "files": [ 1385 | "function.php" 1386 | ] 1387 | }, 1388 | "notification-url": "https://packagist.org/downloads/", 1389 | "license": [ 1390 | "MIT" 1391 | ], 1392 | "authors": [ 1393 | { 1394 | "name": "Nicolas Grekas", 1395 | "email": "p@tchwork.com" 1396 | }, 1397 | { 1398 | "name": "Symfony Community", 1399 | "homepage": "https://symfony.com/contributors" 1400 | } 1401 | ], 1402 | "description": "A generic function and convention to trigger deprecation notices", 1403 | "homepage": "https://symfony.com", 1404 | "support": { 1405 | "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0" 1406 | }, 1407 | "funding": [ 1408 | { 1409 | "url": "https://symfony.com/sponsor", 1410 | "type": "custom" 1411 | }, 1412 | { 1413 | "url": "https://github.com/fabpot", 1414 | "type": "github" 1415 | }, 1416 | { 1417 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1418 | "type": "tidelift" 1419 | } 1420 | ], 1421 | "time": "2024-09-25T14:21:43+00:00" 1422 | }, 1423 | { 1424 | "name": "symfony/event-dispatcher", 1425 | "version": "v7.3.0", 1426 | "source": { 1427 | "type": "git", 1428 | "url": "https://github.com/symfony/event-dispatcher.git", 1429 | "reference": "497f73ac996a598c92409b44ac43b6690c4f666d" 1430 | }, 1431 | "dist": { 1432 | "type": "zip", 1433 | "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/497f73ac996a598c92409b44ac43b6690c4f666d", 1434 | "reference": "497f73ac996a598c92409b44ac43b6690c4f666d", 1435 | "shasum": "" 1436 | }, 1437 | "require": { 1438 | "php": ">=8.2", 1439 | "symfony/event-dispatcher-contracts": "^2.5|^3" 1440 | }, 1441 | "conflict": { 1442 | "symfony/dependency-injection": "<6.4", 1443 | "symfony/service-contracts": "<2.5" 1444 | }, 1445 | "provide": { 1446 | "psr/event-dispatcher-implementation": "1.0", 1447 | "symfony/event-dispatcher-implementation": "2.0|3.0" 1448 | }, 1449 | "require-dev": { 1450 | "psr/log": "^1|^2|^3", 1451 | "symfony/config": "^6.4|^7.0", 1452 | "symfony/dependency-injection": "^6.4|^7.0", 1453 | "symfony/error-handler": "^6.4|^7.0", 1454 | "symfony/expression-language": "^6.4|^7.0", 1455 | "symfony/http-foundation": "^6.4|^7.0", 1456 | "symfony/service-contracts": "^2.5|^3", 1457 | "symfony/stopwatch": "^6.4|^7.0" 1458 | }, 1459 | "type": "library", 1460 | "autoload": { 1461 | "psr-4": { 1462 | "Symfony\\Component\\EventDispatcher\\": "" 1463 | }, 1464 | "exclude-from-classmap": [ 1465 | "/Tests/" 1466 | ] 1467 | }, 1468 | "notification-url": "https://packagist.org/downloads/", 1469 | "license": [ 1470 | "MIT" 1471 | ], 1472 | "authors": [ 1473 | { 1474 | "name": "Fabien Potencier", 1475 | "email": "fabien@symfony.com" 1476 | }, 1477 | { 1478 | "name": "Symfony Community", 1479 | "homepage": "https://symfony.com/contributors" 1480 | } 1481 | ], 1482 | "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", 1483 | "homepage": "https://symfony.com", 1484 | "support": { 1485 | "source": "https://github.com/symfony/event-dispatcher/tree/v7.3.0" 1486 | }, 1487 | "funding": [ 1488 | { 1489 | "url": "https://symfony.com/sponsor", 1490 | "type": "custom" 1491 | }, 1492 | { 1493 | "url": "https://github.com/fabpot", 1494 | "type": "github" 1495 | }, 1496 | { 1497 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1498 | "type": "tidelift" 1499 | } 1500 | ], 1501 | "time": "2025-04-22T09:11:45+00:00" 1502 | }, 1503 | { 1504 | "name": "symfony/event-dispatcher-contracts", 1505 | "version": "v3.6.0", 1506 | "source": { 1507 | "type": "git", 1508 | "url": "https://github.com/symfony/event-dispatcher-contracts.git", 1509 | "reference": "59eb412e93815df44f05f342958efa9f46b1e586" 1510 | }, 1511 | "dist": { 1512 | "type": "zip", 1513 | "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/59eb412e93815df44f05f342958efa9f46b1e586", 1514 | "reference": "59eb412e93815df44f05f342958efa9f46b1e586", 1515 | "shasum": "" 1516 | }, 1517 | "require": { 1518 | "php": ">=8.1", 1519 | "psr/event-dispatcher": "^1" 1520 | }, 1521 | "type": "library", 1522 | "extra": { 1523 | "thanks": { 1524 | "url": "https://github.com/symfony/contracts", 1525 | "name": "symfony/contracts" 1526 | }, 1527 | "branch-alias": { 1528 | "dev-main": "3.6-dev" 1529 | } 1530 | }, 1531 | "autoload": { 1532 | "psr-4": { 1533 | "Symfony\\Contracts\\EventDispatcher\\": "" 1534 | } 1535 | }, 1536 | "notification-url": "https://packagist.org/downloads/", 1537 | "license": [ 1538 | "MIT" 1539 | ], 1540 | "authors": [ 1541 | { 1542 | "name": "Nicolas Grekas", 1543 | "email": "p@tchwork.com" 1544 | }, 1545 | { 1546 | "name": "Symfony Community", 1547 | "homepage": "https://symfony.com/contributors" 1548 | } 1549 | ], 1550 | "description": "Generic abstractions related to dispatching event", 1551 | "homepage": "https://symfony.com", 1552 | "keywords": [ 1553 | "abstractions", 1554 | "contracts", 1555 | "decoupling", 1556 | "interfaces", 1557 | "interoperability", 1558 | "standards" 1559 | ], 1560 | "support": { 1561 | "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.6.0" 1562 | }, 1563 | "funding": [ 1564 | { 1565 | "url": "https://symfony.com/sponsor", 1566 | "type": "custom" 1567 | }, 1568 | { 1569 | "url": "https://github.com/fabpot", 1570 | "type": "github" 1571 | }, 1572 | { 1573 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1574 | "type": "tidelift" 1575 | } 1576 | ], 1577 | "time": "2024-09-25T14:21:43+00:00" 1578 | }, 1579 | { 1580 | "name": "symfony/filesystem", 1581 | "version": "v7.3.2", 1582 | "source": { 1583 | "type": "git", 1584 | "url": "https://github.com/symfony/filesystem.git", 1585 | "reference": "edcbb768a186b5c3f25d0643159a787d3e63b7fd" 1586 | }, 1587 | "dist": { 1588 | "type": "zip", 1589 | "url": "https://api.github.com/repos/symfony/filesystem/zipball/edcbb768a186b5c3f25d0643159a787d3e63b7fd", 1590 | "reference": "edcbb768a186b5c3f25d0643159a787d3e63b7fd", 1591 | "shasum": "" 1592 | }, 1593 | "require": { 1594 | "php": ">=8.2", 1595 | "symfony/polyfill-ctype": "~1.8", 1596 | "symfony/polyfill-mbstring": "~1.8" 1597 | }, 1598 | "require-dev": { 1599 | "symfony/process": "^6.4|^7.0" 1600 | }, 1601 | "type": "library", 1602 | "autoload": { 1603 | "psr-4": { 1604 | "Symfony\\Component\\Filesystem\\": "" 1605 | }, 1606 | "exclude-from-classmap": [ 1607 | "/Tests/" 1608 | ] 1609 | }, 1610 | "notification-url": "https://packagist.org/downloads/", 1611 | "license": [ 1612 | "MIT" 1613 | ], 1614 | "authors": [ 1615 | { 1616 | "name": "Fabien Potencier", 1617 | "email": "fabien@symfony.com" 1618 | }, 1619 | { 1620 | "name": "Symfony Community", 1621 | "homepage": "https://symfony.com/contributors" 1622 | } 1623 | ], 1624 | "description": "Provides basic utilities for the filesystem", 1625 | "homepage": "https://symfony.com", 1626 | "support": { 1627 | "source": "https://github.com/symfony/filesystem/tree/v7.3.2" 1628 | }, 1629 | "funding": [ 1630 | { 1631 | "url": "https://symfony.com/sponsor", 1632 | "type": "custom" 1633 | }, 1634 | { 1635 | "url": "https://github.com/fabpot", 1636 | "type": "github" 1637 | }, 1638 | { 1639 | "url": "https://github.com/nicolas-grekas", 1640 | "type": "github" 1641 | }, 1642 | { 1643 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1644 | "type": "tidelift" 1645 | } 1646 | ], 1647 | "time": "2025-07-07T08:17:47+00:00" 1648 | }, 1649 | { 1650 | "name": "symfony/finder", 1651 | "version": "v7.3.2", 1652 | "source": { 1653 | "type": "git", 1654 | "url": "https://github.com/symfony/finder.git", 1655 | "reference": "2a6614966ba1074fa93dae0bc804227422df4dfe" 1656 | }, 1657 | "dist": { 1658 | "type": "zip", 1659 | "url": "https://api.github.com/repos/symfony/finder/zipball/2a6614966ba1074fa93dae0bc804227422df4dfe", 1660 | "reference": "2a6614966ba1074fa93dae0bc804227422df4dfe", 1661 | "shasum": "" 1662 | }, 1663 | "require": { 1664 | "php": ">=8.2" 1665 | }, 1666 | "require-dev": { 1667 | "symfony/filesystem": "^6.4|^7.0" 1668 | }, 1669 | "type": "library", 1670 | "autoload": { 1671 | "psr-4": { 1672 | "Symfony\\Component\\Finder\\": "" 1673 | }, 1674 | "exclude-from-classmap": [ 1675 | "/Tests/" 1676 | ] 1677 | }, 1678 | "notification-url": "https://packagist.org/downloads/", 1679 | "license": [ 1680 | "MIT" 1681 | ], 1682 | "authors": [ 1683 | { 1684 | "name": "Fabien Potencier", 1685 | "email": "fabien@symfony.com" 1686 | }, 1687 | { 1688 | "name": "Symfony Community", 1689 | "homepage": "https://symfony.com/contributors" 1690 | } 1691 | ], 1692 | "description": "Finds files and directories via an intuitive fluent interface", 1693 | "homepage": "https://symfony.com", 1694 | "support": { 1695 | "source": "https://github.com/symfony/finder/tree/v7.3.2" 1696 | }, 1697 | "funding": [ 1698 | { 1699 | "url": "https://symfony.com/sponsor", 1700 | "type": "custom" 1701 | }, 1702 | { 1703 | "url": "https://github.com/fabpot", 1704 | "type": "github" 1705 | }, 1706 | { 1707 | "url": "https://github.com/nicolas-grekas", 1708 | "type": "github" 1709 | }, 1710 | { 1711 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1712 | "type": "tidelift" 1713 | } 1714 | ], 1715 | "time": "2025-07-15T13:41:35+00:00" 1716 | }, 1717 | { 1718 | "name": "symfony/options-resolver", 1719 | "version": "v7.3.2", 1720 | "source": { 1721 | "type": "git", 1722 | "url": "https://github.com/symfony/options-resolver.git", 1723 | "reference": "119bcf13e67dbd188e5dbc74228b1686f66acd37" 1724 | }, 1725 | "dist": { 1726 | "type": "zip", 1727 | "url": "https://api.github.com/repos/symfony/options-resolver/zipball/119bcf13e67dbd188e5dbc74228b1686f66acd37", 1728 | "reference": "119bcf13e67dbd188e5dbc74228b1686f66acd37", 1729 | "shasum": "" 1730 | }, 1731 | "require": { 1732 | "php": ">=8.2", 1733 | "symfony/deprecation-contracts": "^2.5|^3" 1734 | }, 1735 | "type": "library", 1736 | "autoload": { 1737 | "psr-4": { 1738 | "Symfony\\Component\\OptionsResolver\\": "" 1739 | }, 1740 | "exclude-from-classmap": [ 1741 | "/Tests/" 1742 | ] 1743 | }, 1744 | "notification-url": "https://packagist.org/downloads/", 1745 | "license": [ 1746 | "MIT" 1747 | ], 1748 | "authors": [ 1749 | { 1750 | "name": "Fabien Potencier", 1751 | "email": "fabien@symfony.com" 1752 | }, 1753 | { 1754 | "name": "Symfony Community", 1755 | "homepage": "https://symfony.com/contributors" 1756 | } 1757 | ], 1758 | "description": "Provides an improved replacement for the array_replace PHP function", 1759 | "homepage": "https://symfony.com", 1760 | "keywords": [ 1761 | "config", 1762 | "configuration", 1763 | "options" 1764 | ], 1765 | "support": { 1766 | "source": "https://github.com/symfony/options-resolver/tree/v7.3.2" 1767 | }, 1768 | "funding": [ 1769 | { 1770 | "url": "https://symfony.com/sponsor", 1771 | "type": "custom" 1772 | }, 1773 | { 1774 | "url": "https://github.com/fabpot", 1775 | "type": "github" 1776 | }, 1777 | { 1778 | "url": "https://github.com/nicolas-grekas", 1779 | "type": "github" 1780 | }, 1781 | { 1782 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1783 | "type": "tidelift" 1784 | } 1785 | ], 1786 | "time": "2025-07-15T11:36:08+00:00" 1787 | }, 1788 | { 1789 | "name": "symfony/polyfill-ctype", 1790 | "version": "v1.32.0", 1791 | "source": { 1792 | "type": "git", 1793 | "url": "https://github.com/symfony/polyfill-ctype.git", 1794 | "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" 1795 | }, 1796 | "dist": { 1797 | "type": "zip", 1798 | "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", 1799 | "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", 1800 | "shasum": "" 1801 | }, 1802 | "require": { 1803 | "php": ">=7.2" 1804 | }, 1805 | "provide": { 1806 | "ext-ctype": "*" 1807 | }, 1808 | "suggest": { 1809 | "ext-ctype": "For best performance" 1810 | }, 1811 | "type": "library", 1812 | "extra": { 1813 | "thanks": { 1814 | "url": "https://github.com/symfony/polyfill", 1815 | "name": "symfony/polyfill" 1816 | } 1817 | }, 1818 | "autoload": { 1819 | "files": [ 1820 | "bootstrap.php" 1821 | ], 1822 | "psr-4": { 1823 | "Symfony\\Polyfill\\Ctype\\": "" 1824 | } 1825 | }, 1826 | "notification-url": "https://packagist.org/downloads/", 1827 | "license": [ 1828 | "MIT" 1829 | ], 1830 | "authors": [ 1831 | { 1832 | "name": "Gert de Pagter", 1833 | "email": "BackEndTea@gmail.com" 1834 | }, 1835 | { 1836 | "name": "Symfony Community", 1837 | "homepage": "https://symfony.com/contributors" 1838 | } 1839 | ], 1840 | "description": "Symfony polyfill for ctype functions", 1841 | "homepage": "https://symfony.com", 1842 | "keywords": [ 1843 | "compatibility", 1844 | "ctype", 1845 | "polyfill", 1846 | "portable" 1847 | ], 1848 | "support": { 1849 | "source": "https://github.com/symfony/polyfill-ctype/tree/v1.32.0" 1850 | }, 1851 | "funding": [ 1852 | { 1853 | "url": "https://symfony.com/sponsor", 1854 | "type": "custom" 1855 | }, 1856 | { 1857 | "url": "https://github.com/fabpot", 1858 | "type": "github" 1859 | }, 1860 | { 1861 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1862 | "type": "tidelift" 1863 | } 1864 | ], 1865 | "time": "2024-09-09T11:45:10+00:00" 1866 | }, 1867 | { 1868 | "name": "symfony/polyfill-intl-grapheme", 1869 | "version": "v1.32.0", 1870 | "source": { 1871 | "type": "git", 1872 | "url": "https://github.com/symfony/polyfill-intl-grapheme.git", 1873 | "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" 1874 | }, 1875 | "dist": { 1876 | "type": "zip", 1877 | "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", 1878 | "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", 1879 | "shasum": "" 1880 | }, 1881 | "require": { 1882 | "php": ">=7.2" 1883 | }, 1884 | "suggest": { 1885 | "ext-intl": "For best performance" 1886 | }, 1887 | "type": "library", 1888 | "extra": { 1889 | "thanks": { 1890 | "url": "https://github.com/symfony/polyfill", 1891 | "name": "symfony/polyfill" 1892 | } 1893 | }, 1894 | "autoload": { 1895 | "files": [ 1896 | "bootstrap.php" 1897 | ], 1898 | "psr-4": { 1899 | "Symfony\\Polyfill\\Intl\\Grapheme\\": "" 1900 | } 1901 | }, 1902 | "notification-url": "https://packagist.org/downloads/", 1903 | "license": [ 1904 | "MIT" 1905 | ], 1906 | "authors": [ 1907 | { 1908 | "name": "Nicolas Grekas", 1909 | "email": "p@tchwork.com" 1910 | }, 1911 | { 1912 | "name": "Symfony Community", 1913 | "homepage": "https://symfony.com/contributors" 1914 | } 1915 | ], 1916 | "description": "Symfony polyfill for intl's grapheme_* functions", 1917 | "homepage": "https://symfony.com", 1918 | "keywords": [ 1919 | "compatibility", 1920 | "grapheme", 1921 | "intl", 1922 | "polyfill", 1923 | "portable", 1924 | "shim" 1925 | ], 1926 | "support": { 1927 | "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.32.0" 1928 | }, 1929 | "funding": [ 1930 | { 1931 | "url": "https://symfony.com/sponsor", 1932 | "type": "custom" 1933 | }, 1934 | { 1935 | "url": "https://github.com/fabpot", 1936 | "type": "github" 1937 | }, 1938 | { 1939 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1940 | "type": "tidelift" 1941 | } 1942 | ], 1943 | "time": "2024-09-09T11:45:10+00:00" 1944 | }, 1945 | { 1946 | "name": "symfony/polyfill-intl-normalizer", 1947 | "version": "v1.32.0", 1948 | "source": { 1949 | "type": "git", 1950 | "url": "https://github.com/symfony/polyfill-intl-normalizer.git", 1951 | "reference": "3833d7255cc303546435cb650316bff708a1c75c" 1952 | }, 1953 | "dist": { 1954 | "type": "zip", 1955 | "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", 1956 | "reference": "3833d7255cc303546435cb650316bff708a1c75c", 1957 | "shasum": "" 1958 | }, 1959 | "require": { 1960 | "php": ">=7.2" 1961 | }, 1962 | "suggest": { 1963 | "ext-intl": "For best performance" 1964 | }, 1965 | "type": "library", 1966 | "extra": { 1967 | "thanks": { 1968 | "url": "https://github.com/symfony/polyfill", 1969 | "name": "symfony/polyfill" 1970 | } 1971 | }, 1972 | "autoload": { 1973 | "files": [ 1974 | "bootstrap.php" 1975 | ], 1976 | "psr-4": { 1977 | "Symfony\\Polyfill\\Intl\\Normalizer\\": "" 1978 | }, 1979 | "classmap": [ 1980 | "Resources/stubs" 1981 | ] 1982 | }, 1983 | "notification-url": "https://packagist.org/downloads/", 1984 | "license": [ 1985 | "MIT" 1986 | ], 1987 | "authors": [ 1988 | { 1989 | "name": "Nicolas Grekas", 1990 | "email": "p@tchwork.com" 1991 | }, 1992 | { 1993 | "name": "Symfony Community", 1994 | "homepage": "https://symfony.com/contributors" 1995 | } 1996 | ], 1997 | "description": "Symfony polyfill for intl's Normalizer class and related functions", 1998 | "homepage": "https://symfony.com", 1999 | "keywords": [ 2000 | "compatibility", 2001 | "intl", 2002 | "normalizer", 2003 | "polyfill", 2004 | "portable", 2005 | "shim" 2006 | ], 2007 | "support": { 2008 | "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.32.0" 2009 | }, 2010 | "funding": [ 2011 | { 2012 | "url": "https://symfony.com/sponsor", 2013 | "type": "custom" 2014 | }, 2015 | { 2016 | "url": "https://github.com/fabpot", 2017 | "type": "github" 2018 | }, 2019 | { 2020 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 2021 | "type": "tidelift" 2022 | } 2023 | ], 2024 | "time": "2024-09-09T11:45:10+00:00" 2025 | }, 2026 | { 2027 | "name": "symfony/polyfill-mbstring", 2028 | "version": "v1.32.0", 2029 | "source": { 2030 | "type": "git", 2031 | "url": "https://github.com/symfony/polyfill-mbstring.git", 2032 | "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" 2033 | }, 2034 | "dist": { 2035 | "type": "zip", 2036 | "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", 2037 | "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", 2038 | "shasum": "" 2039 | }, 2040 | "require": { 2041 | "ext-iconv": "*", 2042 | "php": ">=7.2" 2043 | }, 2044 | "provide": { 2045 | "ext-mbstring": "*" 2046 | }, 2047 | "suggest": { 2048 | "ext-mbstring": "For best performance" 2049 | }, 2050 | "type": "library", 2051 | "extra": { 2052 | "thanks": { 2053 | "url": "https://github.com/symfony/polyfill", 2054 | "name": "symfony/polyfill" 2055 | } 2056 | }, 2057 | "autoload": { 2058 | "files": [ 2059 | "bootstrap.php" 2060 | ], 2061 | "psr-4": { 2062 | "Symfony\\Polyfill\\Mbstring\\": "" 2063 | } 2064 | }, 2065 | "notification-url": "https://packagist.org/downloads/", 2066 | "license": [ 2067 | "MIT" 2068 | ], 2069 | "authors": [ 2070 | { 2071 | "name": "Nicolas Grekas", 2072 | "email": "p@tchwork.com" 2073 | }, 2074 | { 2075 | "name": "Symfony Community", 2076 | "homepage": "https://symfony.com/contributors" 2077 | } 2078 | ], 2079 | "description": "Symfony polyfill for the Mbstring extension", 2080 | "homepage": "https://symfony.com", 2081 | "keywords": [ 2082 | "compatibility", 2083 | "mbstring", 2084 | "polyfill", 2085 | "portable", 2086 | "shim" 2087 | ], 2088 | "support": { 2089 | "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0" 2090 | }, 2091 | "funding": [ 2092 | { 2093 | "url": "https://symfony.com/sponsor", 2094 | "type": "custom" 2095 | }, 2096 | { 2097 | "url": "https://github.com/fabpot", 2098 | "type": "github" 2099 | }, 2100 | { 2101 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 2102 | "type": "tidelift" 2103 | } 2104 | ], 2105 | "time": "2024-12-23T08:48:59+00:00" 2106 | }, 2107 | { 2108 | "name": "symfony/polyfill-php80", 2109 | "version": "v1.32.0", 2110 | "source": { 2111 | "type": "git", 2112 | "url": "https://github.com/symfony/polyfill-php80.git", 2113 | "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608" 2114 | }, 2115 | "dist": { 2116 | "type": "zip", 2117 | "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608", 2118 | "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608", 2119 | "shasum": "" 2120 | }, 2121 | "require": { 2122 | "php": ">=7.2" 2123 | }, 2124 | "type": "library", 2125 | "extra": { 2126 | "thanks": { 2127 | "url": "https://github.com/symfony/polyfill", 2128 | "name": "symfony/polyfill" 2129 | } 2130 | }, 2131 | "autoload": { 2132 | "files": [ 2133 | "bootstrap.php" 2134 | ], 2135 | "psr-4": { 2136 | "Symfony\\Polyfill\\Php80\\": "" 2137 | }, 2138 | "classmap": [ 2139 | "Resources/stubs" 2140 | ] 2141 | }, 2142 | "notification-url": "https://packagist.org/downloads/", 2143 | "license": [ 2144 | "MIT" 2145 | ], 2146 | "authors": [ 2147 | { 2148 | "name": "Ion Bazan", 2149 | "email": "ion.bazan@gmail.com" 2150 | }, 2151 | { 2152 | "name": "Nicolas Grekas", 2153 | "email": "p@tchwork.com" 2154 | }, 2155 | { 2156 | "name": "Symfony Community", 2157 | "homepage": "https://symfony.com/contributors" 2158 | } 2159 | ], 2160 | "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", 2161 | "homepage": "https://symfony.com", 2162 | "keywords": [ 2163 | "compatibility", 2164 | "polyfill", 2165 | "portable", 2166 | "shim" 2167 | ], 2168 | "support": { 2169 | "source": "https://github.com/symfony/polyfill-php80/tree/v1.32.0" 2170 | }, 2171 | "funding": [ 2172 | { 2173 | "url": "https://symfony.com/sponsor", 2174 | "type": "custom" 2175 | }, 2176 | { 2177 | "url": "https://github.com/fabpot", 2178 | "type": "github" 2179 | }, 2180 | { 2181 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 2182 | "type": "tidelift" 2183 | } 2184 | ], 2185 | "time": "2025-01-02T08:10:11+00:00" 2186 | }, 2187 | { 2188 | "name": "symfony/polyfill-php81", 2189 | "version": "v1.32.0", 2190 | "source": { 2191 | "type": "git", 2192 | "url": "https://github.com/symfony/polyfill-php81.git", 2193 | "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c" 2194 | }, 2195 | "dist": { 2196 | "type": "zip", 2197 | "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", 2198 | "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", 2199 | "shasum": "" 2200 | }, 2201 | "require": { 2202 | "php": ">=7.2" 2203 | }, 2204 | "type": "library", 2205 | "extra": { 2206 | "thanks": { 2207 | "url": "https://github.com/symfony/polyfill", 2208 | "name": "symfony/polyfill" 2209 | } 2210 | }, 2211 | "autoload": { 2212 | "files": [ 2213 | "bootstrap.php" 2214 | ], 2215 | "psr-4": { 2216 | "Symfony\\Polyfill\\Php81\\": "" 2217 | }, 2218 | "classmap": [ 2219 | "Resources/stubs" 2220 | ] 2221 | }, 2222 | "notification-url": "https://packagist.org/downloads/", 2223 | "license": [ 2224 | "MIT" 2225 | ], 2226 | "authors": [ 2227 | { 2228 | "name": "Nicolas Grekas", 2229 | "email": "p@tchwork.com" 2230 | }, 2231 | { 2232 | "name": "Symfony Community", 2233 | "homepage": "https://symfony.com/contributors" 2234 | } 2235 | ], 2236 | "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", 2237 | "homepage": "https://symfony.com", 2238 | "keywords": [ 2239 | "compatibility", 2240 | "polyfill", 2241 | "portable", 2242 | "shim" 2243 | ], 2244 | "support": { 2245 | "source": "https://github.com/symfony/polyfill-php81/tree/v1.32.0" 2246 | }, 2247 | "funding": [ 2248 | { 2249 | "url": "https://symfony.com/sponsor", 2250 | "type": "custom" 2251 | }, 2252 | { 2253 | "url": "https://github.com/fabpot", 2254 | "type": "github" 2255 | }, 2256 | { 2257 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 2258 | "type": "tidelift" 2259 | } 2260 | ], 2261 | "time": "2024-09-09T11:45:10+00:00" 2262 | }, 2263 | { 2264 | "name": "symfony/process", 2265 | "version": "v7.3.0", 2266 | "source": { 2267 | "type": "git", 2268 | "url": "https://github.com/symfony/process.git", 2269 | "reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af" 2270 | }, 2271 | "dist": { 2272 | "type": "zip", 2273 | "url": "https://api.github.com/repos/symfony/process/zipball/40c295f2deb408d5e9d2d32b8ba1dd61e36f05af", 2274 | "reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af", 2275 | "shasum": "" 2276 | }, 2277 | "require": { 2278 | "php": ">=8.2" 2279 | }, 2280 | "type": "library", 2281 | "autoload": { 2282 | "psr-4": { 2283 | "Symfony\\Component\\Process\\": "" 2284 | }, 2285 | "exclude-from-classmap": [ 2286 | "/Tests/" 2287 | ] 2288 | }, 2289 | "notification-url": "https://packagist.org/downloads/", 2290 | "license": [ 2291 | "MIT" 2292 | ], 2293 | "authors": [ 2294 | { 2295 | "name": "Fabien Potencier", 2296 | "email": "fabien@symfony.com" 2297 | }, 2298 | { 2299 | "name": "Symfony Community", 2300 | "homepage": "https://symfony.com/contributors" 2301 | } 2302 | ], 2303 | "description": "Executes commands in sub-processes", 2304 | "homepage": "https://symfony.com", 2305 | "support": { 2306 | "source": "https://github.com/symfony/process/tree/v7.3.0" 2307 | }, 2308 | "funding": [ 2309 | { 2310 | "url": "https://symfony.com/sponsor", 2311 | "type": "custom" 2312 | }, 2313 | { 2314 | "url": "https://github.com/fabpot", 2315 | "type": "github" 2316 | }, 2317 | { 2318 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 2319 | "type": "tidelift" 2320 | } 2321 | ], 2322 | "time": "2025-04-17T09:11:12+00:00" 2323 | }, 2324 | { 2325 | "name": "symfony/service-contracts", 2326 | "version": "v3.6.0", 2327 | "source": { 2328 | "type": "git", 2329 | "url": "https://github.com/symfony/service-contracts.git", 2330 | "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4" 2331 | }, 2332 | "dist": { 2333 | "type": "zip", 2334 | "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f021b05a130d35510bd6b25fe9053c2a8a15d5d4", 2335 | "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4", 2336 | "shasum": "" 2337 | }, 2338 | "require": { 2339 | "php": ">=8.1", 2340 | "psr/container": "^1.1|^2.0", 2341 | "symfony/deprecation-contracts": "^2.5|^3" 2342 | }, 2343 | "conflict": { 2344 | "ext-psr": "<1.1|>=2" 2345 | }, 2346 | "type": "library", 2347 | "extra": { 2348 | "thanks": { 2349 | "url": "https://github.com/symfony/contracts", 2350 | "name": "symfony/contracts" 2351 | }, 2352 | "branch-alias": { 2353 | "dev-main": "3.6-dev" 2354 | } 2355 | }, 2356 | "autoload": { 2357 | "psr-4": { 2358 | "Symfony\\Contracts\\Service\\": "" 2359 | }, 2360 | "exclude-from-classmap": [ 2361 | "/Test/" 2362 | ] 2363 | }, 2364 | "notification-url": "https://packagist.org/downloads/", 2365 | "license": [ 2366 | "MIT" 2367 | ], 2368 | "authors": [ 2369 | { 2370 | "name": "Nicolas Grekas", 2371 | "email": "p@tchwork.com" 2372 | }, 2373 | { 2374 | "name": "Symfony Community", 2375 | "homepage": "https://symfony.com/contributors" 2376 | } 2377 | ], 2378 | "description": "Generic abstractions related to writing services", 2379 | "homepage": "https://symfony.com", 2380 | "keywords": [ 2381 | "abstractions", 2382 | "contracts", 2383 | "decoupling", 2384 | "interfaces", 2385 | "interoperability", 2386 | "standards" 2387 | ], 2388 | "support": { 2389 | "source": "https://github.com/symfony/service-contracts/tree/v3.6.0" 2390 | }, 2391 | "funding": [ 2392 | { 2393 | "url": "https://symfony.com/sponsor", 2394 | "type": "custom" 2395 | }, 2396 | { 2397 | "url": "https://github.com/fabpot", 2398 | "type": "github" 2399 | }, 2400 | { 2401 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 2402 | "type": "tidelift" 2403 | } 2404 | ], 2405 | "time": "2025-04-25T09:37:31+00:00" 2406 | }, 2407 | { 2408 | "name": "symfony/stopwatch", 2409 | "version": "v7.3.0", 2410 | "source": { 2411 | "type": "git", 2412 | "url": "https://github.com/symfony/stopwatch.git", 2413 | "reference": "5a49289e2b308214c8b9c2fda4ea454d8b8ad7cd" 2414 | }, 2415 | "dist": { 2416 | "type": "zip", 2417 | "url": "https://api.github.com/repos/symfony/stopwatch/zipball/5a49289e2b308214c8b9c2fda4ea454d8b8ad7cd", 2418 | "reference": "5a49289e2b308214c8b9c2fda4ea454d8b8ad7cd", 2419 | "shasum": "" 2420 | }, 2421 | "require": { 2422 | "php": ">=8.2", 2423 | "symfony/service-contracts": "^2.5|^3" 2424 | }, 2425 | "type": "library", 2426 | "autoload": { 2427 | "psr-4": { 2428 | "Symfony\\Component\\Stopwatch\\": "" 2429 | }, 2430 | "exclude-from-classmap": [ 2431 | "/Tests/" 2432 | ] 2433 | }, 2434 | "notification-url": "https://packagist.org/downloads/", 2435 | "license": [ 2436 | "MIT" 2437 | ], 2438 | "authors": [ 2439 | { 2440 | "name": "Fabien Potencier", 2441 | "email": "fabien@symfony.com" 2442 | }, 2443 | { 2444 | "name": "Symfony Community", 2445 | "homepage": "https://symfony.com/contributors" 2446 | } 2447 | ], 2448 | "description": "Provides a way to profile code", 2449 | "homepage": "https://symfony.com", 2450 | "support": { 2451 | "source": "https://github.com/symfony/stopwatch/tree/v7.3.0" 2452 | }, 2453 | "funding": [ 2454 | { 2455 | "url": "https://symfony.com/sponsor", 2456 | "type": "custom" 2457 | }, 2458 | { 2459 | "url": "https://github.com/fabpot", 2460 | "type": "github" 2461 | }, 2462 | { 2463 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 2464 | "type": "tidelift" 2465 | } 2466 | ], 2467 | "time": "2025-02-24T10:49:57+00:00" 2468 | }, 2469 | { 2470 | "name": "symfony/string", 2471 | "version": "v7.3.2", 2472 | "source": { 2473 | "type": "git", 2474 | "url": "https://github.com/symfony/string.git", 2475 | "reference": "42f505aff654e62ac7ac2ce21033818297ca89ca" 2476 | }, 2477 | "dist": { 2478 | "type": "zip", 2479 | "url": "https://api.github.com/repos/symfony/string/zipball/42f505aff654e62ac7ac2ce21033818297ca89ca", 2480 | "reference": "42f505aff654e62ac7ac2ce21033818297ca89ca", 2481 | "shasum": "" 2482 | }, 2483 | "require": { 2484 | "php": ">=8.2", 2485 | "symfony/polyfill-ctype": "~1.8", 2486 | "symfony/polyfill-intl-grapheme": "~1.0", 2487 | "symfony/polyfill-intl-normalizer": "~1.0", 2488 | "symfony/polyfill-mbstring": "~1.0" 2489 | }, 2490 | "conflict": { 2491 | "symfony/translation-contracts": "<2.5" 2492 | }, 2493 | "require-dev": { 2494 | "symfony/emoji": "^7.1", 2495 | "symfony/error-handler": "^6.4|^7.0", 2496 | "symfony/http-client": "^6.4|^7.0", 2497 | "symfony/intl": "^6.4|^7.0", 2498 | "symfony/translation-contracts": "^2.5|^3.0", 2499 | "symfony/var-exporter": "^6.4|^7.0" 2500 | }, 2501 | "type": "library", 2502 | "autoload": { 2503 | "files": [ 2504 | "Resources/functions.php" 2505 | ], 2506 | "psr-4": { 2507 | "Symfony\\Component\\String\\": "" 2508 | }, 2509 | "exclude-from-classmap": [ 2510 | "/Tests/" 2511 | ] 2512 | }, 2513 | "notification-url": "https://packagist.org/downloads/", 2514 | "license": [ 2515 | "MIT" 2516 | ], 2517 | "authors": [ 2518 | { 2519 | "name": "Nicolas Grekas", 2520 | "email": "p@tchwork.com" 2521 | }, 2522 | { 2523 | "name": "Symfony Community", 2524 | "homepage": "https://symfony.com/contributors" 2525 | } 2526 | ], 2527 | "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", 2528 | "homepage": "https://symfony.com", 2529 | "keywords": [ 2530 | "grapheme", 2531 | "i18n", 2532 | "string", 2533 | "unicode", 2534 | "utf-8", 2535 | "utf8" 2536 | ], 2537 | "support": { 2538 | "source": "https://github.com/symfony/string/tree/v7.3.2" 2539 | }, 2540 | "funding": [ 2541 | { 2542 | "url": "https://symfony.com/sponsor", 2543 | "type": "custom" 2544 | }, 2545 | { 2546 | "url": "https://github.com/fabpot", 2547 | "type": "github" 2548 | }, 2549 | { 2550 | "url": "https://github.com/nicolas-grekas", 2551 | "type": "github" 2552 | }, 2553 | { 2554 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 2555 | "type": "tidelift" 2556 | } 2557 | ], 2558 | "time": "2025-07-10T08:47:49+00:00" 2559 | } 2560 | ], 2561 | "packages-dev": [], 2562 | "aliases": [], 2563 | "minimum-stability": "stable", 2564 | "stability-flags": {}, 2565 | "prefer-stable": false, 2566 | "prefer-lowest": false, 2567 | "platform": {}, 2568 | "platform-dev": {}, 2569 | "plugin-api-version": "2.6.0" 2570 | } 2571 | --------------------------------------------------------------------------------