diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c27349a5..12da66ab 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - php: ['7.3', '7.4', '8.0', '8.1'] + php: ['8.0', '8.1', '8.2', '8.3'] name: PHP ${{ matrix.php }} tests steps: - uses: actions/checkout@v3 @@ -56,7 +56,7 @@ jobs: - name: Install PHP uses: shivammathur/setup-php@v2 with: - php-version: '7.4' + php-version: '8.0' - name: Debugging run: | php --version diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8738ecb5..dedc4f4a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,7 +14,7 @@ jobs: - name: Install PHP uses: shivammathur/setup-php@1.3.7 with: - php-version: 7.3 + php-version: 8.0 - name: Install dependencies run: composer install --no-dev --prefer-dist --no-suggest --no-progress diff --git a/.gitignore b/.gitignore index 3d6c7cac..370568b4 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,6 @@ vendor/ .DS_Store composer.lock + +/.phpunit.cache .phpunit.result.cache diff --git a/README.md b/README.md index 4583e63b..def13b9c 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Mozart [![Latest Stable Version](https://poser.pugx.org/coenjacobs/mozart/v/stable.svg)](https://packagist.org/packages/coenjacobs/mozart) [![License](https://poser.pugx.org/coenjacobs/mozart/license.svg)](https://packagist.org/packages/coenjacobs/mozart) [![Total Downloads](https://poser.pugx.org/coenjacobs/mozart/downloads)](//packagist.org/packages/coenjacobs/mozart) [![Docker Image Pulls](https://img.shields.io/docker/pulls/coenjacobs/mozart.svg)](https://hub.docker.com/r/coenjacobs/mozart) Composes all dependencies as a package inside a WordPress plugin. Load packages through Composer and have them wrapped inside your own namespace. Gone are the days when plugins could load conflicting versions of the same package, resulting in hard to reproduce bugs. -This package requires PHP 7.3 or higher in order to run the tool. You can use the resulting files as a bundle, requiring any PHP version you like, even PHP 5.2. +This package requires PHP 8.0 or higher in order to run the tool. You can use the resulting files as a bundle, requiring any PHP version you like, even PHP 5.2. **Warning:** This package is very experimental and breaking changes are very likely until version 1.0.0 is tagged. Use with caution, always wear a helmet when using this in production environments. @@ -42,7 +42,7 @@ php mozart.phar compose To install Mozart and its dependencies, without conflicting with the dependencies of your project, it is recommended that you install Mozart as a global package, if you choose to install Mozart via Composer. #### Global package -Using the `global` command when installing Mozart, it will be installed as a system wide package: +Using the `global` command when installing Mozart, it will be installed as a system wide package: ``` composer global require coenjacobs/mozart @@ -113,7 +113,7 @@ The following configuration is optional: - `excluded_packages` is an optional array that defines the packages to be excluded from the processing performed by Mozart. This is useful if some of the packages in the `packages` array define dependent packages whose namespaces you want to keep unchanged. The array requires the slugs of the packages, as in the case of the `packages` array. - `override_autoload` a dictionary, keyed with the package names, of autoload settings to replace those in the original packages' `composer.json` `autoload` property. -After Composer has loaded the packages as defined in your `composer.json` file, you can now run `mozart compose` and Mozart will bundle your packages according to the above configuration. It is recommended to dump the autoloader after Mozart has finished running, in case there are new classes or namespaces generated that aren't included in the autoloader yet. +After Composer has loaded the packages as defined in your `composer.json` file, you can now run `mozart compose` and Mozart will bundle your packages according to the above configuration. It is recommended to dump the autoloader after Mozart has finished running, in case there are new classes or namespaces generated that aren't included in the autoloader yet. ## Scripts Mozart is designed to install and be forgotten about. Using Composer scripts, the Mozart script can be run as soon as Composer either installs a new package, or updates an already installed one. This ensures that the packages you want to bundle, are always bundled in the latest installed version, automatically. These scripts also offer you the possibility to script dumping the autoloader, after Mozart is finished running: diff --git a/composer.json b/composer.json index 2b25de38..4f37687f 100644 --- a/composer.json +++ b/composer.json @@ -12,10 +12,7 @@ "prefer-stable": true, "license": "MIT", "require": { - "php": "^7.3|^8.0", - "symfony/console": "^4|^5", - "symfony/finder": "^4|^5", - "league/flysystem": "^1.0" + "php": "^8.0" }, "autoload": { "psr-4": { @@ -25,15 +22,21 @@ "config": { "allow-plugins": { "phpstan/extension-installer": true + }, + "platform": { + "php": "8.0" } }, "require-dev": { - "phpunit/phpunit": "^8.5", - "squizlabs/php_codesniffer": "^3.5", - "mheap/phpunit-github-actions-printer": "^1.4", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-deprecation-rules": "^1.0", - "phpstan/extension-installer": "^1.2" + "phpunit/phpunit": "^9.6", + "squizlabs/php_codesniffer": "^3.10", + "mheap/phpunit-github-actions-printer": "^1.5", + "phpstan/phpstan": "^1.11", + "phpstan/phpstan-deprecation-rules": "^1.2", + "phpstan/extension-installer": "^1.4", + "league/flysystem": "^2.5", + "symfony/console": "^5.4", + "symfony/finder": "^5.4" }, "scripts": { "lint": [ diff --git a/phpunit.xml b/phpunit.xml index 6f7b5b5c..9f3970aa 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,16 +1,17 @@ + stopOnFailure="false" + xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/11.2/phpunit.xsd" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + cacheDirectory=".phpunit.cache" + backupStaticProperties="false" + > ./tests/ - \ No newline at end of file + diff --git a/src/Console/Commands/Compose.php b/src/Console/Commands/Compose.php index a832cf11..dd9a567e 100644 --- a/src/Console/Commands/Compose.php +++ b/src/Console/Commands/Compose.php @@ -33,7 +33,7 @@ protected function configure() $this->setHelp(''); } - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { $workingDir = getcwd(); $this->workingDir = $workingDir; diff --git a/src/Mover.php b/src/Mover.php index 6106bf6c..42a78a45 100644 --- a/src/Mover.php +++ b/src/Mover.php @@ -8,7 +8,7 @@ use CoenJacobs\Mozart\Composer\Autoload\Psr0; use CoenJacobs\Mozart\Composer\Autoload\Psr4; use CoenJacobs\Mozart\Composer\Package; -use League\Flysystem\Adapter\Local; +use League\Flysystem\Local\LocalFilesystemAdapter; use League\Flysystem\Filesystem; use Symfony\Component\Finder\Finder; use Symfony\Component\Finder\SplFileInfo; @@ -36,7 +36,12 @@ public function __construct($workingDir, $config) $this->targetDir = $config->dep_directory; $this->config = $config; - $this->filesystem = new Filesystem(new Local($this->workingDir)); + $adapter = new LocalFilesystemAdapter( + $this->workingDir + ); + + // The FilesystemOperator + $this->filesystem = new Filesystem($adapter); } /** @@ -48,9 +53,9 @@ public function __construct($workingDir, $config) */ public function deleteTargetDirs($packages): void { - $this->filesystem->createDir($this->config->dep_directory); + $this->filesystem->createDirectory($this->config->dep_directory); - $this->filesystem->createDir($this->config->classmap_directory); + $this->filesystem->createDirectory($this->config->classmap_directory); foreach ($packages as $package) { $this->deleteDepTargetDirs($package); @@ -76,12 +81,12 @@ private function deleteDepTargetDirs($package): void case Psr4::class: $outputDir = $this->config->dep_directory . $packageAutoloader->namespace; $outputDir = str_replace('\\', DIRECTORY_SEPARATOR, $outputDir); - $this->filesystem->deleteDir($outputDir); + $this->filesystem->deleteDirectory($outputDir); break; case Classmap::class: $outputDir = $this->config->classmap_directory . $package->config->name; $outputDir = str_replace('\\', DIRECTORY_SEPARATOR, $outputDir); - $this->filesystem->deleteDir($outputDir); + $this->filesystem->deleteDirectory($outputDir); break; } } @@ -93,15 +98,15 @@ private function deleteDepTargetDirs($package): void public function deleteEmptyDirs(): void { - if (count($this->filesystem->listContents($this->config->dep_directory, true)) === 0) { - $this->filesystem->deleteDir($this->config->dep_directory); + if (count($this->filesystem->listContents($this->config->dep_directory, true)->toArray()) === 0) { + $this->filesystem->deleteDirectory($this->config->dep_directory); } - if (count($this->filesystem->listContents($this->config->classmap_directory, true)) === 0) { - $this->filesystem->deleteDir($this->config->classmap_directory); + if (count($this->filesystem->listContents($this->config->classmap_directory, true)->toArray()) === 0) { + $this->filesystem->deleteDirectory($this->config->classmap_directory); } } - + /** * @return void */ @@ -224,13 +229,13 @@ protected function deletePackageVendorDirectories(): void continue; } - $this->filesystem->deleteDir($packageDir); + $this->filesystem->deleteDirectory($packageDir); //Delete parent directory too if it became empty //(because that package was the only one from that vendor) $parentDir = dirname($packageDir); if ($this->dirIsEmpty($parentDir)) { - $this->filesystem->deleteDir($parentDir); + $this->filesystem->deleteDirectory($parentDir); } } } diff --git a/src/Replacer.php b/src/Replacer.php index 8fb289b6..57bd4e29 100644 --- a/src/Replacer.php +++ b/src/Replacer.php @@ -8,8 +8,8 @@ use CoenJacobs\Mozart\Composer\Package; use CoenJacobs\Mozart\Replace\ClassmapReplacer; use CoenJacobs\Mozart\Replace\NamespaceReplacer; -use League\Flysystem\Adapter\Local; -use League\Flysystem\FileNotFoundException; +use League\Flysystem\Local\LocalFilesystemAdapter; +use League\Flysystem\UnableToReadFile; use League\Flysystem\Filesystem; use Symfony\Component\Finder\Finder; @@ -36,7 +36,12 @@ public function __construct($workingDir, $config) $this->targetDir = $config->dep_directory; $this->config = $config; - $this->filesystem = new Filesystem(new Local($this->workingDir)); + $adapter = new LocalFilesystemAdapter( + $this->workingDir + ); + + // The FilesystemOperator + $this->filesystem = new Filesystem($adapter); } public function replacePackage(Package $package): void @@ -57,7 +62,7 @@ public function replaceInFile($targetFile, Autoloader $autoloader): void $targetFile = str_replace($this->workingDir, '', $targetFile); try { $contents = $this->filesystem->read($targetFile); - } catch (FileNotFoundException $e) { + } catch (UnableToReadFile $e) { return; } @@ -80,7 +85,7 @@ public function replaceInFile($targetFile, Autoloader $autoloader): void $this->replacedClasses = array_merge($this->replacedClasses, $replacer->replacedClasses); } - $this->filesystem->put($targetFile, $contents); + $this->filesystem->write($targetFile, $contents); } /** @@ -136,7 +141,7 @@ public function replaceParentClassesInDirectory(string $directory): void if ('.php' == substr($targetFile, -4, 4)) { try { $contents = $this->filesystem->read($targetFile); - } catch (FileNotFoundException $e) { + } catch (UnableToReadFile $e) { continue; } @@ -157,7 +162,11 @@ function ($matches) use ($replacement) { ); } - $this->filesystem->put($targetFile, $contents); + if (empty($contents)) { + continue; + } + + $this->filesystem->write($targetFile, $contents); } } } diff --git a/tests/Console/Commands/ComposeTest.php b/tests/Console/Commands/ComposeTest.php index 3282c81b..ea283cfc 100644 --- a/tests/Console/Commands/ComposeTest.php +++ b/tests/Console/Commands/ComposeTest.php @@ -3,6 +3,7 @@ use CoenJacobs\Mozart\Console\Commands\Compose; use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\Attributes\Test; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -35,6 +36,7 @@ public function setUp(): void * * @test */ + #[Test] public function it_fails_gracefully_when_composer_json_absent(): void { @@ -61,6 +63,7 @@ public function __construct($inputInterfaceMock, $outputInterfaceMock) * * @test */ + #[Test] public function it_handles_malformed_json_with_grace(): void { @@ -91,6 +94,7 @@ public function __construct($inputInterfaceMock, $outputInterfaceMock) * * @test */ + #[Test] public function it_handles_absent_extra_config_with_grace(): void { @@ -122,6 +126,7 @@ public function __construct($inputInterfaceMock, $outputInterfaceMock) * * @test */ + #[Test] public function it_handles_malformed_extra_config_with_grace(): void { @@ -152,6 +157,7 @@ public function __construct($inputInterfaceMock, $outputInterfaceMock) * * @test */ + #[Test] public function it_handles_absent_mozart_config_with_grace(): void { @@ -184,6 +190,7 @@ public function __construct($inputInterfaceMock, $outputInterfaceMock) * * @test */ + #[Test] public function it_handles_malformed_mozart_config__with_grace(): void { diff --git a/tests/MoverTest.php b/tests/MoverTest.php index d92c1a19..4577cc90 100644 --- a/tests/MoverTest.php +++ b/tests/MoverTest.php @@ -5,6 +5,7 @@ use CoenJacobs\Mozart\Console\Commands\Compose; use CoenJacobs\Mozart\Mover; use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\Attributes\Test; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -61,6 +62,7 @@ public function setUp(): void * * @test */ + #[Test] public function it_creates_absent_dirs(): void { $mover = new Mover($this->testsWorkingDir, $this->config); @@ -80,6 +82,7 @@ public function it_creates_absent_dirs(): void * * @test */ + #[Test] public function it_is_unpertrubed_by_existing_dirs(): void { $mover = new Mover($this->testsWorkingDir, $this->config); @@ -111,6 +114,7 @@ public function it_is_unpertrubed_by_existing_dirs(): void * * @test */ + #[Test] public function it_deletes_subdirs_for_packages_about_to_be_moved(): void { $mover = new Mover($this->testsWorkingDir, $this->config); @@ -137,8 +141,8 @@ public function it_deletes_subdirs_for_packages_about_to_be_moved(): void $mover->deleteTargetDirs($packages); - $this->assertDirectoryNotExists($this->testsWorkingDir . $this->config->dep_directory . 'Pimple'); - $this->assertDirectoryNotExists($this->testsWorkingDir . $this->config->dep_directory . 'ezyang'); + $this->assertDirectoryDoesNotExist($this->testsWorkingDir . $this->config->dep_directory . 'Pimple'); + $this->assertDirectoryDoesNotExist($this->testsWorkingDir . $this->config->dep_directory . 'ezyang'); } /** @@ -177,6 +181,7 @@ public function it_deletes_subdirs_for_packages_about_to_be_moved(): void * * @test */ + #[Test] public function it_moves_each_file_once_per_namespace() { diff --git a/tests/replacers/ClassMapReplacerTest.php b/tests/replacers/ClassMapReplacerTest.php index f14de75b..10b10ce8 100644 --- a/tests/replacers/ClassMapReplacerTest.php +++ b/tests/replacers/ClassMapReplacerTest.php @@ -3,10 +3,12 @@ use CoenJacobs\Mozart\Replace\ClassmapReplacer; use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\Attributes\Test; class ClassMapReplacerTest extends TestCase { /** @test */ + #[Test] public function it_replaces_class_declarations(): void { $contents = 'class Hello_World {'; @@ -17,6 +19,7 @@ public function it_replaces_class_declarations(): void } /** @test */ + #[Test] public function it_replaces_abstract_class_declarations(): void { $contents = 'abstract class Hello_World {'; @@ -27,6 +30,7 @@ public function it_replaces_abstract_class_declarations(): void } /** @test */ + #[Test] public function it_replaces_interface_class_declarations(): void { $contents = 'interface Hello_World {'; @@ -37,6 +41,7 @@ public function it_replaces_interface_class_declarations(): void } /** @test */ + #[Test] public function it_replaces_class_declarations_that_extend_other_classes(): void { $contents = 'class Hello_World extends Bye_World {'; @@ -47,6 +52,7 @@ public function it_replaces_class_declarations_that_extend_other_classes(): void } /** @test */ + #[Test] public function it_replaces_class_declarations_that_implement_interfaces(): void { $contents = 'class Hello_World implements Bye_World {'; @@ -57,6 +63,7 @@ public function it_replaces_class_declarations_that_implement_interfaces(): void } /** @test */ + #[Test] public function it_stores_replaced_class_names(): void { $contents = 'class Hello_World {'; @@ -67,6 +74,7 @@ public function it_stores_replaced_class_names(): void } /** @test */ + #[Test] public function it_replaces_class_declarations_psr2(): void { $contents = "class Hello_World\n{"; @@ -81,6 +89,7 @@ public function it_replaces_class_declarations_psr2(): void * * @test */ + #[Test] public function it_replaces_class(): void { $contents = "class Hello_World"; @@ -97,6 +106,7 @@ public function it_replaces_class(): void * * @test */ + #[Test] public function it_does_not_replace_inside_namespace_multiline(): void { $input = " @@ -116,6 +126,7 @@ class Hello_World * * @test */ + #[Test] public function it_does_not_replace_inside_namespace_singleline(): void { $input = "namespace Mozart; class Hello_World"; @@ -133,6 +144,7 @@ public function it_does_not_replace_inside_namespace_singleline(): void * * @test */ + #[Test] public function it_does_not_replace_inside_named_namespace_but_does_inside_explicit_global_namespace(): void { diff --git a/tests/replacers/NamespaceReplacerTest.php b/tests/replacers/NamespaceReplacerTest.php index d317378a..f5856ede 100644 --- a/tests/replacers/NamespaceReplacerTest.php +++ b/tests/replacers/NamespaceReplacerTest.php @@ -4,6 +4,7 @@ use CoenJacobs\Mozart\Composer\Autoload\Psr0; use CoenJacobs\Mozart\Replace\NamespaceReplacer; use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\Attributes\Test; class NamespaceReplacerTest extends TestCase { @@ -37,6 +38,7 @@ protected static function createReplacer(string $namespace, string $prefix = sel } /** @test */ + #[Test] public function it_replaces_namespace_declarations(): void { $contents = 'namespace Test\\Test;'; @@ -45,8 +47,8 @@ public function it_replaces_namespace_declarations(): void $this->assertEquals('namespace My\\Mozart\\Prefix\\Test\\Test;', $contents); } - /** @test */ + #[Test] public function it_doesnt_replaces_namespace_inside_namespace(): void { $replacer = self::createReplacer('Test'); @@ -58,6 +60,7 @@ public function it_doesnt_replaces_namespace_inside_namespace(): void } /** @test */ + #[Test] public function it_replaces_partial_namespace_declarations(): void { $contents = 'namespace Test\\Test\\Another;'; @@ -66,7 +69,8 @@ public function it_replaces_partial_namespace_declarations(): void $this->assertEquals('namespace My\\Mozart\\Prefix\\Test\\Test\\Another;', $contents); } - /** @test */ + /** @test */ + #[Test] public function it_doesnt_prefix_already_prefixed_namespace(): void { $replacer = self::createReplacer('Test\\Another'); @@ -78,6 +82,7 @@ public function it_doesnt_prefix_already_prefixed_namespace(): void } /** @test */ + #[Test] public function it_doesnt_double_replace_namespaces_that_also_exist_inside_another_namespace(): void { $chickenReplacer = self::createReplacer('Chicken'); @@ -110,6 +115,7 @@ public function it_doesnt_double_replace_namespaces_that_also_exist_inside_anoth * * @test */ + #[Test] public function it_replaces_namespace_use_as_declarations(): void {