diff --git a/composer.json b/composer.json index baec8f1..5a86cf7 100644 --- a/composer.json +++ b/composer.json @@ -47,7 +47,7 @@ "test": "phpunit -c phpunit.xml.dist --teamcity --testsuite unit", "integration-test": "phpunit -c phpunit.xml.dist --testsuite integration --verbose --no-coverage", "analyse": "phpstan analyse", - "coverage": "phpunit --coverage-html coverage", + "coverage": "phpunit -c phpunit.xml.dist --teamcity --testsuite unit --coverage-html coverage", "coverage-check": "coverage-check build/logs/clover.xml 100" } } diff --git a/src/LicenseKey/Providers/DotEnv/DotEnvAdapterFactory.php b/src/LicenseKey/Providers/DotEnv/DotEnvAdapterFactory.php index f4e3775..b4b2896 100644 --- a/src/LicenseKey/Providers/DotEnv/DotEnvAdapterFactory.php +++ b/src/LicenseKey/Providers/DotEnv/DotEnvAdapterFactory.php @@ -11,7 +11,10 @@ class DotEnvAdapterFactory */ public static function build($dotEnv = null): DotEnvAdapterInterface { - if (self::isV4($dotEnv)) { + if (self::isV5($dotEnv)) { + // vlucas/phpdotenv ^5.0 + return new DotEnvV5Adapter(); + } elseif (self::isV4($dotEnv)) { // vlucas/phpdotenv ^4.0 return new DotEnvV4Adapter(); } else { @@ -20,6 +23,18 @@ public static function build($dotEnv = null): DotEnvAdapterInterface } } + /** + * @param \Dotenv\Dotenv|null $dotEnv + * @return bool + */ + public static function isV5($dotEnv = null): bool + { + // Here we deliberately specify the namespace as string, + // to prevent autoloading from kicking in on load of this file + // This way we can mock it easier in a test + return method_exists(is_null($dotEnv) ? 'Dotenv\\Dotenv' : get_class($dotEnv), 'createUnsafeImmutable'); + } + /** * @param \Dotenv\Dotenv|null $dotEnv * @return bool diff --git a/src/LicenseKey/Providers/DotEnv/DotEnvV5Adapter.php b/src/LicenseKey/Providers/DotEnv/DotEnvV5Adapter.php new file mode 100644 index 0000000..7aeae8d --- /dev/null +++ b/src/LicenseKey/Providers/DotEnv/DotEnvV5Adapter.php @@ -0,0 +1,18 @@ +safeLoad(); + } +} diff --git a/tests/integration/IntegrationTest.php b/tests/integration/IntegrationTest.php index b00655e..b245895 100644 --- a/tests/integration/IntegrationTest.php +++ b/tests/integration/IntegrationTest.php @@ -242,6 +242,34 @@ public function testWithComposerConfigKeyWorksCorrectly() $this->assertEquals(0, $process->getExitCode()); } + public function testWithDotEnvV5EnvFileWorksCorrectly() + { + $localComposerPath = __DIR__ . "/scenarios/composer.dotenv5.json"; + $localDotEnvFilePath = __DIR__."/scenarios/dotenv5.env"; + $process = new Process( + [ + "docker", + "run", + "--rm", + "-i", + "--network=acf-pro-installer-test", + "-e", + "ACF_PRO_KEY=test", + "-v", + "{$localComposerPath}:/app/composer.json", + "-v", + "{$localDotEnvFilePath}:/app/.env", + "acf-pro-installer/testapp:latest" + ], + __DIR__ + ); + $process->setTimeout(60); + $process->mustRun(function ($type, $buffer) { + echo $buffer; + }); + $this->assertEquals(0, $process->getExitCode()); + } + public function testWithBedrockInstallWorksCorrectly() { // Download latest bedrock composer file and modify it to contain the required repository diff --git a/tests/integration/scenarios/composer.dotenv5.json b/tests/integration/scenarios/composer.dotenv5.json new file mode 100644 index 0000000..504fe4c --- /dev/null +++ b/tests/integration/scenarios/composer.dotenv5.json @@ -0,0 +1,37 @@ +{ + "name": "test/acf-pro-installer-test", + "description": "A Test project for ACF Pro Installer", + "license": "MIT", + "repositories": [ + { + "type": "path", + "url": "/plugin", + "version": "dev-master" + }, + { + "type": "composer", + "url": "file:///registry/packages.json" + } + ], + "authors": [ + { + "name": "PivvenIT", + "homepage": "https://pivvenit.nl" + } + ], + "keywords": [ + "test" + ], + "require": { + "vlucas/phpdotenv": "^5.0", + "advanced-custom-fields/advanced-custom-fields-pro": "dev-master", + "pivvenit/acf-pro-installer": "dev-master" + }, + "config": { + "options": { + "ssl": { + "verify_peer": false + } + } + } +} diff --git a/tests/integration/scenarios/dotenv5.env b/tests/integration/scenarios/dotenv5.env new file mode 100644 index 0000000..e6f20ba --- /dev/null +++ b/tests/integration/scenarios/dotenv5.env @@ -0,0 +1 @@ +ACF_PRO_KEY=test \ No newline at end of file diff --git a/tests/unit/LicenseKey/Providers/DotEnv/DotEnvAdapterFactoryTest.php b/tests/unit/LicenseKey/Providers/DotEnv/DotEnvAdapterFactoryTest.php index dbac26b..2864092 100644 --- a/tests/unit/LicenseKey/Providers/DotEnv/DotEnvAdapterFactoryTest.php +++ b/tests/unit/LicenseKey/Providers/DotEnv/DotEnvAdapterFactoryTest.php @@ -7,6 +7,7 @@ use PivvenIT\Composer\Installers\ACFPro\LicenseKey\Providers\DotEnv\DotEnvAdapterFactory; use PivvenIT\Composer\Installers\ACFPro\LicenseKey\Providers\DotEnv\DotEnvV3Adapter; use PivvenIT\Composer\Installers\ACFPro\LicenseKey\Providers\DotEnv\DotEnvV4Adapter; +use PivvenIT\Composer\Installers\ACFPro\LicenseKey\Providers\DotEnv\DotEnvV5Adapter; class DotEnvAdapterFactoryTest extends TestCase { @@ -21,6 +22,17 @@ public function createImmutable() $this->assertInstanceOf(DotEnvV4Adapter::class, DotEnvAdapterFactory::build($mock)); } + public function testBuildWithV5ReturnsV5Adapter() + { + $mock = new class { + public function createUnsafeImmutable() + { + return; + } + }; + $this->assertInstanceOf(DotEnvV5Adapter::class, DotEnvAdapterFactory::build($mock)); + } + public function testBuildWithV34ReturnsV3Adapter() { $mock = new class { @@ -34,7 +46,14 @@ public function create() public function testBuildReturnsCorrectAdapter() { - if (DotEnvAdapterFactory::isV4()) { + if (DotEnvAdapterFactory::isV5()) { + $mock = new class { + public function createUnsafeImmutable() + { + return; + } + }; + } elseif (DotEnvAdapterFactory::isV4()) { $mock = new class { public function createImmutable() { diff --git a/tests/unit/LicenseKey/Providers/DotEnv/DotEnvV5AdapterTest.php b/tests/unit/LicenseKey/Providers/DotEnv/DotEnvV5AdapterTest.php new file mode 100644 index 0000000..b8cce35 --- /dev/null +++ b/tests/unit/LicenseKey/Providers/DotEnv/DotEnvV5AdapterTest.php @@ -0,0 +1,91 @@ +autoloader = function ($className) { + if ($className == "Dotenv\\Dotenv") { + $mock = new class { + public static function createUnsafeImmutable() + { + return new self; + } + + public static function safeLoad() + { + // Load the ACF_PRO_KEY with the key specified above + putenv( + sprintf( + "%s=%s", + EnvironmentVariableLicenseKeyProvider::ENV_VARIABLE_NAME, + "ab83a014-61f5-412b-9084-5c5b056105c0" + ) + ); + } + }; + class_alias(get_class($mock), 'Dotenv\\Dotenv'); + } + }; + spl_autoload_register($this->autoloader, false, true); + $sut = new DotEnvV5Adapter(); + $this->assertFalse(getenv(EnvironmentVariableLicenseKeyProvider::ENV_VARIABLE_NAME)); + $sut->load(getcwd()); + $this->assertEquals($key, getenv(EnvironmentVariableLicenseKeyProvider::ENV_VARIABLE_NAME)); + } + + public function testLoadWithoutKeyInEnvFileDoesNotSetKey() + { + $this->autoloader = function ($className) { + if ($className == "Dotenv\\Dotenv") { + $mock = new class { + public static function createUnsafeImmutable() + { + return new self; + } + + public static function safeLoad() + { + // Does not load anything + return; + } + }; + class_alias(get_class($mock), 'Dotenv\\Dotenv'); + } + }; + spl_autoload_register($this->autoloader, false, true); + $sut = new DotEnvV5Adapter(); + $this->assertFalse(getenv(EnvironmentVariableLicenseKeyProvider::ENV_VARIABLE_NAME)); + $sut->load(getcwd()); + $this->assertFalse(getenv(EnvironmentVariableLicenseKeyProvider::ENV_VARIABLE_NAME)); + } + + /** + * @inheritdoc + */ + protected function tearDown(): void + { + parent::tearDown(); + if ($this->autoloader != null) { + spl_autoload_unregister($this->autoloader); + } + putenv(EnvironmentVariableLicenseKeyProvider::ENV_VARIABLE_NAME); //Clears the environment variable + } +}