diff --git a/.github/workflows/lint-php-cs.yml b/.github/workflows/lint-php-cs.yml new file mode 100644 index 0000000..5250d3a --- /dev/null +++ b/.github/workflows/lint-php-cs.yml @@ -0,0 +1,46 @@ +# This workflow is provided via the organization template repository +# +# https://github.com/nextcloud/.github +# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization +# +# SPDX-FileCopyrightText: 2021-2024 Nextcloud GmbH and Nextcloud contributors +# SPDX-License-Identifier: MIT + +name: Lint php-cs + +on: pull_request + +permissions: + contents: read + +concurrency: + group: lint-php-cs-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + lint: + runs-on: ubuntu-latest + + name: php-cs + + steps: + - name: Checkout + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + + - name: Set up php8.3 + uses: shivammathur/setup-php@c541c155eee45413f5b09a52248675b1a2575231 # v2.31.1 + with: + php-version: 8.3 + extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite + coverage: none + ini-file: development + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Install dependencies + working-directory: translations/translationtool/ + run: composer i + + - name: Lint + working-directory: translations/translationtool/ + run: composer run cs:check || ( echo 'Please run `composer run cs:fix` to format your code' && exit 1 ) diff --git a/translations/handleAppsTranslations.sh b/translations/handleAppsTranslations.sh index 383930a..663af9e 100755 --- a/translations/handleAppsTranslations.sh +++ b/translations/handleAppsTranslations.sh @@ -52,7 +52,7 @@ do fi fi - # prepate git commit + # prepare git commit git add l10n/*.js l10n/*.json || true cd .. diff --git a/translations/translationtool/.gitignore b/translations/translationtool/.gitignore index 8f483a6..c8b802d 100644 --- a/translations/translationtool/.gitignore +++ b/translations/translationtool/.gitignore @@ -1,3 +1,4 @@ vendor composer.phar .vscode +.php-cs-fixer.cache diff --git a/translations/translationtool/.php-cs-fixer.dist.php b/translations/translationtool/.php-cs-fixer.dist.php new file mode 100644 index 0000000..25ef127 --- /dev/null +++ b/translations/translationtool/.php-cs-fixer.dist.php @@ -0,0 +1,22 @@ +setParallelConfig(ParallelConfigFactory::detect()) + ->getFinder() + ->ignoreVCSIgnored(true) + ->notPath('vendor') + ->notPath('vendor-bin') + ->in(__DIR__ . '/src'); +return $config; diff --git a/translations/translationtool/composer.json b/translations/translationtool/composer.json index cf11861..cca1d85 100644 --- a/translations/translationtool/composer.json +++ b/translations/translationtool/composer.json @@ -3,11 +3,29 @@ "description": "Needed for the translation tool", "license": "MIT", "config": { + "allow-plugins": { + "bamarni/composer-bin-plugin": true + }, "vendor-dir": "vendor", "optimize-autoloader": true, - "classmap-authoritative": true + "classmap-authoritative": true, + "platform": { + "php": "8.1" + }, + "sort-packages": true }, "require": { + "bamarni/composer-bin-plugin": "^1.8.2", "clue/phar-composer": "^1.3" + }, + "scripts": { + "cs:check": "php-cs-fixer fix --dry-run --diff", + "cs:fix": "php-cs-fixer fix", + "post-install-cmd": [ + "@composer bin all install --ansi" + ], + "post-update-cmd": [ + "@composer bin all update --ansi" + ] } } diff --git a/translations/translationtool/composer.lock b/translations/translationtool/composer.lock index d2ca039..0e80c10 100644 --- a/translations/translationtool/composer.lock +++ b/translations/translationtool/composer.lock @@ -4,8 +4,65 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "45e8df69bfa7ba4a208d93b75010f360", + "content-hash": "4be7ea479078b9931630769ae40d00a6", "packages": [ + { + "name": "bamarni/composer-bin-plugin", + "version": "1.8.2", + "source": { + "type": "git", + "url": "https://github.com/bamarni/composer-bin-plugin.git", + "reference": "92fd7b1e6e9cdae19b0d57369d8ad31a37b6a880" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/bamarni/composer-bin-plugin/zipball/92fd7b1e6e9cdae19b0d57369d8ad31a37b6a880", + "reference": "92fd7b1e6e9cdae19b0d57369d8ad31a37b6a880", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^2.0", + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "composer/composer": "^2.0", + "ext-json": "*", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^8.5 || ^9.5", + "symfony/console": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0", + "symfony/finder": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0", + "symfony/process": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0" + }, + "type": "composer-plugin", + "extra": { + "class": "Bamarni\\Composer\\Bin\\BamarniBinPlugin" + }, + "autoload": { + "psr-4": { + "Bamarni\\Composer\\Bin\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "No conflicts for your bin dependencies", + "keywords": [ + "composer", + "conflict", + "dependency", + "executable", + "isolation", + "tool" + ], + "support": { + "issues": "https://github.com/bamarni/composer-bin-plugin/issues", + "source": "https://github.com/bamarni/composer-bin-plugin/tree/1.8.2" + }, + "time": "2022-10-31T08:38:03+00:00" + }, { "name": "clue/phar-composer", "version": "v1.3.0", @@ -1586,5 +1643,8 @@ "prefer-lowest": false, "platform": [], "platform-dev": [], - "plugin-api-version": "2.1.0" + "platform-overrides": { + "php": "8.1" + }, + "plugin-api-version": "2.6.0" } diff --git a/translations/translationtool/src/translationtool.php b/translations/translationtool/src/translationtool.php index 398448e..d6a74bb 100644 --- a/translations/translationtool/src/translationtool.php +++ b/translations/translationtool/src/translationtool.php @@ -1,43 +1,29 @@ - * - * @author Jakob Sack - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2017 Jakob Sack + * SPDX-License-Identifier: AGPL-3.0-or-later */ require __DIR__ . '/vendor/autoload.php'; class TranslatableApp { - private $appPath; - private $name; - private $fakeAppInfoFile; - private $fakeVueFile; - private $fakeLocaleFile; - private $ignoreFiles; - private $translationsPath; - private $tool; - - public function __construct($appPath, $translationsPath, $tool) { + private string $appPath; + private string $name = ''; + private string $fakeAppInfoFile; + private string $fakeVueFile; + private string $fakeLocaleFile; + private array $ignoreFiles = []; + private string $translationsPath; + private TranslationTool $tool; + + public function __construct(string $appPath, string $translationsPath, TranslationTool $tool) { $this->appPath = $appPath; $this->translationsPath = $translationsPath; $this->tool = $tool; - $this->ignoreFiles = []; $this->fakeAppInfoFile = $this->appPath . '/specialAppInfoFakeDummyForL10nScript.php'; $this->fakeVueFile = $this->appPath . '/specialVueFakeDummyForL10nScript.js'; $this->fakeLocaleFile = $this->appPath . '/specialLocaleFakeDummyForL10nScript.php'; @@ -61,7 +47,7 @@ public function __construct($appPath, $translationsPath, $tool) { print_r($this->ignoreFiles); } - public function createOrCheckPotFile(bool $checkFiles = false) { + public function createOrCheckPotFile(bool $checkFiles = false): void { $pathToPotFile = $this->translationsPath . '/templates/' . $this->name . '.pot'; // Gather required data @@ -102,10 +88,10 @@ public function createOrCheckPotFile(bool $checkFiles = false) { $extractAll = '--extract-all'; // modify output - $tmpfname = tempnam(sys_get_temp_dir(), 'checkpot'); + $tmpfname = tempnam(sys_get_temp_dir(), 'checkpot'); $output = '--output=' . $tmpfname; // extract-all generates a recurrent warning - $skipErrors = "2>/dev/null"; + $skipErrors = '2>/dev/null'; } $xgetCmd = 'xgettext ' . $output . ' ' . $joinexisting . ' ' . $keywords . ' ' . $language . ' ' . escapeshellarg($entry) . ' ' . $additionalArguments . ' ' . $extractAll . ' ' . $skipErrors; @@ -125,14 +111,14 @@ public function createOrCheckPotFile(bool $checkFiles = false) { $this->deleteFakeFileForLocale(); } - private function checkMissingTranslations(string $entry, string $tmpfname) { + private function checkMissingTranslations(string $entry, string $tmpfname): void { $translations = Gettext\Translations::fromPoFile($tmpfname); - $first=true; + $first = true; foreach($translations as $translation) { if (preg_match_all('/(^|[^a-zA-Z_]+)(t\([^\)]*\))/', $translation->getOriginal(), $matches)) { $suspects = []; foreach($matches[2] as $miss) { - if (preg_match('/["\']'.$this->name.'["\']/', $miss )) { + if (preg_match('/["\']'.$this->name.'["\']/', $miss)) { $suspects[] = $miss; } } @@ -153,7 +139,7 @@ private function checkMissingTranslations(string $entry, string $tmpfname) { } } - public function createNextcloudFiles() { + public function createNextcloudFiles(): void { foreach ($this->findLanguages() as $language) { $poFile = $this->translationsPath . '/' . $language . '/' . $this->name . '.po'; $translations = Gettext\Translations::fromPoFile($poFile); @@ -195,7 +181,7 @@ public function createNextcloudFiles() { } } - private function writeJsFile($language, $plurals, $strings) { + private function writeJsFile(string $language, string $plurals, array $strings): void { $outfile = fopen($this->appPath . '/l10n/' . $language . '.js', 'w'); fwrite($outfile, 'OC.L10N.register(' . PHP_EOL); fwrite($outfile, ' "' . $this->name . '",' . PHP_EOL); @@ -207,7 +193,7 @@ private function writeJsFile($language, $plurals, $strings) { fclose($outfile); } - private function writeJsonFile($language, $plurals, $strings){ + private function writeJsonFile(string $language, string $plurals, array $strings): void { $outfile = fopen($this->appPath . '/l10n/' . $language . '.json', 'w'); fwrite($outfile, '{ "translations": {' . PHP_EOL); fwrite($outfile, ' '); @@ -216,11 +202,11 @@ private function writeJsonFile($language, $plurals, $strings){ fclose($outfile); } - private function escape($string) { + private function escape(string $string): string { return Gettext\Generators\Po::convertString($string); } - private function hasExtension($fileName, $extensions) { + private function hasExtension(string $fileName, string $extensions): bool { foreach ($extensions as $ext) { if (substr($fileName, -strlen($ext)) === $ext) { return true; @@ -229,7 +215,7 @@ private function hasExtension($fileName, $extensions) { return false; } - private function findTranslatableFiles(array $extensions, array $ignoredExtensions = [], $path = '') { + private function findTranslatableFiles(array $extensions, array $ignoredExtensions = [], string $path = ''): array { $realPath = $path === '' ? $this->appPath : $this->appPath . '/' . $path; $translatable = []; @@ -250,7 +236,7 @@ private function findTranslatableFiles(array $extensions, array $ignoredExtensio continue 2; } } - if (is_dir($newRealPath) && $entry != 'l10n' && $entry != 'node_modules') { + if (is_dir($newRealPath) && $entry !== 'l10n' && $entry !== 'node_modules') { $translatable = array_merge($translatable, $this->findTranslatableFiles($extensions, $ignoredExtensions, $newPath)); } if (is_file($newRealPath)) { @@ -264,10 +250,10 @@ private function findTranslatableFiles(array $extensions, array $ignoredExtensio return $translatable; } - private function findLanguages() { + private function findLanguages(): array { $languages = []; - $directoryectoryContent = scandir($this->translationsPath); - foreach ($directoryectoryContent as $entry) { + $directoryContent = scandir($this->translationsPath); + foreach ($directoryContent as $entry) { if ($entry[0] === '.' || $entry === 'templates' || !is_dir($this->translationsPath . '/' . $entry)) { continue; } @@ -288,7 +274,7 @@ private function createFakeFileForAppInfo() { } $strings = []; - $xml = simplexml_load_file($entryName); + $xml = simplexml_load_string(file_get_contents($entryName)); if ($xml->name) { $strings[] = $xml->name->__toString(); @@ -365,13 +351,13 @@ private function createFakeFileForAppInfo() { file_put_contents($this->fakeAppInfoFile, $content); } - private function createFakeFileForVueFiles() { + private function createFakeFileForVueFiles(): void { $fakeFileContent = ''; foreach ($this->findTranslatableFiles(['.vue']) as $vueFile) { $vueSource = file_get_contents($vueFile); if ($vueSource === false) { - echo "Warning: could not read " . $vueFile . PHP_EOL; + echo 'Warning: could not read ' . $vueFile . PHP_EOL; continue; } @@ -397,7 +383,7 @@ private function createFakeFileForVueFiles() { foreach (array_keys($matches2) as $k) { $match2 = $matches2[$k]; $match3 = $matches3[$k]; - $fakeFileContent .= $this->getTranslatorHintWithVueSource($vueFile, $vueSource,$matches0[$k]); + $fakeFileContent .= $this->getTranslatorHintWithVueSource($vueFile, $vueSource, $matches0[$k]); $fakeFileContent .= "n('" . $this->name . "', '" . preg_replace('/\s+/', ' ', $match2) . "', '" . preg_replace('/\s+/', ' ', $match3) . "');" . PHP_EOL; } } @@ -450,19 +436,19 @@ private function getTranslatorHintWithVueSource(string $vueFile, string $content return '// TRANSLATORS ' . $relativeVuePath . ':' . $lineNumber . PHP_EOL; } - private function deleteFakeFileForAppInfo() { - if (is_file($this->fakeAppInfoFile)){ + private function deleteFakeFileForAppInfo(): void { + if (is_file($this->fakeAppInfoFile)) { unlink($this->fakeAppInfoFile); } } - private function deleteFakeFileForVueFiles() { - if (is_file($this->fakeVueFile)){ + private function deleteFakeFileForVueFiles(): void { + if (is_file($this->fakeVueFile)) { unlink($this->fakeVueFile); } } - private function setAppName() { + private function setAppName(): void { $xmlFile = $this->appPath . '/appinfo/info.xml'; $this->name = basename($this->appPath); @@ -471,7 +457,7 @@ private function setAppName() { return; } - $xml = simplexml_load_file($xmlFile); + $xml = simplexml_load_string(file_get_contents($xmlFile)); if ($xml->name) { $this->name = $xml->id->__toString(); @@ -503,19 +489,19 @@ private function createFakeFileForLocale() { file_put_contents($this->fakeLocaleFile, $content); } - private function deleteFakeFileForLocale() { - if (is_file($this->fakeLocaleFile)){ + private function deleteFakeFileForLocale(): void { + if (is_file($this->fakeLocaleFile)) { unlink($this->fakeLocaleFile); } } } class TranslationTool { - private $translationPath; - private $appPaths; - private $verbose = 0; + private string $translationPath; + private array $appPaths; + private int $verbose = 0; - public function __construct(){ + public function __construct() { $this->translationPath = getcwd() . '/translationfiles'; $this->appPaths = []; @@ -524,11 +510,11 @@ public function __construct(){ } } - public function setVerbose(int $verbose) { + public function setVerbose(int $verbose): void { $this->verbose = $verbose; } - public function checkEnvironment() { + public function checkEnvironment(): bool { // Check if the version of xgettext is at least 0.18.3 $output = []; exec('xgettext --version', $output); @@ -536,7 +522,7 @@ public function checkEnvironment() { // we assume the first line looks like this 'xgettext (GNU gettext-tools) 0.19.3' $version = trim(substr($output[0], 29)); - $this->log("xgettext version: ". $version); + $this->log('xgettext version: '. $version); if (version_compare($version, '0.18.3', '<')) { echo 'Minimum expected version of xgettext is 0.18.3. Detected: ' . $version . '".' . PHP_EOL; @@ -552,7 +538,7 @@ public function checkEnvironment() { return true; } - public function createPotFiles() { + public function createPotFiles(): void { // Recreate folder for the templates $this->rrmdir($this->translationPath . '/templates'); mkdir($this->translationPath . '/templates'); @@ -565,7 +551,7 @@ public function createPotFiles() { } } - public function convertPoFiles() { + public function convertPoFiles(): void { foreach ($this->appPaths as $appPath) { $this->log('Application path: ' . $appPath); $app = new TranslatableApp($appPath, $this->translationPath, $this); @@ -573,8 +559,7 @@ public function convertPoFiles() { } } - public function checkFiles() { - + public function checkFiles(): void { // iterate over all apps foreach ($this->appPaths as $appPath) { $this->log('Application path: ' . $appPath); @@ -583,7 +568,7 @@ public function checkFiles() { } } - private function findApps($path){ + private function findApps(string $path): void { $directoryectoryContent = scandir($path); foreach ($directoryectoryContent as $entry) { if ($entry[0] === '.') { @@ -607,7 +592,7 @@ private function findApps($path){ } } - private function rrmdir($path) { + private function rrmdir(string $path): void { if (!is_dir($path)) { return; } @@ -629,11 +614,11 @@ private function rrmdir($path) { rmdir($path); } - public function log(string $message) { - if ($this->verbose == 0) { + public function log(string $message): void { + if ($this->verbose === 0) { return; } - echo " > " . $message . PHP_EOL; + echo ' > ' . $message . PHP_EOL; } } @@ -641,13 +626,14 @@ public function log(string $message) { $task = ''; $usage = false; $verbose = 0; -$returnValue = true; +$returnValue = 0; +$toolName = 'translationtool'; $index = 0; foreach ($argv as $arg) { $index++; - if ($index == 1) { - $TOOLNAME = $arg; + if ($index === 1) { + $toolName = $arg; continue; } switch($arg) { @@ -665,35 +651,35 @@ public function log(string $message) { $task = $arg; break; default: - echo "Unknown command parameter : " . $arg . PHP_EOL; + echo 'Unknown command parameter : ' . $arg . PHP_EOL; $usage = true; - $returnValue = false; + $returnValue = 1; break; - } + } } // read the command line arguments if(empty($task) && !$usage) { echo 'Missing arguments' . PHP_EOL; $usage = true; - $returnValue = false; + $returnValue = 1; } if ($usage) { echo 'Usage:' . PHP_EOL; - echo ' ' . $TOOLNAME . ' []' . PHP_EOL; + echo ' ' . $toolName . ' []' . PHP_EOL; echo 'Arguments:' . PHP_EOL; echo ' task: One of: create-pot-files, convert-po-files, check-files' . PHP_EOL; - echo "Options:". PHP_EOL; - echo " -v, --verbose Verbose mode". PHP_EOL; - echo " -h, --help Display command usage". PHP_EOL; - return $returnValue; + echo 'Options:'. PHP_EOL; + echo ' -v, --verbose Verbose mode'. PHP_EOL; + echo ' -h, --help Display command usage'. PHP_EOL; + exit($returnValue); } $tool = new TranslationTool(); $tool->setVerbose($verbose); if (!$tool->checkEnvironment()) { - return false; + exit(1); } if ($task === 'create-pot-files') { @@ -704,5 +690,5 @@ public function log(string $message) { $tool->checkFiles(); } else { echo 'Unknown task: "' . $task . '".' . PHP_EOL; - return false; + exit(1); } diff --git a/translations/translationtool/translationtool.phar b/translations/translationtool/translationtool.phar index 2016156..8fd3500 100755 Binary files a/translations/translationtool/translationtool.phar and b/translations/translationtool/translationtool.phar differ diff --git a/translations/translationtool/vendor-bin/csfixer/composer.json b/translations/translationtool/vendor-bin/csfixer/composer.json new file mode 100644 index 0000000..8e62d7a --- /dev/null +++ b/translations/translationtool/vendor-bin/csfixer/composer.json @@ -0,0 +1,12 @@ +{ + "config": { + "platform": { + "php": "8.1" + }, + "sort-packages": true + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.16", + "nextcloud/coding-standard": "^1.2.3" + } +} diff --git a/translations/translationtool/vendor-bin/csfixer/composer.lock b/translations/translationtool/vendor-bin/csfixer/composer.lock new file mode 100644 index 0000000..c10465b --- /dev/null +++ b/translations/translationtool/vendor-bin/csfixer/composer.lock @@ -0,0 +1,115 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "dd2ad861beeddc2e9110a24dc5518fed", + "packages": [], + "packages-dev": [ + { + "name": "nextcloud/coding-standard", + "version": "v1.2.3", + "source": { + "type": "git", + "url": "https://github.com/nextcloud/coding-standard.git", + "reference": "bc9c53a5306114b60c4363057aff9c2ed10a54da" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nextcloud/coding-standard/zipball/bc9c53a5306114b60c4363057aff9c2ed10a54da", + "reference": "bc9c53a5306114b60c4363057aff9c2ed10a54da", + "shasum": "" + }, + "require": { + "php": "^7.3|^8.0", + "php-cs-fixer/shim": "^3.17" + }, + "type": "library", + "autoload": { + "psr-4": { + "Nextcloud\\CodingStandard\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christoph Wurst", + "email": "christoph@winzerhof-wurst.at" + } + ], + "description": "Nextcloud coding standards for the php cs fixer", + "support": { + "issues": "https://github.com/nextcloud/coding-standard/issues", + "source": "https://github.com/nextcloud/coding-standard/tree/v1.2.3" + }, + "time": "2024-08-23T14:32:32+00:00" + }, + { + "name": "php-cs-fixer/shim", + "version": "v3.59.3", + "source": { + "type": "git", + "url": "https://github.com/PHP-CS-Fixer/shim.git", + "reference": "c855876e64de3431bc9279f27af7be40d11d7613" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHP-CS-Fixer/shim/zipball/c855876e64de3431bc9279f27af7be40d11d7613", + "reference": "c855876e64de3431bc9279f27af7be40d11d7613", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0" + }, + "replace": { + "friendsofphp/php-cs-fixer": "self.version" + }, + "suggest": { + "ext-dom": "For handling output formats in XML", + "ext-mbstring": "For handling non-UTF8 characters." + }, + "bin": [ + "php-cs-fixer", + "php-cs-fixer.phar" + ], + "type": "application", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Dariusz RumiƄski", + "email": "dariusz.ruminski@gmail.com" + } + ], + "description": "A tool to automatically fix PHP code style", + "support": { + "issues": "https://github.com/PHP-CS-Fixer/shim/issues", + "source": "https://github.com/PHP-CS-Fixer/shim/tree/v3.59.3" + }, + "time": "2024-06-16T14:17:34+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [], + "platform-overrides": { + "php": "8.1" + }, + "plugin-api-version": "2.6.0" +}