From df549aef998ddc39b396981d734c08aa74fe1702 Mon Sep 17 00:00:00 2001 From: mpyw Date: Fri, 19 Nov 2021 11:29:29 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20Improve=20code=20quality?= =?UTF-8?q?=20and=20support=20PHPStan?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 12 +- .gitignore | 1 + .scrutinizer.yml | 11 +- README.md | 7 +- _ide_helper.php | 165 ++++++++++++++++++ composer.json | 27 ++- extension.neon | 13 ++ phpstan.neon | 10 ++ phpstan/CallableArgumentParameter.php | 41 +++++ phpstan/CallableFacadeReturnTypeExtension.php | 52 ++++++ phpstan/CallableParameter.php | 54 ++++++ phpstan/CallableReturnTypeExtension.php | 52 ++++++ phpstan/ConnectionClassExtension.php | 29 +++ phpstan/KeyParameter.php | 41 +++++ phpstan/MemoizeParameter.php | 42 +++++ phpstan/SystemVariablesMethod.php | 154 ++++++++++++++++ phpstan/ValueParameter.php | 41 +++++ phpstan/ValuesParameter.php | 46 +++++ src/ExpressionInterface.php | 6 +- src/ExpressionTrait.php | 6 - src/ManagesSystemVariables.php | 19 +- src/Replacer.php | 18 +- src/Replacers/BooleanReplacerInterface.php | 3 - src/Replacers/CallbackBooleanReplacer.php | 8 +- src/Replacers/CallbackFloatReplacer.php | 8 +- src/Replacers/CallbackIntegerReplacer.php | 8 +- src/Replacers/CallbackStringReplacer.php | 8 +- src/Replacers/FloatReplacerInterface.php | 3 - src/Replacers/IntegerReplacerInterface.php | 3 - src/Replacers/StringReplacerInterface.php | 3 - src/SystemVariableAssigner.php | 52 +++--- src/SystemVariableAwareReconnector.php | 9 +- src/SystemVariableGrammar.php | 20 +-- src/SystemVariableMemoizedAssigner.php | 10 +- src/SystemVariableSelector.php | 15 +- src/SystemVariableTemporaryAssigner.php | 9 +- src/Value.php | 41 ++--- tests/BasicVariableAssignmentTest.php | 4 - tests/TestCase.php | 23 ++- 39 files changed, 889 insertions(+), 185 deletions(-) create mode 100644 _ide_helper.php create mode 100644 extension.neon create mode 100644 phpstan.neon create mode 100644 phpstan/CallableArgumentParameter.php create mode 100644 phpstan/CallableFacadeReturnTypeExtension.php create mode 100644 phpstan/CallableParameter.php create mode 100644 phpstan/CallableReturnTypeExtension.php create mode 100644 phpstan/ConnectionClassExtension.php create mode 100644 phpstan/KeyParameter.php create mode 100644 phpstan/MemoizeParameter.php create mode 100644 phpstan/SystemVariablesMethod.php create mode 100644 phpstan/ValueParameter.php create mode 100644 phpstan/ValuesParameter.php diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c5f6005..eeba8df 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ jobs: strategy: matrix: - php: [7.3, 7.4, '8.0', 8.1] + php: [7.4, '8.0', 8.1] lib: - { laravel: ^9.0 } - { laravel: ^8.0 } @@ -36,7 +36,11 @@ jobs: - { php: 8.1, lib: { laravel: ^6.0 } } - { php: 8.1, lib: { laravel: ^6.0, flags: --prefer-lowest } } - { php: 7.4, lib: { laravel: ^9.0 } } - - { php: 7.3, lib: { laravel: ^9.0 } } + include: + - { php: 8.1, lib: { laravel: ^9.0 }, phpstan: '1' } + - { php: 8.1, lib: { laravel: ^8.0 }, phpstan: '1' } + - { php: '8.0', lib: { laravel: ^9.0 }, phpstan: '1' } + - { php: '8.0', lib: { laravel: ^8.0 }, phpstan: '1' } steps: - uses: actions/checkout@v2 @@ -51,6 +55,10 @@ jobs: run: | composer require "laravel/framework:${{ matrix.lib.laravel }}" --dev ${{ matrix.lib.flags }} + - name: PHPStan + if: ${{ matrix.phpstan }} + run: composer phpstan + - run: mkdir -p build/logs - run: vendor/bin/phpunit --coverage-clover build/logs/clover.xml diff --git a/.gitignore b/.gitignore index 475b6ea..a2363fe 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ composer.lock +/.idea/ /vendor/ /build/logs/ .php_cs.cache diff --git a/.scrutinizer.yml b/.scrutinizer.yml index 67d9421..a40aeee 100644 --- a/.scrutinizer.yml +++ b/.scrutinizer.yml @@ -4,6 +4,7 @@ checks: filter: excluded_paths: + - phpstan/* - tests/* - vendor/* @@ -14,12 +15,6 @@ build: tests: override: - php-scrutinizer-run - - environment: - php: '7.4' - docker: true - - nodes: custom-nodes: services: custom-mysql: @@ -32,6 +27,10 @@ build: ports: - 3306 + environment: + php: '7.4' + docker: true + dependencies: before: - composer install diff --git a/README.md b/README.md index f31e761..a5c59b3 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,11 @@ A tiny extension of `MySqlConnection` that manages **session** system variables ## Requirements -- PHP: `^7.3 || ^8.0` -- Laravel: `^6.0 || ^7.0 || ^8.0 || ^9.0` +| Package | Version | Mandatory | +|:---|:---|:---:| +| PHP | ^7.4 || ^8.0 | ✅ | +| Laravel | ^6.0 || ^7.0 || ^8.0 || ^9.0 | ✅ | +| PHPStan | >=1.1 | | ## Installing diff --git a/_ide_helper.php b/_ide_helper.php new file mode 100644 index 0000000..0159066 --- /dev/null +++ b/_ide_helper.php @@ -0,0 +1,165 @@ +=6.6", - "phpunit/phpunit": ">=9.5" + "phpunit/phpunit": ">=9.5", + "phpstan/phpstan": ">=1.1", + "phpstan/extension-installer": ">=1.1", + "nunomaduro/larastan": "^1.0" + }, + "scripts": { + "test": "vendor/bin/phpunit", + "phpstan": "vendor/bin/phpstan analyse src tests phpstan" }, "minimum-stability": "dev", - "prefer-stable": true + "prefer-stable": true, + "extra": { + "phpstan": { + "includes": [ + "extension.neon" + ] + } + } } diff --git a/extension.neon b/extension.neon new file mode 100644 index 0000000..a4ac342 --- /dev/null +++ b/extension.neon @@ -0,0 +1,13 @@ +services: + - + class: Mpyw\LaravelMySqlSystemVariableManager\PHPStan\ConnectionClassExtension + tags: + - phpstan.broker.methodsClassReflectionExtension + - + class: Mpyw\LaravelMySqlSystemVariableManager\PHPStan\CallableReturnTypeExtension + tags: + - phpstan.broker.dynamicMethodReturnTypeExtension + - + class: Mpyw\LaravelMySqlSystemVariableManager\PHPStan\CallableFacadeReturnTypeExtension + tags: + - phpstan.broker.dynamicStaticMethodReturnTypeExtension diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..2a71b8d --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,10 @@ +includes: + - ./extension.neon + +parameters: + level: 9 + checkMissingIterableValueType: false + reportUnmatchedIgnoredErrors: false + ignoreErrors: + - message: '#Cannot access property \$value on mixed#' + path: tests diff --git a/phpstan/CallableArgumentParameter.php b/phpstan/CallableArgumentParameter.php new file mode 100644 index 0000000..6c9092d --- /dev/null +++ b/phpstan/CallableArgumentParameter.php @@ -0,0 +1,41 @@ +getName(), $methods, true); + } + + public function getTypeFromStaticMethodCall(MethodReflection $methodReflection, StaticCall $methodCall, Scope $scope): \PHPStan\Type\Type + { + if ($methodReflection->getName()[0] === 's') { + return new ObjectType(Connection::class); + } + + $offset = $methodReflection->getName()[\strlen($methodReflection->getName()) - 1] === 's' ? 1 : 2; + + if (\count($methodCall->getArgs()) > $offset) { + $type = $scope->getType($methodCall->getArgs()[$offset]->value); + + if ($type instanceof ParametersAcceptor) { + return $type->getReturnType(); + } + } + + return new MixedType(); + } +} diff --git a/phpstan/CallableParameter.php b/phpstan/CallableParameter.php new file mode 100644 index 0000000..e432187 --- /dev/null +++ b/phpstan/CallableParameter.php @@ -0,0 +1,54 @@ +argumentParameters = $argumentParameters; + } + + public function getName(): string + { + return 'callback'; + } + + public function isOptional(): bool + { + return false; + } + + public function getType(): Type + { + return new CallableType($this->argumentParameters); + } + + public function passedByReference(): PassedByReference + { + return PassedByReference::createNo(); + } + + public function isVariadic(): bool + { + return false; + } + + public function getDefaultValue(): ?Type + { + return null; + } +} diff --git a/phpstan/CallableReturnTypeExtension.php b/phpstan/CallableReturnTypeExtension.php new file mode 100644 index 0000000..9306e2d --- /dev/null +++ b/phpstan/CallableReturnTypeExtension.php @@ -0,0 +1,52 @@ +getName(), $methods, true); + } + + public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type + { + if ($methodReflection->getName()[0] === 's') { + return new ThisType($methodReflection->getDeclaringClass()); + } + + $offset = $methodReflection->getName()[\strlen($methodReflection->getName()) - 1] === 's' ? 1 : 2; + + if (\count($methodCall->getArgs()) > $offset) { + $type = $scope->getType($methodCall->getArgs()[$offset]->value); + + if ($type instanceof ParametersAcceptor) { + return $type->getReturnType(); + } + } + + return new MixedType(); + } +} diff --git a/phpstan/ConnectionClassExtension.php b/phpstan/ConnectionClassExtension.php new file mode 100644 index 0000000..841a23d --- /dev/null +++ b/phpstan/ConnectionClassExtension.php @@ -0,0 +1,29 @@ +getName(), ConnectionInterface::class, true); + } + + public function getMethod(ClassReflection $classReflection, string $methodName): MethodReflection + { + return new SystemVariablesMethod($classReflection, $methodName); + } +} diff --git a/phpstan/KeyParameter.php b/phpstan/KeyParameter.php new file mode 100644 index 0000000..4ce858c --- /dev/null +++ b/phpstan/KeyParameter.php @@ -0,0 +1,41 @@ +class = $classReflection; + $this->name = $methodName; + } + + public function getDeclaringClass(): ClassReflection + { + return $this->class; + } + + public function isStatic(): bool + { + return false; + } + + public function isPrivate(): bool + { + return false; + } + + public function isPublic(): bool + { + return true; + } + + public function getDocComment(): ?string + { + return null; + } + + public function getName(): string + { + return $this->name; + } + + public function getPrototype(): ClassMemberReflection + { + return $this; + } + + public function getVariants(): array + { + return $this->getName()[0] === 's' + ? $this->getSetVariants() + : $this->getUsingVariants(); + } + + private function getSetVariants(): array + { + return [new FunctionVariant( + TemplateTypeMap::createEmpty(), + null, + $this->getName()[\strlen($this->getName()) - 1] === 's' + ? [ + new ValuesParameter(), + new MemoizeParameter(), + ] + : [ + new KeyParameter(), + new ValueParameter(), + new MemoizeParameter(), + ], + false, + new ThisType($this->class), + )]; + } + + private function getUsingVariants(): array + { + $baseArgs = $this->getName()[\strlen($this->getName()) - 1] === 's' + ? [ + new ValuesParameter(), + ] + : [ + new KeyParameter(), + new ValueParameter(), + ]; + + $variants = []; + + for ($i = 0; $i < 10; ++$i) { + $argumentParameters = []; + for ($j = 0; $j < $i; ++$j) { + $argumentParameters[] = new CallableArgumentParameter(); + } + + $variants[] = new FunctionVariant( + TemplateTypeMap::createEmpty(), + null, + [ + ...$baseArgs, + new CallableParameter($argumentParameters), + ...$argumentParameters, + ], + false, + new MixedType(), + ); + } + + return $variants; + } + + public function isDeprecated(): TrinaryLogic + { + return TrinaryLogic::createNo(); + } + + public function getDeprecatedDescription(): ?string + { + return null; + } + + public function isFinal(): TrinaryLogic + { + return TrinaryLogic::createNo(); + } + + public function isInternal(): TrinaryLogic + { + return TrinaryLogic::createNo(); + } + + public function getThrowType(): ?Type + { + return new ObjectType(QueryException::class); + } + + public function hasSideEffects(): TrinaryLogic + { + return TrinaryLogic::createMaybe(); + } +} diff --git a/phpstan/ValueParameter.php b/phpstan/ValueParameter.php new file mode 100644 index 0000000..e8bf8a7 --- /dev/null +++ b/phpstan/ValueParameter.php @@ -0,0 +1,41 @@ +pdos = array_filter($pdos); + $this->pdos = \array_filter($pdos); } /** @@ -47,8 +47,7 @@ public function assign(array $values) /** * Configure PDO using query and parameters temporarily enabling PDO::ATTR_EMULATE_PREPARES. * - * @param string $query - * @param array $values + * @param array $values * @return $this */ protected function withEmulatedStatement(string $query, array $values = []) @@ -67,10 +66,7 @@ protected function withEmulatedStatement(string $query, array $values = []) } /** - * @param \PDO $pdo - * @param string $query - * @param array $values - * @return \PDO + * @param array $values */ protected static function withEmulatedStatementFor(PDO $pdo, string $query, array $values): PDO { @@ -83,14 +79,11 @@ protected static function withEmulatedStatementFor(PDO $pdo, string $query, arra } /** - * @param \PDO $pdo - * @param string $query - * @param array $values - * @return \PDO + * @param array $values */ protected static function withStatementFor(PDO $pdo, string $query, array $values): PDO { - $expressions = array_map([Value::class, 'wrap'], $values); + $expressions = \array_map([Value::class, 'wrap'], $values); $original = static::selectOriginalVariablesForReplacer($pdo, $expressions); $statement = $pdo->prepare($query); @@ -104,13 +97,12 @@ protected static function withStatementFor(PDO $pdo, string $query, array $value } /** - * @param \PDO $pdo * @param \Mpyw\LaravelMySqlSystemVariableManager\ExpressionInterface[] $expressions * @return \Mpyw\LaravelMySqlSystemVariableManager\ValueInterface[] */ protected static function selectOriginalVariablesForReplacer(PDO $pdo, array $expressions): array { - $replacers = array_filter($expressions, function ($value) { + $replacers = \array_filter($expressions, function ($value) { return $value instanceof IntegerReplacerInterface || $value instanceof BooleanReplacerInterface || $value instanceof FloatReplacerInterface @@ -120,12 +112,6 @@ protected static function selectOriginalVariablesForReplacer(PDO $pdo, array $ex return SystemVariableSelector::selectOriginalVariables($pdo, $replacers); } - /** - * @param \PDOStatement $statement - * @param int $parameter - * @param \Mpyw\LaravelMySqlSystemVariableManager\ExpressionInterface $expression - * @param null|\Mpyw\LaravelMySqlSystemVariableManager\ValueInterface $original - */ protected static function bindValue(PDOStatement $statement, int $parameter, ExpressionInterface $expression, ?ValueInterface $original = null): void { if ($expression instanceof ValueInterface) { @@ -133,13 +119,23 @@ protected static function bindValue(PDOStatement $statement, int $parameter, Exp return; } - if (($expression instanceof IntegerReplacerInterface - || $expression instanceof BooleanReplacerInterface - || $expression instanceof FloatReplacerInterface - || $expression instanceof StringReplacerInterface - ) && $original) { - $statement->bindValue($parameter, $expression->replace($original->getValue()), $expression->getParamType()); - return; + if ($original) { + if ($expression instanceof IntegerReplacerInterface) { + $statement->bindValue($parameter, $expression->replace((int)$original->getValue()), $expression->getParamType()); + return; + } + if ($expression instanceof BooleanReplacerInterface) { + $statement->bindValue($parameter, $expression->replace((bool)$original->getValue()), $expression->getParamType()); + return; + } + if ($expression instanceof FloatReplacerInterface) { + $statement->bindValue($parameter, $expression->replace((float)$original->getValue()), $expression->getParamType()); + return; + } + if ($expression instanceof StringReplacerInterface) { + $statement->bindValue($parameter, $expression->replace((string)$original->getValue()), $expression->getParamType()); + return; + } } // @codeCoverageIgnoreStart diff --git a/src/SystemVariableAwareReconnector.php b/src/SystemVariableAwareReconnector.php index 72cf33f..b184bc3 100644 --- a/src/SystemVariableAwareReconnector.php +++ b/src/SystemVariableAwareReconnector.php @@ -13,7 +13,7 @@ class SystemVariableAwareReconnector /** * @var array */ - protected $memoizedSystemVariables = []; + protected array $memoizedSystemVariables = []; /** * @var null|callable @@ -22,8 +22,6 @@ class SystemVariableAwareReconnector /** * SystemVariableAwareReconnector constructor. - * - * @param null|callable $reconnector */ public function __construct(?callable $reconnector = null) { @@ -36,17 +34,16 @@ public function __construct(?callable $reconnector = null) */ public function memoizeSystemVariables(array $values) { - $this->memoizedSystemVariables = array_replace($this->memoizedSystemVariables, $values); + $this->memoizedSystemVariables = \array_replace($this->memoizedSystemVariables, $values); return $this; } /** - * @param \Illuminate\Database\ConnectionInterface|\Mpyw\LaravelMySqlSystemVariableManager\ManagesSystemVariables $connection * @return mixed */ public function __invoke(ConnectionInterface $connection) { - if (is_callable($this->reconnector)) { + if (\is_callable($this->reconnector) && \method_exists($connection, 'setSystemVariables')) { $result = ($this->reconnector)($connection); $connection->setSystemVariables($this->memoizedSystemVariables, true); return $result; diff --git a/src/SystemVariableGrammar.php b/src/SystemVariableGrammar.php index 034b0e2..1ad161b 100644 --- a/src/SystemVariableGrammar.php +++ b/src/SystemVariableGrammar.php @@ -5,34 +5,32 @@ class SystemVariableGrammar { /** - * @param string[] $variables - * @return string + * @param string[] $variables */ public static function selectStatement(array $variables): string { - return 'select ' . implode(', ', static::variableExpressions($variables)); + return 'select ' . \implode(', ', static::variableExpressions($variables)); } /** - * @param string[] $variables + * @param string[] $variables * @return string[] */ public static function variableExpressions(array $variables): array { $expressions = []; foreach ($variables as $variable) { - $expressions[] = sprintf('@@%1$s as %1$s', static::escapeIdentifier($variable)); + $expressions[] = \sprintf('@@%1$s as %1$s', static::escapeIdentifier($variable)); } return $expressions; } /** - * @param array $values - * @return string + * @param array $values */ public static function assignmentStatement(array $values): string { - return 'set session ' . implode(', ', static::assignmentExpressions($values)); + return 'set session ' . \implode(', ', static::assignmentExpressions($values)); } /** @@ -48,12 +46,8 @@ public static function assignmentExpressions(array $values): array return $expressions; } - /** - * @param string $identifier - * @return string - */ public static function escapeIdentifier(string $identifier): string { - return '`' . str_replace('`', '``', $identifier) . '`'; + return '`' . \str_replace('`', '``', $identifier) . '`'; } } diff --git a/src/SystemVariableMemoizedAssigner.php b/src/SystemVariableMemoizedAssigner.php index fb70db6..63d5321 100644 --- a/src/SystemVariableMemoizedAssigner.php +++ b/src/SystemVariableMemoizedAssigner.php @@ -4,15 +4,12 @@ class SystemVariableMemoizedAssigner { - /** - * @var \Mpyw\LaravelMySqlSystemVariableManager\SystemVariableAwareReconnector - */ - protected $reconnector; + protected SystemVariableAwareReconnector $reconnector; /** * @var \Closure[]|\PDO[] */ - protected $pdos; + protected array $pdos; /** * SystemVariableMemoizedAssigner constructor. @@ -26,14 +23,13 @@ public function __construct(&$reconnector, &...$pdos) ? new SystemVariableAwareReconnector($reconnector) : $reconnector; - $this->pdos = array_filter($pdos); + $this->pdos = \array_filter($pdos); } /** * Set MySQL system variables for PDO. * * @param array $values - * @param bool $memoizeForReconnect * @return $this */ public function assign(array $values, bool $memoizeForReconnect = true) diff --git a/src/SystemVariableSelector.php b/src/SystemVariableSelector.php index 31aa07a..5ec8790 100644 --- a/src/SystemVariableSelector.php +++ b/src/SystemVariableSelector.php @@ -9,7 +9,6 @@ class SystemVariableSelector /** * Select current MySQL system variable values. * - * @param \PDO $pdo * @param array $newValues * @return \Mpyw\LaravelMySqlSystemVariableManager\ValueInterface[] */ @@ -19,9 +18,17 @@ public static function selectOriginalVariables(PDO $pdo, array $newValues): arra return []; } - $original = $pdo - ->query(SystemVariableGrammar::selectStatement(array_keys($newValues))) - ->fetch(PDO::FETCH_ASSOC); + $stmt = $pdo->query(SystemVariableGrammar::selectStatement(\array_keys($newValues))); + + if (!$stmt) { + // @codeCoverageIgnoreStart + return []; + // @codeCoverageIgnoreEnd + } + + $original = $stmt->fetch(PDO::FETCH_ASSOC); + + \assert(\is_array($original)); foreach ($original as $key => $value) { $original[$key] = Value::as(Value::wrap($newValues[$key])->getType(), $value); diff --git a/src/SystemVariableTemporaryAssigner.php b/src/SystemVariableTemporaryAssigner.php index 912bb10..bcf8767 100644 --- a/src/SystemVariableTemporaryAssigner.php +++ b/src/SystemVariableTemporaryAssigner.php @@ -10,7 +10,7 @@ class SystemVariableTemporaryAssigner /** * @var \Closure[]|\PDO[] */ - protected $pdos; + protected array $pdos; /** * SystemVariableAssigner constructor. @@ -19,15 +19,14 @@ class SystemVariableTemporaryAssigner */ public function __construct(&...$pdos) { - $this->pdos = array_filter($pdos); + $this->pdos = \array_filter($pdos); } /** * Temporarily set MySQL system variables for PDO. * - * @param array $using - * @param callable $callback - * @param array $args + * @param array $using + * @param mixed ...$args * @return $this */ public function using(array $using, callable $callback, ...$args) diff --git a/src/Value.php b/src/Value.php index 12162bf..3a43b2c 100644 --- a/src/Value.php +++ b/src/Value.php @@ -5,25 +5,22 @@ use Closure; use InvalidArgumentException; use ReflectionFunction; +use ReflectionNamedType; class Value implements ValueInterface { use ExpressionTrait; /** - * @var mixed + * @var bool|float|int|string */ protected $value; - /** - * @var string - */ - protected $type; + protected string $type; /** * Create new int value for MySQL system variable. * - * @param int $value * @return static */ public static function int(int $value) @@ -34,7 +31,6 @@ public static function int(int $value) /** * Create new bool value for MySQL system variable. * - * @param bool $value * @return static */ public static function bool(bool $value) @@ -45,7 +41,6 @@ public static function bool(bool $value) /** * Create new float value for MySQL system variable. * - * @param float $value * @return static */ public static function float(float $value) @@ -56,7 +51,6 @@ public static function float(float $value) /** * Create new string value for MySQL system variable. * - * @param string $value * @return static */ public static function str(string $value) @@ -67,21 +61,19 @@ public static function str(string $value) /** * Create new typed value for MySQL system variable. * - * @param string $type - * @param bool|float|int|string $value - * @return \Mpyw\LaravelMySqlSystemVariableManager\ExpressionInterface + * @param bool|float|int|string $value */ public static function as(string $type, $value): ExpressionInterface { switch ($type) { case static::TYPE_INTEGER: - return static::int($value); + return static::int((int)$value); case static::TYPE_BOOLEAN: - return static::bool($value); + return static::bool((bool)$value); case static::TYPE_FLOAT: - return static::float($value); + return static::float((float)$value); case static::TYPE_STRING: - return static::str($value); + return static::str((string)$value); default: throw new InvalidArgumentException('The type must be one of "integer", "boolean", "double" or "string".'); } @@ -92,8 +84,7 @@ public static function as(string $type, $value): ExpressionInterface /** * Automatically wrap a non-null value. * - * @param mixed $value - * @return \Mpyw\LaravelMySqlSystemVariableManager\ExpressionInterface + * @param mixed $value */ public static function wrap($value): ExpressionInterface { @@ -101,16 +92,17 @@ public static function wrap($value): ExpressionInterface return $value; } - if (is_scalar($value)) { + if (\is_scalar($value)) { return static::as(gettype($value), $value); } if ($value instanceof Closure) { /* @noinspection PhpUnhandledExceptionInspection */ $reflector = new ReflectionFunction($value); - if ($reflector->hasReturnType()) { - $returnType = $reflector->getReturnType(); - if (!$returnType->allowsNull() && $type = ExpressionInterface::GRAMMATICAL_TYPE_TO_STRING_TYPE[$returnType->getName()] ?? null) { + $returnType = $reflector->getReturnType(); + if ($returnType instanceof ReflectionNamedType && !$returnType->allowsNull()) { + $type = ExpressionInterface::GRAMMATICAL_TYPE_TO_STRING_TYPE[$returnType->getName()] ?? null; + if ($type) { return Replacer::as($type, $value); } } @@ -122,10 +114,9 @@ public static function wrap($value): ExpressionInterface /** * Value constructor. * - * @param mixed $value - * @param string $type + * @param bool|float|int|string $value */ - protected function __construct($value, string $type) + final protected function __construct($value, string $type) { $this->value = $value; $this->type = $type; diff --git a/tests/BasicVariableAssignmentTest.php b/tests/BasicVariableAssignmentTest.php index 10b2efb..217b492 100644 --- a/tests/BasicVariableAssignmentTest.php +++ b/tests/BasicVariableAssignmentTest.php @@ -11,8 +11,6 @@ class BasicVariableAssignmentTest extends TestCase { /** * @test - * @param string $variableName - * @param bool $emulated * @param mixed $expectedOriginal * @param mixed $newValue * @param mixed $expectedChanged @@ -29,8 +27,6 @@ public function testAssignments(string $variableName, bool $emulated, $expectedO /** * @test - * @param string $variableName - * @param bool $emulated * @param mixed $expectedOriginal * @param mixed $newValue * @param mixed $expectedChanged diff --git a/tests/TestCase.php b/tests/TestCase.php index 670139b..4ebad34 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -7,15 +7,16 @@ use Mpyw\LaravelMySqlSystemVariableManager\MySqlConnectionServiceProvider; use Orchestra\Testbench\TestCase as BaseTestCase; use PDO; +use ReflectionProperty; class TestCase extends BaseTestCase { /** * @param \Illuminate\Foundation\Application $app */ - protected function getEnvironmentSetUp($app) + protected function getEnvironmentSetUp($app): void { - $host = \gethostbyname('mysql') !== 'mysql' // Is "mysql" valid hostname? + $host = gethostbyname('mysql') !== 'mysql' // Is "mysql" valid hostname? ? 'mysql' // Local : '127.0.0.1'; // CI @@ -41,9 +42,9 @@ protected function getEnvironmentSetUp($app) /** * @param \Illuminate\Foundation\Application $app - * @return array + * @return string[] */ - protected function getPackageProviders($app) + protected function getPackageProviders($app): array { return [ MySqlConnectionServiceProvider::class, @@ -64,20 +65,24 @@ protected function onEmulatedConnection(callable $callback): void * @param string $property * @return \Closure|\PDO */ - protected function getConnectionPropertyValue(string $connection, string $property) + protected function getConnectionPropertyValue(?string $connection, string $property) { $db = DB::connection($connection); - $rp = new \ReflectionProperty($db, $property); + $rp = new ReflectionProperty($db, $property); $rp->setAccessible(true); - return $rp->getValue($db); + $value = $rp->getValue($db); + + assert($value instanceof Closure || $value instanceof PDO); + + return $value; } - protected function assertPdoResolved(string $connection): void + protected function assertPdoResolved(?string $connection): void { $this->assertInstanceOf(PDO::class, $this->getConnectionPropertyValue($connection, 'pdo')); } - protected function assertPdoNotResolved(string $connection): void + protected function assertPdoNotResolved(?string $connection): void { $this->assertInstanceOf(Closure::class, $this->getConnectionPropertyValue($connection, 'pdo')); }