diff --git a/AttributeReflection.php b/AttributeReflection.php index c9e5cc9..7c1a14e 100644 --- a/AttributeReflection.php +++ b/AttributeReflection.php @@ -96,15 +96,41 @@ public function isRepeated(): bool return $this->data[Data::AttributeRepeated]; } - public function arguments(): array + /** + * This method returns the actual attribute's constructor arguments and thus might trigger autoloading or throw errors. + */ + public function evaluateArguments(): array { return $this->data[Data::ArgumentsExpression]->evaluate($this->reflector); } - public function newInstance(): object + /** + * @deprecated since 0.4.2 in favor of evaluateArguments() + */ + public function arguments(): array + { + trigger_deprecation('typhoon/reflection', '0.4.2', 'Calling %s is deprecated in favor of %s::evaluateArguments()', __METHOD__, self::class); + + return $this->evaluateArguments(); + } + + /** + * This method returns the actual attribute object and thus might trigger autoloading or throw errors. + */ + public function evaluate(): object { /** @psalm-suppress InvalidStringClass */ - return new ($this->className())(...$this->arguments()); + return new ($this->className())(...$this->evaluateArguments()); + } + + /** + * @deprecated since 0.4.2 in favor of evaluate() + */ + public function newInstance(): object + { + trigger_deprecation('typhoon/reflection', '0.4.2', 'Calling %s is deprecated in favor of %s::evaluate()', __METHOD__, self::class); + + return $this->evaluate(); } private ?AttributeAdapter $native = null; diff --git a/CHANGELOG.md b/CHANGELOG.md index c701843..0e3142c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,23 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] +## [0.4.2] 2024-08-05 + +### Added + +- Add `AttributeReflection::evaluate()` to emphasize the risk of runtime errors. +- Add `AttributeReflection::evaluateArguments()` to emphasize the risk of runtime errors. +- Add `ClassConstantReflection::evaluate()` to emphasize the risk of runtime errors. +- Add `ParameterReflection::evaluateDefault()` to emphasize the risk of runtime errors. +- Add `PropertyReflection::evaluateDefault()` to emphasize the risk of runtime errors. + +### Deprecated + +- Deprecate calling `AttributeReflection::newInstance()` in favor of `evaluate()`. +- Deprecate calling `AttributeReflection::arguments()` in favor of `evaluateArguments()`. +- Deprecate calling `ClassConstantReflection::value()` in favor of `evaluate()`. +- Deprecate calling `ParameterReflection::defaultValue()` in favor of `evaluateDefault()`. +- Deprecate calling `PropertyReflection::defaultValue()` in favor of `evaluateDefault()`. ## [0.4.1] 2024-08-05 diff --git a/ClassConstantReflection.php b/ClassConstantReflection.php index b60e170..f16867b 100644 --- a/ClassConstantReflection.php +++ b/ClassConstantReflection.php @@ -84,7 +84,12 @@ public function class(): ClassReflection return $this->reflector->reflect($this->id->class); } - public function value(): mixed + /** + * This method returns the actual class constant's value and thus might trigger autoloading or throw errors. + * + * @see https://github.com/typhoon-php/typhoon/issues/64 + */ + public function evaluate(): mixed { if ($this->isEnumCase()) { \assert($this->id->class instanceof NamedClassId, 'Enum cannot be an anonymous class'); @@ -95,6 +100,16 @@ public function value(): mixed return $this->data[Data::ValueExpression]->evaluate($this->reflector); } + /** + * @deprecated since 0.4.2 in favor of evaluate() + */ + public function value(): mixed + { + trigger_deprecation('typhoon/reflection', '0.4.2', 'Calling %s is deprecated in favor of %s::evaluate()', __METHOD__, self::class); + + return $this->evaluate(); + } + public function isPrivate(): bool { return $this->data[Data::Visibility] === Visibility::Private; diff --git a/Internal/ConstantExpression/ClassConstantFetch.php b/Internal/ConstantExpression/ClassConstantFetch.php index d401149..9f54c51 100644 --- a/Internal/ConstantExpression/ClassConstantFetch.php +++ b/Internal/ConstantExpression/ClassConstantFetch.php @@ -61,6 +61,6 @@ public function evaluate(?TyphoonReflector $reflector = null): mixed return \constant($class . '::' . $name); } - return $reflector->reflectClass($class)->constants()[$name]->value(); + return $reflector->reflectClass($class)->constants()[$name]->evaluate(); } } diff --git a/Internal/NativeAdapter/AttributeAdapter.php b/Internal/NativeAdapter/AttributeAdapter.php index c351543..0032862 100644 --- a/Internal/NativeAdapter/AttributeAdapter.php +++ b/Internal/NativeAdapter/AttributeAdapter.php @@ -62,7 +62,7 @@ public function __toString(): string public function getArguments(): array { - return $this->reflection->arguments(); + return $this->reflection->evaluateArguments(); } public function getName(): string @@ -93,6 +93,6 @@ public function isRepeated(): bool */ public function newInstance(): object { - return $this->reflection->newInstance(); + return $this->reflection->evaluate(); } } diff --git a/Internal/NativeAdapter/ClassAdapter.php b/Internal/NativeAdapter/ClassAdapter.php index 4dc6eb6..61ebb33 100644 --- a/Internal/NativeAdapter/ClassAdapter.php +++ b/Internal/NativeAdapter/ClassAdapter.php @@ -106,7 +106,7 @@ public function getConstant(string $name): mixed $constant = $this->reflection->constants()[$name] ?? null; - return $constant === null ? false : $constant->value(); + return $constant === null ? false : $constant->evaluate(); } public function getConstants(?int $filter = null): array @@ -115,7 +115,7 @@ public function getConstants(?int $filter = null): array ->reflection ->constants() ->filter(static fn(ClassConstantReflection $constant): bool => $filter === null || ($constant->toNativeReflection()->getModifiers() & $filter) !== 0) - ->map(static fn(ClassConstantReflection $constant): mixed => $constant->value()) + ->map(static fn(ClassConstantReflection $constant): mixed => $constant->evaluate()) ->toArray(); } @@ -129,7 +129,7 @@ public function getDefaultProperties(): array return $this ->nativeProperties() ->filter(static fn(PropertyReflection $property): bool => $property->hasDefaultValue()) - ->map(static fn(PropertyReflection $property): mixed => $property->defaultValue()) + ->map(static fn(PropertyReflection $property): mixed => $property->evaluateDefault()) ->toArray(); } diff --git a/Internal/NativeAdapter/ClassConstantAdapter.php b/Internal/NativeAdapter/ClassConstantAdapter.php index e790e6e..6a56511 100644 --- a/Internal/NativeAdapter/ClassConstantAdapter.php +++ b/Internal/NativeAdapter/ClassConstantAdapter.php @@ -113,7 +113,7 @@ public function getType(): ?\ReflectionType public function getValue(): mixed { - return $this->reflection->value(); + return $this->reflection->evaluate(); } /** diff --git a/Internal/NativeAdapter/ParameterAdapter.php b/Internal/NativeAdapter/ParameterAdapter.php index 278230c..94b06db 100644 --- a/Internal/NativeAdapter/ParameterAdapter.php +++ b/Internal/NativeAdapter/ParameterAdapter.php @@ -97,7 +97,7 @@ public function getDeclaringFunction(): \ReflectionFunctionAbstract public function getDefaultValue(): mixed { - return $this->reflection->defaultValue(); + return $this->reflection->evaluateDefault(); } public function getDefaultValueConstantName(): ?string diff --git a/Internal/NativeAdapter/PropertyAdapter.php b/Internal/NativeAdapter/PropertyAdapter.php index f9ceb75..79155dc 100644 --- a/Internal/NativeAdapter/PropertyAdapter.php +++ b/Internal/NativeAdapter/PropertyAdapter.php @@ -75,7 +75,7 @@ public function getDeclaringClass(): \ReflectionClass public function getDefaultValue(): mixed { - return $this->reflection->defaultValue(); + return $this->reflection->evaluateDefault(); } public function getDocComment(): string|false diff --git a/ParameterReflection.php b/ParameterReflection.php index eeab828..c01a1db 100644 --- a/ParameterReflection.php +++ b/ParameterReflection.php @@ -101,11 +101,24 @@ public function hasDefaultValue(): bool return $this->data[Data::DefaultValueExpression] !== null; } - public function defaultValue(): mixed + /** + * This method returns the actual parameter's default value and thus might trigger autoloading or throw errors. + */ + public function evaluateDefault(): mixed { return $this->data[Data::DefaultValueExpression]?->evaluate($this->reflector); } + /** + * @deprecated since 0.4.2 in favor of evaluateDefault() instead + */ + public function defaultValue(): mixed + { + trigger_deprecation('typhoon/reflection', '0.4.2', 'Calling %s is deprecated in favor of %s::evaluateDefault()', __METHOD__, self::class); + + return $this->evaluateDefault(); + } + public function isNative(): bool { return !$this->isAnnotated(); diff --git a/PropertyReflection.php b/PropertyReflection.php index 29d38be..9919198 100644 --- a/PropertyReflection.php +++ b/PropertyReflection.php @@ -101,11 +101,24 @@ public function isPromoted(): bool return $this->data[Data::Promoted]; } - public function defaultValue(): mixed + /** + * This method returns the actual property's default value and thus might trigger autoloading or throw errors. + */ + public function evaluateDefault(): mixed { return $this->data[Data::DefaultValueExpression]?->evaluate($this->reflector); } + /** + * @deprecated since 0.4.2 in favor of evaluateDefault() + */ + public function defaultValue(): mixed + { + trigger_deprecation('typhoon/reflection', '0.4.2', 'Calling %s is deprecated in favor of %s::evaluateDefault()', __METHOD__, self::class); + + return $this->evaluateDefault(); + } + public function hasDefaultValue(): bool { return $this->data[Data::DefaultValueExpression] !== null; diff --git a/composer.json b/composer.json index 9e7f47a..b49b910 100644 --- a/composer.json +++ b/composer.json @@ -19,6 +19,7 @@ "nikic/php-parser": "^4.18 || ^5.0", "phpstan/phpdoc-parser": "^1.21", "psr/simple-cache": "^3.0", + "symfony/deprecation-contracts": "^3.0", "typhoon/change-detector": "^0.4", "typhoon/declaration-id": "^0.4", "typhoon/type": "^0.4",