diff --git a/composer.json b/composer.json index 052ee04f..f3042772 100644 --- a/composer.json +++ b/composer.json @@ -49,6 +49,10 @@ "preferred-install": "dist" }, "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": true + }, "branch-alias": { "dev-master": "5.4-dev" } diff --git a/src/Parser/EntryParser.php b/src/Parser/EntryParser.php index 5cfa3eef..bfdb3df9 100644 --- a/src/Parser/EntryParser.php +++ b/src/Parser/EntryParser.php @@ -164,6 +164,7 @@ private static function parseValue(string $value) }); }); }, Success::create([Value::blank(), self::INITIAL_STATE]))->flatMap(static function (array $result) { + /** @psalm-suppress DocblockTypeContradiction */ if (in_array($result[1], self::REJECT_STATES, true)) { return Error::create('a missing closing quote'); } diff --git a/src/Repository/Adapter/ApacheAdapter.php b/src/Repository/Adapter/ApacheAdapter.php index 868033af..af0aae11 100644 --- a/src/Repository/Adapter/ApacheAdapter.php +++ b/src/Repository/Adapter/ApacheAdapter.php @@ -50,7 +50,7 @@ private static function isSupported() /** * Read an environment variable, if it exists. * - * @param string $name + * @param non-empty-string $name * * @return \PhpOption\Option */ @@ -65,8 +65,8 @@ public function read(string $name) /** * Write to an environment variable, if possible. * - * @param string $name - * @param string $value + * @param non-empty-string $name + * @param string $value * * @return bool */ @@ -78,7 +78,7 @@ public function write(string $name, string $value) /** * Delete an environment variable, if possible. * - * @param string $name + * @param non-empty-string $name * * @return bool */ diff --git a/src/Repository/Adapter/ArrayAdapter.php b/src/Repository/Adapter/ArrayAdapter.php index 2881a7e1..df64cf6d 100644 --- a/src/Repository/Adapter/ArrayAdapter.php +++ b/src/Repository/Adapter/ArrayAdapter.php @@ -40,7 +40,7 @@ public static function create() /** * Read an environment variable, if it exists. * - * @param string $name + * @param non-empty-string $name * * @return \PhpOption\Option */ @@ -52,8 +52,8 @@ public function read(string $name) /** * Write to an environment variable, if possible. * - * @param string $name - * @param string $value + * @param non-empty-string $name + * @param string $value * * @return bool */ @@ -67,7 +67,7 @@ public function write(string $name, string $value) /** * Delete an environment variable, if possible. * - * @param string $name + * @param non-empty-string $name * * @return bool */ diff --git a/src/Repository/Adapter/EnvConstAdapter.php b/src/Repository/Adapter/EnvConstAdapter.php index 9ef7fb4d..67752827 100644 --- a/src/Repository/Adapter/EnvConstAdapter.php +++ b/src/Repository/Adapter/EnvConstAdapter.php @@ -33,7 +33,7 @@ public static function create() /** * Read an environment variable, if it exists. * - * @param string $name + * @param non-empty-string $name * * @return \PhpOption\Option */ @@ -41,6 +41,9 @@ public function read(string $name) { /** @var \PhpOption\Option */ return Option::fromArraysValue($_ENV, $name) + ->filter(static function ($value) { + return \is_scalar($value); + }) ->map(static function ($value) { if ($value === false) { return 'false'; @@ -50,17 +53,15 @@ public function read(string $name) return 'true'; } - return $value; - })->filter(static function ($value) { - return \is_string($value); + return (string) $value; }); } /** * Write to an environment variable, if possible. * - * @param string $name - * @param string $value + * @param non-empty-string $name + * @param string $value * * @return bool */ @@ -74,7 +75,7 @@ public function write(string $name, string $value) /** * Delete an environment variable, if possible. * - * @param string $name + * @param non-empty-string $name * * @return bool */ diff --git a/src/Repository/Adapter/GuardedWriter.php b/src/Repository/Adapter/GuardedWriter.php index 7bb69e82..fed8b9ba 100644 --- a/src/Repository/Adapter/GuardedWriter.php +++ b/src/Repository/Adapter/GuardedWriter.php @@ -37,8 +37,8 @@ public function __construct(WriterInterface $writer, array $allowList) /** * Write to an environment variable, if possible. * - * @param string $name - * @param string $value + * @param non-empty-string $name + * @param string $value * * @return bool */ @@ -56,7 +56,7 @@ public function write(string $name, string $value) /** * Delete an environment variable, if possible. * - * @param string $name + * @param non-empty-string $name * * @return bool */ @@ -74,7 +74,7 @@ public function delete(string $name) /** * Determine if the given variable is allowed. * - * @param string $name + * @param non-empty-string $name * * @return bool */ diff --git a/src/Repository/Adapter/ImmutableWriter.php b/src/Repository/Adapter/ImmutableWriter.php index 574fcd69..399e6f9b 100644 --- a/src/Repository/Adapter/ImmutableWriter.php +++ b/src/Repository/Adapter/ImmutableWriter.php @@ -45,8 +45,8 @@ public function __construct(WriterInterface $writer, ReaderInterface $reader) /** * Write to an environment variable, if possible. * - * @param string $name - * @param string $value + * @param non-empty-string $name + * @param string $value * * @return bool */ @@ -72,7 +72,7 @@ public function write(string $name, string $value) /** * Delete an environment variable, if possible. * - * @param string $name + * @param non-empty-string $name * * @return bool */ @@ -99,7 +99,7 @@ public function delete(string $name) * * That is, is it an "existing" variable. * - * @param string $name + * @param non-empty-string $name * * @return bool */ diff --git a/src/Repository/Adapter/MultiReader.php b/src/Repository/Adapter/MultiReader.php index 12b3bda4..0cfda6f6 100644 --- a/src/Repository/Adapter/MultiReader.php +++ b/src/Repository/Adapter/MultiReader.php @@ -30,7 +30,7 @@ public function __construct(array $readers) /** * Read an environment variable, if it exists. * - * @param string $name + * @param non-empty-string $name * * @return \PhpOption\Option */ diff --git a/src/Repository/Adapter/MultiWriter.php b/src/Repository/Adapter/MultiWriter.php index e1dcf56b..15a9d8fd 100644 --- a/src/Repository/Adapter/MultiWriter.php +++ b/src/Repository/Adapter/MultiWriter.php @@ -28,8 +28,8 @@ public function __construct(array $writers) /** * Write to an environment variable, if possible. * - * @param string $name - * @param string $value + * @param non-empty-string $name + * @param string $value * * @return bool */ @@ -47,7 +47,7 @@ public function write(string $name, string $value) /** * Delete an environment variable, if possible. * - * @param string $name + * @param non-empty-string $name * * @return bool */ diff --git a/src/Repository/Adapter/PutenvAdapter.php b/src/Repository/Adapter/PutenvAdapter.php index 126c4656..6d017cdb 100644 --- a/src/Repository/Adapter/PutenvAdapter.php +++ b/src/Repository/Adapter/PutenvAdapter.php @@ -48,7 +48,7 @@ private static function isSupported() /** * Read an environment variable, if it exists. * - * @param string $name + * @param non-empty-string $name * * @return \PhpOption\Option */ @@ -63,8 +63,8 @@ public function read(string $name) /** * Write to an environment variable, if possible. * - * @param string $name - * @param string $value + * @param non-empty-string $name + * @param string $value * * @return bool */ @@ -78,7 +78,7 @@ public function write(string $name, string $value) /** * Delete an environment variable, if possible. * - * @param string $name + * @param non-empty-string $name * * @return bool */ diff --git a/src/Repository/Adapter/ReaderInterface.php b/src/Repository/Adapter/ReaderInterface.php index 5ece5ee7..306a63fc 100644 --- a/src/Repository/Adapter/ReaderInterface.php +++ b/src/Repository/Adapter/ReaderInterface.php @@ -9,7 +9,7 @@ interface ReaderInterface /** * Read an environment variable, if it exists. * - * @param string $name + * @param non-empty-string $name * * @return \PhpOption\Option */ diff --git a/src/Repository/Adapter/ReplacingWriter.php b/src/Repository/Adapter/ReplacingWriter.php index 326cd187..98c0f041 100644 --- a/src/Repository/Adapter/ReplacingWriter.php +++ b/src/Repository/Adapter/ReplacingWriter.php @@ -45,8 +45,8 @@ public function __construct(WriterInterface $writer, ReaderInterface $reader) /** * Write to an environment variable, if possible. * - * @param string $name - * @param string $value + * @param non-empty-string $name + * @param string $value * * @return bool */ @@ -63,7 +63,7 @@ public function write(string $name, string $value) /** * Delete an environment variable, if possible. * - * @param string $name + * @param non-empty-string $name * * @return bool */ @@ -83,7 +83,7 @@ public function delete(string $name) * Returns true if it currently exists, or existed at any point in the past * that we are aware of. * - * @param string $name + * @param non-empty-string $name * * @return bool */ diff --git a/src/Repository/Adapter/ServerConstAdapter.php b/src/Repository/Adapter/ServerConstAdapter.php index 8e3dc98e..8fc1d4b0 100644 --- a/src/Repository/Adapter/ServerConstAdapter.php +++ b/src/Repository/Adapter/ServerConstAdapter.php @@ -33,7 +33,7 @@ public static function create() /** * Read an environment variable, if it exists. * - * @param string $name + * @param non-empty-string $name * * @return \PhpOption\Option */ @@ -41,6 +41,9 @@ public function read(string $name) { /** @var \PhpOption\Option */ return Option::fromArraysValue($_SERVER, $name) + ->filter(static function ($value) { + return \is_scalar($value); + }) ->map(static function ($value) { if ($value === false) { return 'false'; @@ -50,17 +53,15 @@ public function read(string $name) return 'true'; } - return $value; - })->filter(static function ($value) { - return \is_string($value); + return (string) $value; }); } /** * Write to an environment variable, if possible. * - * @param string $name - * @param string $value + * @param non-empty-string $name + * @param string $value * * @return bool */ @@ -74,7 +75,7 @@ public function write(string $name, string $value) /** * Delete an environment variable, if possible. * - * @param string $name + * @param non-empty-string $name * * @return bool */ diff --git a/src/Repository/Adapter/WriterInterface.php b/src/Repository/Adapter/WriterInterface.php index 8b3fa577..4cb3d61f 100644 --- a/src/Repository/Adapter/WriterInterface.php +++ b/src/Repository/Adapter/WriterInterface.php @@ -9,8 +9,8 @@ interface WriterInterface /** * Write to an environment variable, if possible. * - * @param string $name - * @param string $value + * @param non-empty-string $name + * @param string $value * * @return bool */ @@ -19,7 +19,7 @@ public function write(string $name, string $value); /** * Delete an environment variable, if possible. * - * @param string $name + * @param non-empty-string $name * * @return bool */ diff --git a/src/Repository/AdapterRepository.php b/src/Repository/AdapterRepository.php index ada1c458..e4b8fb78 100644 --- a/src/Repository/AdapterRepository.php +++ b/src/Repository/AdapterRepository.php @@ -6,6 +6,7 @@ use Dotenv\Repository\Adapter\ReaderInterface; use Dotenv\Repository\Adapter\WriterInterface; +use InvalidArgumentException; final class AdapterRepository implements RepositoryInterface { @@ -46,7 +47,7 @@ public function __construct(ReaderInterface $reader, WriterInterface $writer) */ public function has(string $name) { - return $this->reader->read($name)->isDefined(); + return '' !== $name && $this->reader->read($name)->isDefined(); } /** @@ -54,10 +55,16 @@ public function has(string $name) * * @param string $name * + * @throws \InvalidArgumentException + * * @return string|null */ public function get(string $name) { + if ('' === $name) { + throw new InvalidArgumentException('Expected name to be a non-empty string.'); + } + return $this->reader->read($name)->getOrElse(null); } @@ -67,10 +74,16 @@ public function get(string $name) * @param string $name * @param string $value * + * @throws \InvalidArgumentException + * * @return bool */ public function set(string $name, string $value) { + if ('' === $name) { + throw new InvalidArgumentException('Expected name to be a non-empty string.'); + } + return $this->writer->write($name, $value); } @@ -79,10 +92,16 @@ public function set(string $name, string $value) * * @param string $name * + * @throws \InvalidArgumentException + * * @return bool */ public function clear(string $name) { + if ('' === $name) { + throw new InvalidArgumentException('Expected name to be a non-empty string.'); + } + return $this->writer->delete($name); } } diff --git a/src/Repository/RepositoryInterface.php b/src/Repository/RepositoryInterface.php index a2a7d32f..d9b18a40 100644 --- a/src/Repository/RepositoryInterface.php +++ b/src/Repository/RepositoryInterface.php @@ -20,6 +20,8 @@ public function has(string $name); * * @param string $name * + * @throws \InvalidArgumentException + * * @return string|null */ public function get(string $name); @@ -30,6 +32,8 @@ public function get(string $name); * @param string $name * @param string $value * + * @throws \InvalidArgumentException + * * @return bool */ public function set(string $name, string $value); @@ -39,6 +43,8 @@ public function set(string $name, string $value); * * @param string $name * + * @throws \InvalidArgumentException + * * @return bool */ public function clear(string $name); diff --git a/tests/Dotenv/Repository/Adapter/EnvConstAdapterTest.php b/tests/Dotenv/Repository/Adapter/EnvConstAdapterTest.php index 592f304e..8e10b711 100644 --- a/tests/Dotenv/Repository/Adapter/EnvConstAdapterTest.php +++ b/tests/Dotenv/Repository/Adapter/EnvConstAdapterTest.php @@ -35,7 +35,7 @@ public function testTrueRead() public function testBadTypeRead() { - $_ENV['CONST_TEST'] = 123; + $_ENV['CONST_TEST'] = [123]; $value = self::createAdapter()->read('CONST_TEST'); self::assertFalse($value->isDefined()); } diff --git a/tests/Dotenv/Repository/Adapter/ServerConstAdapterTest.php b/tests/Dotenv/Repository/Adapter/ServerConstAdapterTest.php index 7366b79a..d322f779 100644 --- a/tests/Dotenv/Repository/Adapter/ServerConstAdapterTest.php +++ b/tests/Dotenv/Repository/Adapter/ServerConstAdapterTest.php @@ -35,7 +35,7 @@ public function testTrueRead() public function testBadTypeRead() { - $_SERVER['CONST_TEST'] = 123; + $_SERVER['CONST_TEST'] = [123]; $value = self::createAdapter()->read('CONST_TEST'); self::assertFalse($value->isDefined()); } diff --git a/tests/Dotenv/Repository/RepositoryTest.php b/tests/Dotenv/Repository/RepositoryTest.php index 8f36f885..ce296dc6 100644 --- a/tests/Dotenv/Repository/RepositoryTest.php +++ b/tests/Dotenv/Repository/RepositoryTest.php @@ -165,7 +165,7 @@ public function testGettingVariableByName() self::assertSame('bar', $repo->get('FOO')); } - public function testGettingBadVariable() + public function testGettingNullVariable() { $repo = RepositoryBuilder::createWithDefaultAdapters()->make(); @@ -174,6 +174,16 @@ public function testGettingBadVariable() $repo->get(null); } + public function testGettingEmptyVariable() + { + $repo = RepositoryBuilder::createWithDefaultAdapters()->make(); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Expected name to be a non-empty string.'); + + $repo->get(''); + } + public function testSettingVariable() { $this->load(); @@ -185,7 +195,7 @@ public function testSettingVariable() self::assertSame('new', $repo->get('FOO')); } - public function testSettingBadVariable() + public function testSettingNullVariable() { $repo = RepositoryBuilder::createWithDefaultAdapters()->make(); @@ -194,6 +204,16 @@ public function testSettingBadVariable() $repo->set(null, 'foo'); } + public function testSettingEmptyVariable() + { + $repo = RepositoryBuilder::createWithDefaultAdapters()->make(); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Expected name to be a non-empty string.'); + + $repo->set('', 'foo'); + } + public function testClearingVariable() { $this->load(); @@ -217,7 +237,7 @@ public function testClearingVariableWithArrayAdapter() self::assertFalse($repo->has('FOO')); } - public function testClearingBadVariable() + public function testClearingNullVariable() { $repo = RepositoryBuilder::createWithDefaultAdapters()->make(); @@ -226,6 +246,16 @@ public function testClearingBadVariable() $repo->clear(null); } + public function testClearingEmptyVariable() + { + $repo = RepositoryBuilder::createWithDefaultAdapters()->make(); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Expected name to be a non-empty string.'); + + $repo->clear(''); + } + public function testCannotSetVariableOnImmutableInstance() { $this->load(); diff --git a/vendor-bin/phpstan/composer.json b/vendor-bin/phpstan/composer.json index 684fac88..ac2a0ebc 100644 --- a/vendor-bin/phpstan/composer.json +++ b/vendor-bin/phpstan/composer.json @@ -9,6 +9,9 @@ "thecodingmachine/phpstan-strict-rules": "1.0.0" }, "config": { - "preferred-install": "dist" + "preferred-install": "dist", + "allow-plugins": { + "phpstan/extension-installer": true + } } } diff --git a/vendor-bin/psalm/composer.json b/vendor-bin/psalm/composer.json index 55355425..fa599500 100644 --- a/vendor-bin/psalm/composer.json +++ b/vendor-bin/psalm/composer.json @@ -1,7 +1,7 @@ { "require": { "php": "^7.4", - "psalm/phar": "4.15.0" + "psalm/phar": "4.19.0" }, "config": { "preferred-install": "dist"