diff --git a/.github/actions/phar/action.yaml b/.github/actions/phar/action.yaml index e969d017..d4543ea0 100644 --- a/.github/actions/phar/action.yaml +++ b/.github/actions/phar/action.yaml @@ -18,19 +18,19 @@ runs: shell: bash working-directory: tools/phar - - name: Compile phar Linux + - name: Build Castor phar for Linux run: bin/castor castor:phar:linux shell: bash - - name: Compile phar Darwin + - name: Build Castor phar for Darwin run: bin/castor castor:phar:darwin shell: bash - - name: Compile phar Windows + - name: Build Castor phar for Windows run: bin/castor castor:phar:windows shell: bash - - name: Ensure PHAR is OK + - name: Ensure phar is OK run: build/castor.linux-amd64.phar --version shell: bash working-directory: tools/phar diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f08d7111..ff899b62 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,16 +35,33 @@ jobs: REQUIRE_DEV: true phpunit: - name: "PHPUnit on ${{ matrix.php }} ${{ matrix.phar && '(with phar)' || '' }}" + name: "PHPUnit on ${{ matrix.php }} | Castor from ${{ matrix.castor.method }}" runs-on: ubuntu-latest strategy: fail-fast: false matrix: - php: [ "8.1", "8.2", "8.3" ] - phar: [ false ] include: - php: "8.1" - phar: true + castor: + bin: 'bin/castor' + method: 'bin/castor' + - php: "8.1" + castor: + bin: 'tools/phar/build/castor.linux-amd64.phar' + method: 'phar' + - php: "8.1" + castor: + bin: 'castor' + method: 'binary' + - php: "8.2" + castor: + bin: 'bin/castor' + method: 'bin/castor' + - php: "8.3" + castor: + bin: 'bin/castor' + method: 'bin/castor' + steps: - name: Checkout uses: actions/checkout@v4 @@ -62,10 +79,22 @@ jobs: run: composer install --prefer-dist --no-progress --optimize-autoloader --classmap-authoritative working-directory: tools/phar - - name: Compile phar Linux + - name: Build Castor phar for Linux run: bin/castor castor:phar:linux shell: bash - if: matrix.phar + if: matrix.castor.method == 'phar' || matrix.castor.method == 'binary' + + - name: Cache PHP static building artifacts + uses: actions/cache@v4 + with: + path: | + /home/runner/.cache/castor/castor-php-static-compiler + key: php-static-building-artifacts-cache + + - name: Compile Custom Built PHP along Castor phar for Linux + run: bin/castor compile tools/phar/build/castor.linux-amd64.phar --php-extensions=mbstring,phar,posix,tokenizer,pcntl + shell: bash + if: matrix.castor.method == 'binary' - name: Link box run: sudo ln -s $GITHUB_WORKSPACE/tools/phar/vendor/bin/box /usr/local/bin/box @@ -76,10 +105,10 @@ jobs: - name: Run tests run: vendor/bin/simple-phpunit env: - CASTOR_BIN: ${{ github.workspace }}/${{ matrix.phar && 'tools/phar/build/castor.linux-amd64.phar' || 'bin/castor'}} + CASTOR_BIN: ${{ github.workspace }}/${{ matrix.castor.bin }} phar: - name: Ensure PHAR is OK + name: Ensure phar is OK runs-on: ubuntu-latest steps: diff --git a/CHANGELOG.md b/CHANGELOG.md index 679f79b4..f837c088 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * Set the process title according to the current application name and task name * Ignore some low level env vars in runnable command showed in logs * Fix section output to work on Windows +* Add a `compile` command that puts together a customizable PHP binary with a repacked castor app into one executable file ## 0.12.1 (2024-02-06) diff --git a/bin/generate-tests.php b/bin/generate-tests.php index 9e655895..45a3d947 100755 --- a/bin/generate-tests.php +++ b/bin/generate-tests.php @@ -63,6 +63,7 @@ 'log:with-context', 'parallel:sleep', 'repack', + 'compile', 'run:ls', 'run:run-parallel', ]; diff --git a/doc/going-further/compile.md b/doc/going-further/compile.md new file mode 100644 index 00000000..4e7583e9 --- /dev/null +++ b/doc/going-further/compile.md @@ -0,0 +1,57 @@ +# Compiling your application into a standalone binary + +[Packing your Castor application as a phar](repack.md) can be a good way to easily +share and use it in various environments. + +However, you need to ensure that PHP is installed and configured correctly in all +the environments where you want to use your Castor app. +This can be a hassle, especially if you don't have control over the environments. + +To make things simpler, Castor's `compile` command can help by creating a +customizable PHP binary with a phar, making one executable file that can be used in any setting. + +Just pass your repacked Castor app phar as an argument of this command. + +## Pre-requisites + +Follow the [`repack` documentation](repack.md) to produce a phar of your +Castor app. + +## Running the Compile Command + +To compile your Castor application, navigate to your project directory and run: + +```bash +vendor/bin/castor compile my-custom-castor-app.phar +``` + +> [!WARNING] +> Compiling is not supported yet on Windows. + +### Options + +Make sure to take a look at the command description to see all the available options: +```bash +vendor/bin/castor compile --help +``` + +### Behavior + +The `compile` command performs several steps: + +1. Downloads or uses an existing [Static PHP CLI tool](https://github.com/crazywhalecc/static-php-cli) to compile PHP and the phar into a binary. +2. If required, it automatically installs dependencies and compiles PHP with the specified extensions. +3. Combines the compiled PHP and your phar file into a single executable. + +## Post-Compilation + +Once the compilation is finished, your Castor application is transformed into a standalone binary named `castor` by default (you can use the `--output` option to change it). + +This binary is now ready to be distributed and run in environments that do not have PHP installed. + +You can simply run it like any other executable: + +```bash +./castor +``` + diff --git a/doc/going-further/index.md b/doc/going-further/index.md index 092a864d..ddfa2687 100644 --- a/doc/going-further/index.md +++ b/doc/going-further/index.md @@ -25,6 +25,7 @@ * [Listening to events](events.md) * [Repacking your application in a new phar](repack.md) +* [Compiling your application in a standalone binary](compile.md) ## Examples diff --git a/doc/going-further/repack.md b/doc/going-further/repack.md index 79648ff2..6ed8b2a6 100644 --- a/doc/going-further/repack.md +++ b/doc/going-further/repack.md @@ -3,6 +3,8 @@ You have created a Castor application, with many tasks, and you want to distribute it as a single phar file? Castor can help you with that. +## Pre-requisites + In your project, install Castor as a dependency: ```bash @@ -18,6 +20,8 @@ configuration. See the [PHP documentation](https://www.php.net/manual/en/phar.configuration.php#ini.phar.readonly) to disabled `phar.readonly`. +## Running the Repack Command + Then, run the repack command to create the new phar: ``` @@ -34,3 +38,9 @@ vendor/bin/castor repack --help > Castor will automatically import all files in the current directly. > So ensure to have the less files possible in the directory where you run the > repack task to avoid including useless files in the phar. + +## Going further + +Packaging your Castor app as a phar simplifies distribution but requires PHP setup on target systems. + +[Castor's `compile` command](compile.md) streamlines this by embedding the phar in a PHP binary, creating a standalone executable for diverse environments. diff --git a/examples/failure.php b/examples/failure.php index f238fda4..4073c714 100644 --- a/examples/failure.php +++ b/examples/failure.php @@ -9,7 +9,7 @@ #[AsTask(description: 'A failing task not authorized to fail')] function failure(): void { - run('i_do_not_exist', path: '/tmp'); + run('i_do_not_exist', path: '/tmp', pty: false); } #[AsTask(description: 'A failing task authorized to fail')] diff --git a/src/Console/ApplicationFactory.php b/src/Console/ApplicationFactory.php index ffb242c4..ce258f9a 100644 --- a/src/Console/ApplicationFactory.php +++ b/src/Console/ApplicationFactory.php @@ -2,6 +2,7 @@ namespace Castor\Console; +use Castor\Console\Command\CompileCommand; use Castor\Console\Command\DebugCommand; use Castor\Console\Command\RepackCommand; use Castor\ContextRegistry; @@ -45,6 +46,7 @@ public static function create(): SymfonyApplication $cacheDir = PlatformUtil::getCacheDirectory(); $cache = new FilesystemAdapter(directory: $cacheDir); $logger = new Logger('castor', [], [new ProcessProcessor()]); + $fs = new Filesystem(); /** @var SymfonyApplication */ // @phpstan-ignore-next-line @@ -56,7 +58,7 @@ public static function create(): SymfonyApplication new ExpressionLanguage($contextRegistry), new StubsGenerator($logger), $logger, - new Filesystem(), + $fs, $httpClient, $cache, new WaitForHelper($httpClient, $logger), @@ -67,6 +69,7 @@ public static function create(): SymfonyApplication if (!class_exists(\RepackedApplication::class)) { $application->add(new RepackCommand()); + $application->add(new CompileCommand($httpClient, $fs)); } return $application; diff --git a/src/Console/Command/CompileCommand.php b/src/Console/Command/CompileCommand.php new file mode 100644 index 00000000..37832f8f --- /dev/null +++ b/src/Console/Command/CompileCommand.php @@ -0,0 +1,222 @@ +setName('compile') + ->addArgument('phar-path', InputArgument::REQUIRED, 'Path to phar to compile along PHP') + ->addOption('output', null, InputOption::VALUE_REQUIRED, 'Compiled standalone binary output filepath', PathHelper::getRoot() . '/castor') + ->addOption('os', null, InputOption::VALUE_REQUIRED, 'Target OS for PHP compilation', 'linux', ['linux', 'macos']) + ->addOption('arch', null, InputOption::VALUE_REQUIRED, 'Target architecture for PHP compilation', 'x86_64', ['x86_64', 'aarch64']) + ->addOption('php-version', null, InputOption::VALUE_REQUIRED, 'PHP version in major.minor format', '8.3') + ->addOption('php-extensions', null, InputOption::VALUE_REQUIRED, 'PHP extensions required, in a comma-separated format. Defaults are the minimum required to run a basic "Hello World" task in Castor.', 'mbstring,phar,posix,tokenizer') + ->addOption('php-rebuild', null, InputOption::VALUE_NONE, 'Ignore cache and force PHP build compilation.') + ->setHidden(true) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $os = $input->getOption('os'); + if (!\in_array($os, ['linux', 'macos'])) { + throw new \InvalidArgumentException('Currently supported target OS are one of "linux" or "macos"'); + } + + $arch = $input->getOption('arch'); + if (!\in_array($arch, ['x86_64', 'aarch64'])) { + throw new \InvalidArgumentException('Target architecture must be one of "x86_64" or "aarch64"'); + } + + $io = new SymfonyStyle($input, $output); + $io->section('Compiling PHP and your Castor app phar into a standalone binary'); + + $phpBuildCacheKey = $this->generatePHPBuildCacheKey($input); + + $spcBinaryPath = PlatformUtil::getCacheDirectory() . '/castor-php-static-compiler/' . $phpBuildCacheKey . '/spc'; + $spcBinaryDir = \dirname($spcBinaryPath); + + $this->setupSPC( + $spcBinaryDir, + $spcBinaryPath, + $io, + $os, + $arch, + $output + ); + + if (!$this->fs->exists($spcBinaryDir . '/buildroot/bin/micro.sfx') || $input->getOption('php-rebuild')) { + $this->installPHPBuildTools($spcBinaryPath, $spcBinaryDir, $io); + + $phpExtensions = $input->getOption('php-extensions'); + + $this->downloadPHPSourceDeps( + $spcBinaryPath, + $phpExtensions, + $input->getOption('php-version'), + $spcBinaryDir, + $io + ); + + $this->buildPHP( + $spcBinaryPath, + $phpExtensions, + $arch, + $spcBinaryDir, + $io + ); + } + + $this->mergePHPandPHARIntoSingleExecutable( + $spcBinaryPath, + $input->getArgument('phar-path'), + $input->getOption('output'), + $spcBinaryDir, + $io + ); + + return Command::SUCCESS; + } + + private function downloadSPC(string $spcSourceUrl, string $spcBinaryDestination, SymfonyStyle $io): void + { + $response = $this->httpClient->request('GET', $spcSourceUrl); + $contentLength = $response->getHeaders()['content-length'][0] ?? 0; + + $outputStream = fopen($spcBinaryDestination, 'w'); + $progressBar = $io->createProgressBar((int) $contentLength); + + if (false === $outputStream) { + throw new \RuntimeException(sprintf('Failed to open file "%s" for writing.', $spcBinaryDestination)); + } + + foreach ($this->httpClient->stream($response) as $chunk) { + fwrite($outputStream, $chunk->getContent()); + $progressBar->advance(\strlen($chunk->getContent())); + } + + fclose($outputStream); + chmod($spcBinaryDestination, 0o755); + + $progressBar->finish(); + } + + private function installPHPBuildTools(string $spcBinaryPath, string $spcBinaryDir, SymfonyStyle $io): void + { + $installSPCDepsProcess = new Process( + command: [$spcBinaryPath, 'doctor', '--auto-fix'], + cwd: $spcBinaryDir, + timeout: null, + ); + $io->text('Running command: ' . $installSPCDepsProcess->getCommandLine()); + $installSPCDepsProcess->mustRun(fn ($type, $buffer) => print $buffer); + } + + private function downloadPHPSourceDeps(string $spcBinaryPath, mixed $phpExtensions, mixed $phpVersion, string $spcBinaryDir, SymfonyStyle $io): void + { + $downloadProcess = new Process( + command: [ + $spcBinaryPath, 'download', + '--for-extensions=' . $phpExtensions, + '--with-php=' . $phpVersion, + ], + cwd: $spcBinaryDir, + timeout: null, + ); + $io->text('Running command: ' . $downloadProcess->getCommandLine()); + $downloadProcess->mustRun(fn ($type, $buffer) => print $buffer); + } + + private function buildPHP(string $spcBinaryPath, mixed $phpExtensions, mixed $arch, string $spcBinaryDir, SymfonyStyle $io): void + { + $buildProcess = new Process( + command: [ + $spcBinaryPath, 'build', $phpExtensions, + '--build-micro', + '--with-micro-fake-cli', + '--arch=' . $arch, + ], + cwd: $spcBinaryDir, + timeout: null, + ); + $io->text('Running command: ' . $buildProcess->getCommandLine()); + $buildProcess->mustRun(fn ($type, $buffer) => print $buffer); + } + + private function mergePHPandPHARIntoSingleExecutable(string $spcBinaryPath, string $pharFilePath, string $appBinaryFilePath, string $spcBinaryDir, SymfonyStyle $io): void + { + if (!$this->fs->isAbsolutePath($pharFilePath)) { + $pharFilePath = PathHelper::getRoot() . '/' . $pharFilePath; + } + + $mergePHPandPHARProcess = new Process( + [ + $spcBinaryPath, + 'micro:combine', $pharFilePath, + '--output=' . $appBinaryFilePath, + ], + cwd: $spcBinaryDir, + timeout: null, + ); + + $io->text('Running command: ' . $mergePHPandPHARProcess->getCommandLine()); + $mergePHPandPHARProcess->mustRun(fn ($type, $buffer) => print $buffer); + } + + private function setupSPC(string $spcBinaryDir, string $spcBinaryPath, SymfonyStyle $io, mixed $os, mixed $arch, OutputInterface $output): void + { + $this->fs->mkdir($spcBinaryDir, 0o755); + + if ($this->fs->exists($spcBinaryPath)) { + $io->text(sprintf('Using the static-php-cli (spc) tool from "%s"', $spcBinaryPath)); + } else { + $spcSourceUrl = sprintf('https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-%s-%s', $os, $arch); + $io->text(sprintf('Downloading the static-php-cli (spc) tool from "%s" to "%s"', $spcSourceUrl, $spcBinaryPath)); + $this->downloadSPC($spcSourceUrl, $spcBinaryPath, $io); + $io->newLine(2); + } + } + + private function generatePHPBuildCacheKey(InputInterface $input): string + { + $keyComponents = []; + + foreach (['os', 'arch', 'php-version'] as $phpBuildOption) { + $keyComponents[$phpBuildOption] = $input->getOption($phpBuildOption); + } + + $phpExtensions = explode(',', $input->getOption('php-extensions')); + sort($phpExtensions); + + $keyComponents['php-extensions'] = $phpExtensions; + + $keyComponents['compile-command'] = hash_file('md5', __FILE__); + + return md5(serialize($keyComponents)); + } +} diff --git a/src/Console/Command/RepackCommand.php b/src/Console/Command/RepackCommand.php index 1cbef599..e55adb4c 100644 --- a/src/Console/Command/RepackCommand.php +++ b/src/Console/Command/RepackCommand.php @@ -22,7 +22,7 @@ protected function configure(): void ->setName('repack') ->addOption('app-name', null, InputOption::VALUE_REQUIRED, 'The name of the phar application', 'my-app') ->addOption('app-version', null, InputOption::VALUE_REQUIRED, 'The version of the phar application', '1.0.0') - ->addOption('os', null, InputOption::VALUE_REQUIRED, 'The targeted OS', 'linux', ['linux', 'macos', 'windows']) + ->addOption('os', null, InputOption::VALUE_REQUIRED, 'The targeted OS', 'linux', ['linux', 'darwin', 'windows']) ->setHidden(true) ; } @@ -34,8 +34,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int } $os = $input->getOption('os'); - if (!\in_array($os, ['linux', 'macos', 'windows'])) { - throw new \RuntimeException('The os option must be one of linux, macos or windows.'); + if (!\in_array($os, ['linux', 'darwin', 'windows'])) { + throw new \InvalidArgumentException('The os option must be one of linux, darwin or windows.'); } $finder = new ExecutableFinder(); @@ -89,7 +89,7 @@ class RepackedApplication extends Application ), ]; } - // add all files from the FunctionFinder, this is usefull if the file + // add all files from the FunctionFinder, this is useful if the file // are in a hidden directory, because it's not included by default by // box $boxConfig['files'] = [ @@ -106,7 +106,7 @@ class RepackedApplication extends Application $process = new Process([$box, 'compile', '--config=.box.json']); try { - $process->mustRun(fn ($type, $buffer) => print ($buffer)); + $process->mustRun(fn ($type, $buffer) => print $buffer); } finally { unlink('.box.json'); unlink('.main.php'); diff --git a/tests/CompileCommandTest.php b/tests/CompileCommandTest.php new file mode 100644 index 00000000..7d70b415 --- /dev/null +++ b/tests/CompileCommandTest.php @@ -0,0 +1,53 @@ +mustRun(); + + $binary = $castorAppDirPath . '/castor'; + + (new Process( + [ + 'vendor/jolicode/castor/bin/castor', + 'compile', $castorAppDirPath . '/my-app.linux.phar', + '--os', 'linux', + '--output', $binary, + '--php-extensions', 'mbstring,phar,posix,tokenizer', + '-vvv', + ], + cwd: $castorAppDirPath, + timeout: 5 * 60) + )->mustRun(); + + $this->assertFileExists($binary); + + (new Process([$binary], cwd: $castorAppDirPath))->mustRun(); + + $p = (new Process([$binary, 'hello'], cwd: $castorAppDirPath))->mustRun(); + $this->assertSame('hello', $p->getOutput()); + + // Twice, because we want to be sure the phar is not corrupted after a + // run + $p = (new Process([$binary, 'hello'], cwd: $castorAppDirPath))->mustRun(); + $this->assertSame('hello', $p->getOutput()); + } +} diff --git a/tests/Examples/Generated/FailureFailureTest.php.err.txt b/tests/Examples/Generated/FailureFailureTest.php.err.txt index 29775049..86a74933 100644 --- a/tests/Examples/Generated/FailureFailureTest.php.err.txt +++ b/tests/Examples/Generated/FailureFailureTest.php.err.txt @@ -1,3 +1,4 @@ +sh: 1: i_do_not_exist: not found In failure.php line 12: diff --git a/tests/Examples/Generated/FailureFailureTest.php.output.txt b/tests/Examples/Generated/FailureFailureTest.php.output.txt index e5b48ae6..e69de29b 100644 --- a/tests/Examples/Generated/FailureFailureTest.php.output.txt +++ b/tests/Examples/Generated/FailureFailureTest.php.output.txt @@ -1 +0,0 @@ -sh: 1: i_do_not_exist: not found diff --git a/tests/RepackCommandTest.php b/tests/RepackCommandTest.php index 474afc53..76cc2ad3 100644 --- a/tests/RepackCommandTest.php +++ b/tests/RepackCommandTest.php @@ -10,12 +10,36 @@ class RepackCommandTest extends TestCase { public function test() { - $tmp = sys_get_temp_dir() . '/castor-test-repack'; + $castorAppDirPath = self::setupRepackedCastorApp('castor-test-repack'); + + (new Process([ + 'vendor/jolicode/castor/bin/castor', + 'repack', + '--os', 'linux', + ], cwd: $castorAppDirPath))->mustRun(); + + $phar = $castorAppDirPath . '/my-app.linux.phar'; + $this->assertFileExists($phar); + + (new Process([$phar], cwd: $castorAppDirPath))->mustRun(); + + $p = (new Process([$phar, 'hello'], cwd: $castorAppDirPath))->mustRun(); + $this->assertSame('hello', $p->getOutput()); + + // Twice, because we want to be sure the phar is not corrupted after a + // run + $p = (new Process([$phar, 'hello'], cwd: $castorAppDirPath))->mustRun(); + $this->assertSame('hello', $p->getOutput()); + } + + public static function setupRepackedCastorApp(string $castorAppDirName): string + { + $castorAppDirPath = sys_get_temp_dir() . '/' . $castorAppDirName; $fs = new Filesystem(); - $fs->remove($tmp); - $fs->mkdir($tmp); - $fs->dumpFile($tmp . '/castor.php', <<<'PHP' + $fs->remove($castorAppDirPath); + $fs->mkdir($castorAppDirPath); + $fs->dumpFile($castorAppDirPath . '/castor.php', <<<'PHP' dumpFile($tmp . '/composer.json', json_encode([ + $fs->dumpFile($castorAppDirPath . '/composer.json', json_encode([ 'repositories' => [ [ 'type' => 'path', @@ -41,27 +65,10 @@ function hello(): void ])); (new Process(['composer', 'install'], - cwd: $tmp, + cwd: $castorAppDirPath, env: ['COMPOSER_MIRROR_PATH_REPOS' => '1'], ))->mustRun(); - (new Process([ - 'vendor/jolicode/castor/bin/castor', - 'repack', - '--os', 'linux', - ], cwd: $tmp))->mustRun(); - - $phar = $tmp . '/my-app.linux.phar'; - $this->assertFileExists($phar); - - (new Process([$phar], cwd: $tmp))->mustRun(); - - $p = (new Process([$phar, 'hello'], cwd: $tmp))->mustRun(); - $this->assertSame('hello', $p->getOutput()); - - // Twice, because we want to be sure the phar is not corrupted after a - // run - $p = (new Process([$phar, 'hello'], cwd: $tmp))->mustRun(); - $this->assertSame('hello', $p->getOutput()); + return $castorAppDirPath; } } diff --git a/tests/TaskTestCase.php b/tests/TaskTestCase.php index 206c5cd0..ac238a09 100644 --- a/tests/TaskTestCase.php +++ b/tests/TaskTestCase.php @@ -17,13 +17,14 @@ public function runTask(array $args, ?string $cwd = null): Process { $coverage = $this->getTestResultObject()?->getCodeCoverage(); - $bin = $_SERVER['CASTOR_BIN'] ?? __DIR__ . '/../bin/castor'; + $castorBin = $_SERVER['CASTOR_BIN'] ?? __DIR__ . '/../bin/castor'; + $extraEnv = [ 'ENDPOINT' => $_SERVER['ENDPOINT'], ]; if ($coverage) { - $bin = __DIR__ . '/bin/castor'; + $castorBin = __DIR__ . '/bin/castor'; $testName = debug_backtrace()[1]['class'] . '::' . debug_backtrace()[1]['function']; $outputFilename = stream_get_meta_data(tmpfile())['uri']; $extraEnv = [ @@ -33,7 +34,7 @@ public function runTask(array $args, ?string $cwd = null): Process } $process = new Process( - [\PHP_BINARY, $bin, '--no-ansi', ...$args], + [$castorBin, '--no-ansi', ...$args], cwd: $cwd ?? __DIR__ . '/..', env: [ 'COLUMNS' => 120, diff --git a/tools/php-cs-fixer/composer.lock b/tools/php-cs-fixer/composer.lock index ef89bde9..811d41a9 100644 --- a/tools/php-cs-fixer/composer.lock +++ b/tools/php-cs-fixer/composer.lock @@ -1047,16 +1047,16 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.28.0", + "version": "v1.29.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb" + "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", - "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ef4d7e442ca910c4764bce785146269b30cb5fc4", + "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4", "shasum": "" }, "require": { @@ -1070,9 +1070,6 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -1109,7 +1106,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.28.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.29.0" }, "funding": [ { @@ -1125,20 +1122,20 @@ "type": "tidelift" } ], - "time": "2023-01-26T09:26:14+00:00" + "time": "2024-01-29T20:11:03+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.28.0", + "version": "v1.29.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "875e90aeea2777b6f135677f618529449334a612" + "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/875e90aeea2777b6f135677f618529449334a612", - "reference": "875e90aeea2777b6f135677f618529449334a612", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/32a9da87d7b3245e09ac426c83d334ae9f06f80f", + "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f", "shasum": "" }, "require": { @@ -1149,9 +1146,6 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -1190,7 +1184,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.28.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.29.0" }, "funding": [ { @@ -1206,20 +1200,20 @@ "type": "tidelift" } ], - "time": "2023-01-26T09:26:14+00:00" + "time": "2024-01-29T20:11:03+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.28.0", + "version": "v1.29.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92" + "reference": "bc45c394692b948b4d383a08d7753968bed9a83d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92", - "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/bc45c394692b948b4d383a08d7753968bed9a83d", + "reference": "bc45c394692b948b4d383a08d7753968bed9a83d", "shasum": "" }, "require": { @@ -1230,9 +1224,6 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -1274,7 +1265,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.28.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.29.0" }, "funding": [ { @@ -1290,20 +1281,20 @@ "type": "tidelift" } ], - "time": "2023-01-26T09:26:14+00:00" + "time": "2024-01-29T20:11:03+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.28.0", + "version": "v1.29.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "42292d99c55abe617799667f454222c54c60e229" + "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229", - "reference": "42292d99c55abe617799667f454222c54c60e229", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec", + "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec", "shasum": "" }, "require": { @@ -1317,9 +1308,6 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -1357,7 +1345,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.29.0" }, "funding": [ { @@ -1373,20 +1361,20 @@ "type": "tidelift" } ], - "time": "2023-07-28T09:04:16+00:00" + "time": "2024-01-29T20:11:03+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.28.0", + "version": "v1.29.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5" + "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/6caa57379c4aec19c0a12a38b59b26487dcfe4b5", - "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/87b68208d5c1188808dd7839ee1e6c8ec3b02f1b", + "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b", "shasum": "" }, "require": { @@ -1394,9 +1382,6 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -1440,7 +1425,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.28.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.29.0" }, "funding": [ { @@ -1456,20 +1441,20 @@ "type": "tidelift" } ], - "time": "2023-01-26T09:26:14+00:00" + "time": "2024-01-29T20:11:03+00:00" }, { "name": "symfony/polyfill-php81", - "version": "v1.28.0", + "version": "v1.29.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php81.git", - "reference": "7581cd600fa9fd681b797d00b02f068e2f13263b" + "reference": "c565ad1e63f30e7477fc40738343c62b40bc672d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/7581cd600fa9fd681b797d00b02f068e2f13263b", - "reference": "7581cd600fa9fd681b797d00b02f068e2f13263b", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/c565ad1e63f30e7477fc40738343c62b40bc672d", + "reference": "c565ad1e63f30e7477fc40738343c62b40bc672d", "shasum": "" }, "require": { @@ -1477,9 +1462,6 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -1519,7 +1501,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php81/tree/v1.28.0" + "source": "https://github.com/symfony/polyfill-php81/tree/v1.29.0" }, "funding": [ { @@ -1535,7 +1517,7 @@ "type": "tidelift" } ], - "time": "2023-01-26T09:26:14+00:00" + "time": "2024-01-29T20:11:03+00:00" }, { "name": "symfony/process",