From cad5cb8f5e5c08b44d525ce21ec0f4ed5bd8fd71 Mon Sep 17 00:00:00 2001 From: WalterWoshid Date: Sat, 5 Sep 2026 19:52:50 +0200 Subject: [PATCH] Add Mago 1.47.4 quality checks and resolve findings --- .github/workflows/tests.yml | 17 + MAGO.md | 64 ++++ README.md | 2 + composer.json | 11 +- mago-tests.toml | 35 +++ mago.toml | 35 +++ src/Advice/AdviceType.php | 4 +- src/AopKernel.php | 286 +++++++++-------- src/Attributes/After.php | 30 +- src/Attributes/Around.php | 4 +- src/Attributes/Aspect.php | 30 +- src/Attributes/Before.php | 4 +- .../Attributes/AdviceType/MethodAdvice.php | 6 +- src/Core/Attributes/Base/BaseAdvice.php | 2 +- src/Core/Attributes/Base/BaseAttribute.php | 4 +- src/Core/AutoloadInterceptor/ClassLoader.php | 81 ++--- src/Core/Cache/CacheState/WovenCacheState.php | 54 ++-- src/Core/Cache/CacheStateManager.php | 1 + src/Core/Container/AdviceContainer.php | 12 +- src/Core/Container/AdviceContainerFactory.php | 31 +- .../AdviceType/MethodAdviceContainer.php | 19 +- src/Core/Container/AspectManager.php | 76 +++-- .../JoinPoint/MethodJoinPointContainer.php | 11 +- src/Core/Container/JoinPointContainer.php | 33 +- src/Core/Container/TransformerManager.php | 1 + .../Advice/MissingClassNameException.php | 5 +- .../Advice/MissingMethodNameException.php | 8 +- src/Core/Exception/AdviceException.php | 4 +- src/Core/Exception/AopException.php | 4 +- .../Aspect/AspectNotFoundException.php | 4 +- .../InvalidAspectClassNameException.php | 4 +- .../MissingAspectAttributeException.php | 4 +- src/Core/Exception/AspectException.php | 4 +- src/Core/Factory/InvocationFactory.php | 39 +-- src/Core/Intercept/Interceptor.php | 58 ++-- src/Core/Invocation/AdviceChain.php | 27 +- src/Core/JoinPoint/JoinPointHandler.php | 16 +- src/Core/JoinPoint/JoinPointInjector.php | 12 +- src/Core/Matcher/AdviceMatcher.php | 13 +- .../Matcher/AdviceMatcher/MethodMatcher.php | 87 ++---- src/Core/Matcher/AspectMatcher.php | 101 +++--- src/Core/Matcher/ClassMatcher.php | 92 ++---- src/Core/Processor/AspectProcessor.php | 123 ++++---- src/Core/Transform/ProxiedClassModifier.php | 116 +++---- src/Core/Transform/WovenClassBuilder.php | 127 ++++---- src/Invocation/BeforeMethodInvocation.php | 4 +- src/Invocation/MethodInvocation.php | 32 +- src/Invocation/PropertyAccessor.php | 9 +- src/PropertyAccess.php | 83 +++-- tests/ClassLoaderMockTrait.php | 56 ++-- .../AdviceMatchingAbstractMethodTest.php | 5 +- .../Aspect/FileUploaderAspect.php | 7 +- .../AdviceMatchingAbstractMethod/Kernel.php | 1 + .../Aspect/LoggingAspect.php | 15 +- .../ExplicitClassLevelAspectTest.php | 5 +- .../Target/InventoryTracker.php | 1 + .../Aspect/PerformanceAspect.php | 9 +- .../ExplicitMethodLevelAspectTest.php | 4 +- .../ExplicitMethodLevelAspect/Kernel.php | 1 + .../Aspect/SecurityAspect.php | 9 +- ...MultipleExplicitMethodLevelAspectsTest.php | 13 +- .../Target/AccountService.php | 7 +- .../Target/TransactionService.php | 7 +- .../AdviceOrder/AdviceOrderTest.php | 7 +- .../Aspect/ArticleModerationAspect.php | 25 +- .../AdviceBehavior/AdviceOrder/Kernel.php | 1 + .../AdviceOrder/Target/ArticleManager.php | 2 +- .../Aspect/CalculatorLoggerAspect.php | 26 +- ...roundAfterAdviceOnSameAdviceMethodTest.php | 16 +- .../Kernel.php | 1 + .../Aspect/PaymentProcessorAspect.php | 39 +-- ...roundAfterAdviceOnSameTargetMethodTest.php | 38 ++- .../Kernel.php | 1 + .../Target/PaymentProcessor.php | 1 + .../Aspect/NotificationAspect.php | 7 +- .../ClassHierarchyAspectTest.php | 15 +- .../ClassHierarchyAspect/Kernel.php | 1 + .../Aspect/CommentFilterAspect.php | 2 + .../ExceptionInsideAdviceTest.php | 2 +- .../ExceptionInsideAdvice/Kernel.php | 1 + .../Include/Aspect/DatabaseModifierAspect.php | 45 ++- .../AdviceBehavior/Include/Database/data.php | 14 +- .../AdviceBehavior/Include/IncludeTest.php | 82 ++--- .../AdviceBehavior/Include/Kernel.php | 33 +- .../Include/Target/SecureDatabaseService.php | 56 ++-- .../Aspect/UserInterfaceAspect.php | 6 +- .../InterfaceAdvice/InterfaceAdviceTest.php | 4 +- .../AdviceBehavior/InterfaceAdvice/Kernel.php | 1 + .../MagicConstants/Aspect/AspectOnClass.php | 5 +- .../MagicConstants/Aspect/AspectOnParent.php | 5 +- .../MagicConstants/Aspect/AspectOnTrait.php | 5 +- .../MagicConstants/Kernel/KernelOnClass.php | 1 + .../Kernel/KernelOnClassAndParent.php | 1 + .../Kernel/KernelOnClassAndParentAndTrait.php | 1 + .../Kernel/KernelOnClassAndTrait.php | 1 + .../MagicConstants/Kernel/KernelOnParent.php | 1 + .../Kernel/KernelOnParentAndTrait.php | 1 + .../MagicConstants/Kernel/KernelOnTrait.php | 1 + .../MagicConstants/MagicConstantsTest.php | 221 ++++++------- .../MagicConstants/Target/TargetClass.php | 108 +++---- .../MagicConstants/Target/TargetClass82.php | 108 +++---- .../MagicConstants/Target/TargetParent.php | 108 +++---- .../MagicConstants/Target/TargetParent82.php | 108 +++---- .../MagicConstants/Target/TargetTrait.php | 88 +++--- .../MagicConstants/Target/TargetTrait82.php | 109 +++---- .../Aspect/NumberHelperAspect.php | 9 +- .../AdviceBehavior/ModifyArgument/Kernel.php | 1 + .../ModifyArgument/ModifyArgumentTest.php | 4 +- .../ModifyArgument/Target/NumberHelper.php | 1 + .../Aspect/AddMetadataToArrayAspect.php | 46 ++- .../Kernel.php | 33 +- .../ModifyArgumentPassedByReferenceTest.php | 62 ++-- .../Target/ArrayCreator.php | 23 +- .../Aspect/ProfilePictureValidatorAspect.php | 19 +- .../Kernel.php | 1 + ...thSameAdviceTypeOnSameTargetMethodTest.php | 41 +-- .../Target/ProfileController.php | 1 + .../Aspect/ModifyGroupPolicyAspect.php | 39 +-- .../Kernel.php | 33 +- ...NewClassCreationWithProxiedClassesTest.php | 140 +++++---- .../Target/GroupMemberService.php | 36 +-- .../Target/GroupPolicy.php | 22 +- .../Aspect/DefaultAspect.php | 8 +- .../Aspect/OnlyPublicMethodsAspect.php | 3 +- .../OnlyPublicMethods/Kernel.php | 1 + .../OnlyPublicMethodsTest.php | 2 +- .../OnlyPublicMethods/Target/TargetClass.php | 4 +- .../Target/TargetParentClass.php | 5 +- .../OnlyPublicMethods/Target/TargetTrait.php | 5 +- .../PrivateProperties/EverythingAspect.php | 1 + .../PrivateProperties/Kernel.php | 2 + .../PrivatePropertiesTest.php | 174 ++++++----- .../PrivateProperties/Target/ChildInput.php | 7 +- .../PrivateProperties/Target/MagicInput.php | 27 +- .../PrivateProperties/Target/ParentInput.php | 16 +- .../Target/PromotedInput.php | 11 +- .../Target/PublicGrandchild.php | 1 + .../PrivateProperties/Target/PublicInput.php | 13 +- .../Target/SameTypeInput.php | 9 +- .../Target/SharedStaticChild.php | 1 + .../Target/SharedStaticParent.php | 7 +- .../PrivateProperties/Target/StaticChild.php | 7 +- .../PrivateProperties/Target/StaticParent.php | 9 +- .../PrivateProperties/Target/TokenTrait.php | 7 +- .../PrivateProperties/Target/TraitInput.php | 1 + .../Aspect/BankingAspect.php | 17 +- .../ProtectedAndPrivateMethods/Kernel.php | 1 + .../ProtectedAndPrivateMethodsTest.php | 10 +- .../Target/BankingSystem.php | 4 +- .../Readonly/Aspect/ReadonlyAspect.php | 30 +- .../AdviceBehavior/Readonly/Kernel.php | 33 +- .../AdviceBehavior/Readonly/ReadonlyTest.php | 97 +++--- .../Readonly/Target/ReadonlyClass.php | 17 +- .../Target/ReadonlyPromotedProperties.php | 22 +- .../TraitAdvice/Aspect/RouteCachingAspect.php | 10 +- .../AdviceBehavior/TraitAdvice/Kernel.php | 1 + .../TraitAdvice/Target/RouteCaching.php | 1 + .../TraitAdvice/TraitAdviceTest.php | 4 +- .../Aspect/StringPrefixerAspect.php | 8 +- .../VariadicParameters/Target/IdHelper.php | 2 +- .../VariadicParametersTest.php | 4 +- ...eMatchingMultipleClassesAndMethodsTest.php | 8 +- .../Aspect/DiscountAspect.php | 10 +- .../Kernel.php | 1 + .../ClassHierarchyOnlyInvokedOnce/Aspect.php | 3 +- .../ClassHierarchyOnlyInvokedOnceTest.php | 2 +- .../ClassHierarchyOnlyInvokedOnce/Kernel.php | 1 + .../Target/TargetClassA.php | 4 +- .../Target/TargetClassB.php | 4 +- .../Target/TargetClassC.php | 2 +- .../AdviceInterceptTraitMethodsTest.php | 2 +- .../Aspect/DefaultAspect.php | 6 +- .../Aspect/InterceptTraitMethodsAspect.php | 1 + .../InterceptTraitMethods/Kernel.php | 1 + .../Target/TargetClass.php | 2 +- .../Target/TargetTrait.php | 3 +- .../SelfType/Aspect/SalaryIncreaserAspect.php | 12 +- .../AspectMatching/SelfType/Kernel.php | 1 + .../AspectMatching/SelfType/SelfTypeTest.php | 31 +- .../SelfType/Target/Employee.php | 4 + .../SelfType/Target/PartTimeEmployee.php | 4 +- .../InvalidAspect/Aspect/InvalidAspect.php | 5 +- .../Kernel/InvalidAspectClassKernel.php | 1 + .../Kernel/InvalidAspectClassNameKernel.php | 1 + .../Kernel/InvalidAspectsTypeKernel.php | 1 + .../Aspect/AddItemLoggerAspect.php | 12 +- .../Aspect/GetQuantityLoggerAspect.php | 9 +- .../Aspect/RemoveItemLoggerAspect.php | 11 +- .../Kernel/AddItemKernel.php | 1 + .../Kernel/GetQuantityKernel.php | 1 + .../Kernel/RemoveItemKernel.php | 1 + .../MissingClassOrMethodTest.php | 15 +- .../Target/InventoryManager.php | 2 + .../Aspect.php | 5 +- .../CustomDependencyInjectionHandlerTest.php | 11 +- .../Kernel.php | 5 +- .../Aspect/FixWrongReturnValueAspect.php | 9 +- .../TransformerAndAspect/Kernel.php | 6 +- ...hp => IncorrectFunctionAndReturnClass.php} | 5 +- ...hp => FixIncorrectFunctionTransformer.php} | 16 +- .../TransformerAndAspectTest.php | 20 +- .../Aspect.php | 7 +- .../Kernel.php | 13 +- .../Transformer.php | 16 +- ...ndAspectDependencyInjectionHandlerTest.php | 16 +- tests/Performance/Aspect/AddOneAspect.php | 7 +- .../Kernel/MeasurePerformanceKernel.php | 1 + tests/Performance/MeasurePerformanceTest.php | 291 ++++++++++-------- tests/Performance/Service/NumbersService.php | 2 +- .../Service/NumbersServiceInterface.php | 10 + tests/Stubs/Etc/Logger.php | 2 + tests/Stubs/Etc/MailQueue.php | 2 + tests/Stubs/Etc/StackTrace.php | 2 + tests/Stubs/Kernel/EmptyKernel.php | 1 + tests/Util.php | 38 +-- tools/mago/check.php | 101 ++++++ tools/mago/patches/CodeTransformerKernel.php | 20 ++ tools/mago/tests.toml | 9 + tools/mago/tests/invalid/ScalarCallback.php | 14 + .../mago/tests/invalid/WrongBaseCallback.php | 16 + tools/mago/tests/invalid/WrongManager.php | 13 + tools/mago/tests/invalid/WrongReturn.php | 17 + tools/mago/tests/valid/ExampleAspect.php | 5 + tools/mago/tests/valid/ExampleTransformer.php | 16 + tools/mago/tests/valid/Valid.php | 18 ++ tools/mago/tests/valid/ValidManager.php | 14 + 226 files changed, 2811 insertions(+), 2439 deletions(-) create mode 100644 MAGO.md create mode 100644 mago-tests.toml create mode 100644 mago.toml rename tests/Integration/TransformerAndAspect/Target/{DeprecatedAndWrongClass.php => IncorrectFunctionAndReturnClass.php} (55%) rename tests/Integration/TransformerAndAspect/Transformer/{FixDeprecatedFunctionTransformer.php => FixIncorrectFunctionTransformer.php} (53%) create mode 100644 tests/Performance/Service/NumbersServiceInterface.php create mode 100644 tools/mago/check.php create mode 100644 tools/mago/patches/CodeTransformerKernel.php create mode 100644 tools/mago/tests.toml create mode 100644 tools/mago/tests/invalid/ScalarCallback.php create mode 100644 tools/mago/tests/invalid/WrongBaseCallback.php create mode 100644 tools/mago/tests/invalid/WrongManager.php create mode 100644 tools/mago/tests/invalid/WrongReturn.php create mode 100644 tools/mago/tests/valid/ExampleAspect.php create mode 100644 tools/mago/tests/valid/ExampleTransformer.php create mode 100644 tools/mago/tests/valid/Valid.php create mode 100644 tools/mago/tests/valid/ValidManager.php diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a8cd097..b980e19 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -19,6 +19,23 @@ permissions: contents: read jobs: + quality: + name: Mago formatter, linter and analyzer + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v3 + - uses: shivammathur/setup-php@v2 + with: + php-version: '8.2' + coverage: none + - name: Install development dependencies + run: composer install --prefer-dist --no-progress --no-interaction + - name: Run formatter, linter and analyzer without a baseline + run: composer check + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: runs-on: ${{ matrix.operating-system }} diff --git a/MAGO.md b/MAGO.md new file mode 100644 index 0000000..f336389 --- /dev/null +++ b/MAGO.md @@ -0,0 +1,64 @@ +# Mago checks + +Install development dependencies with `composer install`, then run: + +```sh +composer check # formatting, linter, analyzer for source and tests +composer format # apply formatting +composer lint +composer analyze +``` + +Mago is pinned to **1.47.4**, the newest published version verified for this upgrade. +Update the pin deliberately and run the full checks before upgrading. Composer installs the +platform-specific launcher; its first invocation downloads the matching binary. +CI provides `GITHUB_TOKEN` for that download. Scripts invoke the project-local PHP +launcher explicitly so an older system-wide binary cannot be used accidentally. +There is no baseline. + +The formatter, linter, and analyzer have been run successfully with 1.47.4 against +both configurations. On Windows, the Composer launcher needs PHP's ZIP extension +and a configured trusted CA certificate bundle to download and extract its binary. + +`mago.toml` targets PHP 8.1 for library code. `mago-tests.toml` targets PHP 8.2 because +some fixtures intentionally exercise readonly classes and traits; PHPUnit skips +the applicable tests on PHP 8.1. Both configurations read dependency declarations +without checking vendor code. Generated AOP cache and coverage files are excluded. + +The linter uses its default correctness, consistency, and maintainability checks. +Exceptions in configuration are explicit design choices: mandatory strict types +would change coercion behavior; named arguments and boolean flags are existing API +choices; `isset` intentionally distinguishes null; aggregate complexity metrics +are left to review. Analyzer missing-type checks are enabled. CI fails on all +reported severities. No analyzer issue codes are ignored globally. + +The vendor type patch in `tools/mago/patches/CodeTransformerKernel.php` models its +dependency injection callback as a generic extension point. The default remains +the original one-argument callback returning a Transformer. AopKernel specializes +it to its established two-argument callback returning an aspect or transformer. +This replaces the previous callback suppression without changing runtime code or +loosening TransformerManager's contract. `composer check-mago-types` checks valid +callbacks and rejects scalar returns and incorrect default transformer callbacks. +It runs as part of `composer check`, including in CI. Its deliberately invalid +fixtures live in `tools/mago/tests` and are checked separately from library code. + +Two narrow `@mago-expect` annotations still cover four property-access diagnostics. +These are explicit analyzer exceptions, not fixes to the analyzer's limitations: + +- Two property-access closures operate on a declaration validated through reflection. + Mago cannot prove a runtime property name exists on the subject. Reference, scope, + uninitialized-property, and unset behavior have runtime regression tests. + +These expectations name individual diagnostics at the affected operations. Mago +reports an unfulfilled expectation if a future release stops producing a diagnostic, +so stale exceptions fail CI. No baseline, file exclusions for these operations, or +global analyzer diagnostic suppressions are used. + +An extension experiment confirmed that Mago 1.47.4 does not dispatch property type +providers for these unresolved runtime operations. The experiment was discarded; +no custom extension or issue filter is installed. + +Performance fixture generation now uses a shared service interface and validates +generated service instantiation through reflection. Its construction work is part +of the class-loading measurement; compare loading results only against runs using +the same harness. The timed method-execution loop is unchanged. diff --git a/README.md b/README.md index c8196c6..e216841 100644 --- a/README.md +++ b/README.md @@ -759,6 +759,8 @@ $firstLog = $logs[0]; ## Testing + +Development quality checks: see [Mago setup and commands](MAGO.md). - Run `composer run-script test`
or - Run `composer run-script test-coverage` diff --git a/composer.json b/composer.json index 523c88d..88c443d 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,13 @@ "scripts": { "test": "phpunit --testsuite=Tests --display-notices", "test-performance": "phpunit --testsuite=Performance --display-notices", - "test-coverage": "phpunit --testsuite=Tests --coverage-html tests/coverage --display-notices" + "test-coverage": "phpunit --testsuite=Tests --coverage-html tests/coverage --display-notices", + "format": ["@php vendor/bin/mago format", "@php vendor/bin/mago --config mago-tests.toml format"], + "format-check": ["@php vendor/bin/mago format --check", "@php vendor/bin/mago --config mago-tests.toml format --check"], + "lint": ["@php vendor/bin/mago lint --minimum-fail-level note", "@php vendor/bin/mago --config mago-tests.toml lint --minimum-fail-level note"], + "analyze": ["@php vendor/bin/mago analyze --minimum-fail-level note", "@php vendor/bin/mago --config mago-tests.toml analyze --minimum-fail-level note"], + "check-mago-types": "@php tools/mago/check.php", + "check": ["@format-check", "@lint", "@analyze", "@check-mago-types"] }, "require": { "php": ">=8.1", @@ -30,7 +36,8 @@ "okapi/singleton": "^1.0", "php-di/php-di": "^7.0" }, - "require-dev": { + "require-dev": { + "carthage-software/mago": "1.47.4", "phpunit/phpunit": "^10.3", "symfony/var-dumper": "^6.3", "symfony/console": "^6.3" diff --git a/mago-tests.toml b/mago-tests.toml new file mode 100644 index 0000000..f4b7ad0 --- /dev/null +++ b/mago-tests.toml @@ -0,0 +1,35 @@ +php-version = "8.2" + +[source] +paths = ["tests"] +includes = ["src", "vendor"] +patches = ["tools/mago/patches"] +excludes = ["tests/cache", "tests/coverage"] + +[linter] +integrations = ["phpunit"] + +# Preserve the library's coercion contract and existing public call signatures. +[linter.rules.strict-types] +enabled = false +[linter.rules.literal-named-argument] +enabled = false +[linter.rules.no-boolean-flag-parameter] +enabled = false +# isset intentionally distinguishes null from a usable value throughout this code. +[linter.rules.no-isset] +enabled = false +# Complexity metrics are review aids, not correctness gates for a weaving engine. +[linter.rules.halstead] +enabled = false +[linter.rules.kan-defect] +enabled = false +[linter.rules.cyclomatic-complexity] +enabled = false +[linter.rules.too-many-methods] +enabled = false +[linter.rules.excessive-parameter-list] +threshold = 10 + +[analyzer] +check-missing-type-hints = true diff --git a/mago.toml b/mago.toml new file mode 100644 index 0000000..6744327 --- /dev/null +++ b/mago.toml @@ -0,0 +1,35 @@ +php-version = "8.1" + +[source] +paths = ["src", "tools/mago/check.php"] +includes = ["vendor"] +patches = ["tools/mago/patches"] +excludes = ["tests/cache", "tests/coverage"] + +[linter] +integrations = ["phpunit"] + +# Preserve the library's coercion contract and existing public call signatures. +[linter.rules.strict-types] +enabled = false +[linter.rules.literal-named-argument] +enabled = false +[linter.rules.no-boolean-flag-parameter] +enabled = false +# isset intentionally distinguishes null from a usable value throughout this code. +[linter.rules.no-isset] +enabled = false +# Complexity metrics are review aids, not correctness gates for a weaving engine. +[linter.rules.halstead] +enabled = false +[linter.rules.kan-defect] +enabled = false +[linter.rules.cyclomatic-complexity] +enabled = false +[linter.rules.too-many-methods] +enabled = false +[linter.rules.excessive-parameter-list] +threshold = 10 + +[analyzer] +check-missing-type-hints = true diff --git a/src/Advice/AdviceType.php b/src/Advice/AdviceType.php index 132693a..e41aa5b 100644 --- a/src/Advice/AdviceType.php +++ b/src/Advice/AdviceType.php @@ -12,8 +12,8 @@ enum AdviceType case Before; case Around; case After; - // TODO: implement + // Reserved for future after-returning advice support. case AfterReturning; - // TODO: implement + // Reserved for future after-throwing advice support. case AfterThrowing; } diff --git a/src/AopKernel.php b/src/AopKernel.php index 1a79ea5..ff7fe55 100644 --- a/src/AopKernel.php +++ b/src/AopKernel.php @@ -1,149 +1,137 @@ -Default: ROOT_DIR/cache/aop
- * - * @var string|null - */ - protected ?string $cacheDir = null; - - /** - * The exclude paths. Paths/directories in this array will be excluded - * - * @var array - */ - protected array $excludePaths = []; - - // endregion - - /** - * List of aspects to be applied. - * - * Class should be annotated with #[{@link Aspect}] attribute. - * - * @var class-string[] - */ - protected array $aspects = []; - - /** - * @inheritDoc - */ - protected static function registerDependencyInjection(): void - { - parent::registerDependencyInjection(); - - // Overload classes for extending the functionality - DI::set(CodeTransformerOptions::class, decorate(function () { - return DI::get(Options::class); - })); - DI::set(CodeTransformerCachePaths::class, decorate(function () { - return DI::get(CachePaths::class); - })); - DI::set( - CodeTransformerClassLoader::class, - decorate(function (CodeTransformerClassLoader $previous) { - return DI::make(ClassLoader::class, [ - 'originalClassLoader' => $previous->originalClassLoader, - ]); - }), - ); - DI::set(TransformerProcessor::class, decorate(function () { - return DI::get(AspectProcessor::class); - })); - DI::set(CodeTransformerCacheStateFactory::class, decorate(function () { - return DI::get(CacheStateFactory::class); - })); - DI::set(CodeTransformerCacheStateManager::class, decorate(function () { - return DI::get(CacheStateManager::class); - })); - DI::set(CodeTransformerTransformerManager::class, decorate(function () { - return DI::get(TransformerManager::class); - })); - } - - /** - * @inheritdoc - */ - protected function preInit(): void - { - // Add the aspects - $this->aspectManager->addAspects($this->aspects); - - parent::preInit(); - } - - /** - * Custom dependency injection handler. - * - * Pass a closure that takes an aspect/transformer class name as the - * argument and returns an aspect/transformer instance. - * - * @return null|Closure(class-string, ComponentType): object - */ - protected function dependencyInjectionHandler(): ?Closure - { - // Override this method to configure the dependency injection handler - - return null; - } - - protected function registerServices(): void - { - $this->aspectManager->registerCustomDependencyInjectionHandler( - $this->dependencyInjectionHandler(), - ); - $this->aspectManager->register(); - - parent::registerServices(); - } -} + + */ +abstract class AopKernel extends CodeTransformerKernel +{ + // region DI + + #[Inject] + private AspectManager $aspectManager; + + // endregion + + // region Settings + + /** + * The cache directory. + *
Default: ROOT_DIR/cache/aop
+ * + * @var string|null + */ + protected ?string $cacheDir = null; + + /** + * The exclude paths. Paths/directories in this array will be excluded + * + * @var array + */ + protected array $excludePaths = []; + + // endregion + + /** + * List of aspects to be applied. + * + * Class should be annotated with #[{@link Aspect}] attribute. + * + * @var class-string[] + */ + protected array $aspects = []; + + /** + * @inheritDoc + */ + protected static function registerDependencyInjection(): void + { + parent::registerDependencyInjection(); + + // Overload classes for extending the functionality + DI::set(CodeTransformerOptions::class, decorate(static fn() => DI::get(Options::class))); + DI::set(CodeTransformerCachePaths::class, decorate(static fn() => DI::get(CachePaths::class))); + DI::set(CodeTransformerClassLoader::class, decorate(static fn(CodeTransformerClassLoader $previous) => DI::make(ClassLoader::class, [ + 'originalClassLoader' => $previous->originalClassLoader, + ]))); + DI::set(TransformerProcessor::class, decorate(static fn() => DI::get(AspectProcessor::class))); + DI::set(CodeTransformerCacheStateFactory::class, decorate(static fn() => DI::get(CacheStateFactory::class))); + DI::set(CodeTransformerCacheStateManager::class, decorate(static fn() => DI::get(CacheStateManager::class))); + DI::set(CodeTransformerTransformerManager::class, decorate(static fn() => DI::get(TransformerManager::class))); + } + + /** + * @inheritdoc + */ + protected function preInit(): void + { + // Add the aspects + $this->aspectManager->addAspects($this->aspects); + + parent::preInit(); + } + + /** + * Custom dependency injection handler. + * + * Pass a closure that takes an aspect/transformer class name as the + * argument and returns an aspect/transformer instance. + * + * @return null|Closure(class-string, ComponentType): object + */ + protected function dependencyInjectionHandler(): ?Closure + { + // Override this method to configure the dependency injection handler + + return null; + } + + protected function registerServices(): void + { + $this->aspectManager->registerCustomDependencyInjectionHandler($this->dependencyInjectionHandler()); + $this->aspectManager->register(); + + parent::registerServices(); + } +} diff --git a/src/Attributes/After.php b/src/Attributes/After.php index 3ec09cd..8089dff 100644 --- a/src/Attributes/After.php +++ b/src/Attributes/After.php @@ -1,16 +1,14 @@ -class = $class ? Regex::fromWildcard($class) : null; diff --git a/src/Core/Attributes/Base/BaseAttribute.php b/src/Core/Attributes/Base/BaseAttribute.php index bae4f8c..caa676e 100644 --- a/src/Core/Attributes/Base/BaseAttribute.php +++ b/src/Core/Attributes/Base/BaseAttribute.php @@ -7,6 +7,4 @@ * * Base attribute for all AOP attributes. */ -abstract class BaseAttribute -{ -} +abstract class BaseAttribute {} diff --git a/src/Core/AutoloadInterceptor/ClassLoader.php b/src/Core/AutoloadInterceptor/ClassLoader.php index 7b2ccbf..4c42fc5 100644 --- a/src/Core/AutoloadInterceptor/ClassLoader.php +++ b/src/Core/AutoloadInterceptor/ClassLoader.php @@ -1,4 +1,5 @@ originalClassLoader->findFile($namespacedClass); @@ -57,9 +59,15 @@ public function findFile($namespacedClass): false|string } $filePath = Path::resolve($filePath); + if (!is_string($filePath)) { + return false; + } - foreach ($this->options->getExcludePaths() as $path) { - if (str_starts_with($filePath, Path::resolve($path))) { + /** @var string[] $excludePaths */ + $excludePaths = $this->options->getExcludePaths(); + foreach ($excludePaths as $path) { + $resolvedPath = Path::resolve($path); + if (is_string($resolvedPath) && str_starts_with($filePath, $resolvedPath)) { return $filePath; } } @@ -67,63 +75,30 @@ public function findFile($namespacedClass): false|string // Query cache state $cacheState = $this->cacheStateManager->queryCacheState($filePath); - // When debugging, bypass the caching mechanism - if ($this->options->isDebug()) { - // ... - } - - // In production mode, use the cache without checking if it is fresh - elseif ($this->options->getEnvironment() === Environment::PRODUCTION - && $cacheState - ) { - // Use the cached file if aspects have been applied - if ($cacheFilePath = $cacheState->getFilePath()) { - $this->classContainer->addClassContext( - $filePath, - $namespacedClass, - $cacheFilePath, - ); - - // For cached files, the debugger will have trouble finding the - // original file, that's why we rewrite the file path with a PHP - // stream filter - /** @see CachedStreamFilter::filter() */ - return $this->filterInjector->rewriteCached($filePath); - } - - // Or return the original file if no aspects have been applied - return $filePath; - } - - // In development mode, check if the cache is fresh - elseif ($this->options->getEnvironment() === Environment::DEVELOPMENT - && $cacheState - && $cacheState->isFresh() - ) { - if ($cacheFilePath = $cacheState->getFilePath()) { - $this->classContainer->addClassContext( - $filePath, - $namespacedClass, - $cacheFilePath, - ); - + // Production trusts the cache; development checks freshness. + $useCache = + !$this->options->isDebug() + && $cacheState !== null + && ( + $this->options->getEnvironment() === Environment::PRODUCTION + || $this->options->getEnvironment() === Environment::DEVELOPMENT + && $cacheState->isFresh() + ); + if ($useCache) { + $cacheFilePath = $cacheState->getFilePath(); + if ($cacheFilePath) { + $this->classContainer->addClassContext($filePath, $namespacedClass, $cacheFilePath); + // Preserve the original source path for debuggers. return $this->filterInjector->rewriteCached($filePath); } - return $filePath; } - // Match the aspects - $matchedAspects = $this->aspectMatcher->matchByClassLoaderAndStore( - $namespacedClass, - ); + $matchedAspects = $this->aspectMatcher->matchByClassLoaderAndStore($namespacedClass); // Match the transformer - $matchedTransformers = $this->transformerMatcher->matchAndStore( - $namespacedClass, - $filePath, - ); + $matchedTransformers = $this->transformerMatcher->matchAndStore($namespacedClass, $filePath); // No aspects or transformers matched if (!($matchedAspects || $matchedTransformers)) { @@ -167,7 +142,7 @@ protected function isInternal(string $namespacedClass): bool 'Okapi\Aop\AopKernel', 'Okapi\Aop\Tests\\', 'Nette\PhpGenerator\Factory', - 'Nette\Utils\Reflection' + 'Nette\Utils\Reflection', ], ); } diff --git a/src/Core/Cache/CacheState/WovenCacheState.php b/src/Core/Cache/CacheState/WovenCacheState.php index 744461f..a3ae847 100644 --- a/src/Core/Cache/CacheState/WovenCacheState.php +++ b/src/Core/Cache/CacheState/WovenCacheState.php @@ -1,4 +1,5 @@ proxyFilePath)) { // @codeCoverageIgnoreStart return false; + // @codeCoverageIgnoreEnd } @@ -78,17 +82,16 @@ public function isFresh(): bool if (!file_exists($this->wovenFilePath)) { // @codeCoverageIgnoreStart return false; + // @codeCoverageIgnoreEnd } - $transformerAndAspectFilePaths = array_merge( - $this->transformerFilePaths, - $this->aspectFilePaths, - ); + $transformerAndAspectFilePaths = array_merge($this->transformerFilePaths, $this->aspectFilePaths); foreach ($transformerAndAspectFilePaths as $filePath) { if (!file_exists($filePath)) { // @codeCoverageIgnoreStart return false; + // @codeCoverageIgnoreEnd } } @@ -107,10 +110,7 @@ public function getFilePath(): ?string } // Add the cached advice containers to the aspect matcher - $this->aspectMatcher->addMatchedAdviceContainers( - $this->namespacedClass, - $this->getAdviceContainers(), - ); + $this->aspectMatcher->addMatchedAdviceContainers($this->namespacedClass, $this->getAdviceContainers()); return $this->proxyFilePath; } @@ -118,12 +118,10 @@ public function getFilePath(): ?string /** * Get the advice containers for the given advice names. * - * @return array + * @return \Okapi\Aop\Core\Container\AdviceContainer[] */ private function getAdviceContainers(): array { - return $this->aspectManager->getAdviceContainersByAdviceNames( - $this->adviceNames, - ); + return $this->aspectManager->getAdviceContainersByAdviceNames($this->adviceNames); } } diff --git a/src/Core/Cache/CacheStateManager.php b/src/Core/Cache/CacheStateManager.php index 5ac1cf7..ab434f6 100644 --- a/src/Core/Cache/CacheStateManager.php +++ b/src/Core/Cache/CacheStateManager.php @@ -1,4 +1,5 @@ newInstance(); // Check if the aspect are implicit or class/method-level explicit - $isExplicit = (bool)$aspectRefClass->getAttributes(Attribute::class); + $isExplicit = (bool) $aspectRefClass->getAttributes(Attribute::class); if ($adviceAttributeInstance instanceof MethodAdvice) { $methodAdviceContainer = DI::make(MethodAdviceContainer::class, [ - 'aspectClassName' => $aspectClassName, - 'aspectInstance' => $aspectInstance, - 'aspectRefClass' => $aspectRefClass, - 'adviceAttribute' => $adviceAttribute, + 'aspectClassName' => $aspectClassName, + 'aspectInstance' => $aspectInstance, + 'aspectRefClass' => $aspectRefClass, + 'adviceAttribute' => $adviceAttribute, 'adviceAttributeInstance' => $adviceAttributeInstance, - 'adviceRefMethod' => $adviceRefMethod, - 'isExplicit' => $isExplicit, + 'adviceRefMethod' => $adviceRefMethod, + 'isExplicit' => $isExplicit, ]); // If the aspect is implicit, // check if the class and method names are set if (!$isExplicit) { if (!$adviceAttributeInstance->class) { - throw new MissingClassNameException( - $methodAdviceContainer->getName(), - ); + throw new MissingClassNameException($methodAdviceContainer->getName()); } if (!$adviceAttributeInstance->method) { - throw new MissingMethodNameException( - $methodAdviceContainer->getName(), - ); + throw new MissingMethodNameException($methodAdviceContainer->getName()); } } return $methodAdviceContainer; } + throw new \LogicException('Unsupported advice type.'); } } diff --git a/src/Core/Container/AdviceType/MethodAdviceContainer.php b/src/Core/Container/AdviceType/MethodAdviceContainer.php index c22c1fa..49359d5 100644 --- a/src/Core/Container/AdviceType/MethodAdviceContainer.php +++ b/src/Core/Container/AdviceType/MethodAdviceContainer.php @@ -16,7 +16,7 @@ * * This class is used to store method advice information. * - * @property-read MethodAdvice $adviceAttributeInstance + * @extends AdviceContainer */ class MethodAdviceContainer extends AdviceContainer { @@ -39,13 +39,13 @@ class MethodAdviceContainer extends AdviceContainer * @param bool $isExplicit */ public function __construct( - string $aspectClassName, - object $aspectInstance, - BaseReflectionClass $aspectRefClass, - BaseReflectionAttribute $adviceAttribute, - MethodAdvice $adviceAttributeInstance, + string $aspectClassName, + object $aspectInstance, + BaseReflectionClass $aspectRefClass, + BaseReflectionAttribute $adviceAttribute, + MethodAdvice $adviceAttributeInstance, public readonly BaseReflectionMethod $adviceRefMethod, - private readonly bool $isExplicit, + private readonly bool $isExplicit, ) { parent::__construct( $aspectClassName, @@ -63,9 +63,8 @@ public function __construct( * * @return void */ - public function addMatchedMethod( - BetterReflectionMethod $matchedRefMethod, - ): void { + public function addMatchedMethod(BetterReflectionMethod $matchedRefMethod): void + { $this->matchedMethods[] = DI::make(MatchedMethod::class, [ 'matchedRefMethod' => $matchedRefMethod, ]); diff --git a/src/Core/Container/AspectManager.php b/src/Core/Container/AspectManager.php index 43eaf67..c7d7137 100644 --- a/src/Core/Container/AspectManager.php +++ b/src/Core/Container/AspectManager.php @@ -1,11 +1,10 @@ aspects = array_merge( - $this->aspects, - $aspectClasses, - ); + $this->aspects = array_merge($this->aspects, $aspectClasses); } // endregion @@ -84,9 +79,8 @@ public function addAspects(array $aspectClasses): void /** * @param null|(Closure(class-string, ComponentType): object) $dependencyInjectionHandler */ - public function registerCustomDependencyInjectionHandler( - ?Closure $dependencyInjectionHandler - ): void { + public function registerCustomDependencyInjectionHandler(?Closure $dependencyInjectionHandler): void + { $this->dependencyInjectionHandler = $dependencyInjectionHandler; } @@ -130,46 +124,36 @@ public function loadAspect(mixed $aspectClassName): void if (array_key_exists($aspectClassName, $this->aspectAdviceContainers)) { // @codeCoverageIgnoreStart return; + // @codeCoverageIgnoreEnd } // Validate the aspect if (gettype($aspectClassName) !== 'string') { - throw new InvalidAspectClassNameException; + throw new InvalidAspectClassNameException(); } - // Instantiate the aspect - if ($this->dependencyInjectionHandler) { - $aspectInstance = ($this->dependencyInjectionHandler)( - $aspectClassName, - ComponentType::ASPECT, - ); - } else { - try { - $aspectInstance = DI::make($aspectClassName); - } catch (Error|Exception) { - throw new AspectNotFoundException($aspectClassName); - } - } + // Instantiate the aspect. + $aspectInstance = $this->instantiateAspect($aspectClassName); // Create a reflection of the aspect $aspectRefClass = new BaseReflectionClass($aspectInstance); // Validate the aspect attribute - $attributes = $aspectRefClass->getAttributes( - Aspect::class, - BaseReflectionAttribute::IS_INSTANCEOF, - ); + $attributes = $aspectRefClass->getAttributes(Aspect::class, BaseReflectionAttribute::IS_INSTANCEOF); if (!$attributes) { throw new MissingAspectAttributeException($aspectClassName); } // Iterate over the aspect methods and properties - $methods = $aspectRefClass->getMethods(); + $methods = $aspectRefClass->getMethods(); $properties = $aspectRefClass->getProperties(); /** @var (BaseReflectionMethod|BaseReflectionProperty)[] $adviceRefMembers */ $adviceRefMembers = array_merge($methods, $properties); foreach ($adviceRefMembers as $adviceRefMember) { + if (!$adviceRefMember instanceof BaseReflectionMethod) { + continue; + } // Get the advices $adviceAttributes = $adviceRefMember->getAttributes( BaseAdvice::class, @@ -186,12 +170,25 @@ public function loadAspect(mixed $aspectClassName): void $adviceRefMember, ); - $this->aspectAdviceContainers[$aspectClassName][] = $adviceContainer; + $this->aspectAdviceContainers[$aspectClassName][] = $adviceContainer; $this->adviceContainers[$adviceContainer->getName()][] = $adviceContainer; } } } + /** @param class-string $aspectClassName */ + private function instantiateAspect(string $aspectClassName): object + { + if ($this->dependencyInjectionHandler !== null) { + return ($this->dependencyInjectionHandler)($aspectClassName, ComponentType::ASPECT); + } + try { + return DI::make($aspectClassName); + } catch (Throwable) { + throw new AspectNotFoundException($aspectClassName); + } + } + /** * Get the aspects. * @@ -237,13 +234,10 @@ public function getAspectAdviceNames(): array */ public function getAdviceContainersByAdviceNames(array $adviceNames): array { - return array_reduce( - $adviceNames, - fn (array $carry, string $adviceName) => array_merge( - $carry, - $this->adviceContainers[$adviceName] ?? [], - ), - [], - ); + $containers = []; + foreach ($adviceNames as $adviceName) { + $containers = array_merge($containers, $this->adviceContainers[$adviceName] ?? []); + } + return $containers; } } diff --git a/src/Core/Container/JoinPoint/MethodJoinPointContainer.php b/src/Core/Container/JoinPoint/MethodJoinPointContainer.php index 9724363..65e5b16 100644 --- a/src/Core/Container/JoinPoint/MethodJoinPointContainer.php +++ b/src/Core/Container/JoinPoint/MethodJoinPointContainer.php @@ -5,7 +5,7 @@ use Okapi\Aop\Core\Intercept\Interceptor; use Okapi\CodeTransformer\Core\DI; -// TODO: docs +/** Converts method join points into an interceptor callback. */ class MethodJoinPointContainer { private Interceptor $interceptor; @@ -18,18 +18,19 @@ class MethodJoinPointContainer * @param string[] $joinPoints */ public function __construct( - string $className, + string $className, public readonly string $methodName, - array $joinPoints, + array $joinPoints, ) { $this->interceptor = DI::make(Interceptor::class, [ - 'className' => $className, + 'className' => $className, 'methodName' => $methodName, 'joinPoints' => $joinPoints, ]); } - // TODO: docs + /** Converts method join points into an interceptor callback. */ + /** @return array{Interceptor, string} */ public function getValue(): array { return [ diff --git a/src/Core/Container/JoinPointContainer.php b/src/Core/Container/JoinPointContainer.php index 33b8e17..1cb1f1a 100644 --- a/src/Core/Container/JoinPointContainer.php +++ b/src/Core/Container/JoinPointContainer.php @@ -23,24 +23,21 @@ class JoinPointContainer * JoinPointContainer constructor. * * @param class-string $className - * @param array<'method', array> $joinPointPropertyValue + * @param array> $joinPointPropertyValue */ - public function __construct( - string $className, - array $joinPointPropertyValue, - ) { + public function __construct(string $className, array $joinPointPropertyValue) + { foreach ($joinPointPropertyValue as $joinPointType => $joinPointValue) { - if ($joinPointType === JoinPoint::TYPE_METHOD) { - foreach ($joinPointValue as $methodName => $joinPoints) { - $this->methodJoinPointContainers[] = DI::make( - MethodJoinPointContainer::class, - [ - 'className' => $className, - 'methodName' => $methodName, - 'joinPoints' => $joinPoints, - ], - ); - } + if ($joinPointType !== JoinPoint::TYPE_METHOD) { + continue; + } + + foreach ($joinPointValue as $methodName => $joinPoints) { + $this->methodJoinPointContainers[] = DI::make(MethodJoinPointContainer::class, [ + 'className' => $className, + 'methodName' => $methodName, + 'joinPoints' => $joinPoints, + ]); } } } @@ -48,14 +45,14 @@ public function __construct( /** * Get the value of the join point container. * - * @return array<'method', array> + * @return array<'method', array> */ public function getValue(): array { $value = []; foreach ($this->methodJoinPointContainers as $methodJoinPointContainer) { - $methodName = $methodJoinPointContainer->methodName; + $methodName = $methodJoinPointContainer->methodName; $joinPointValue = $methodJoinPointContainer->getValue(); $value[JoinPoint::TYPE_METHOD][$methodName] = $joinPointValue; diff --git a/src/Core/Container/TransformerManager.php b/src/Core/Container/TransformerManager.php index 7091685..359ae01 100644 --- a/src/Core/Container/TransformerManager.php +++ b/src/Core/Container/TransformerManager.php @@ -7,6 +7,7 @@ class TransformerManager extends CodeTransformerTransformerManager { + /** @return list */ protected function getAdditionalDependencyInjectionParams(): array { return [ComponentType::TRANSFORMER]; diff --git a/src/Core/Exception/Advice/MissingClassNameException.php b/src/Core/Exception/Advice/MissingClassNameException.php index b82d7f9..d7b2f59 100644 --- a/src/Core/Exception/Advice/MissingClassNameException.php +++ b/src/Core/Exception/Advice/MissingClassNameException.php @@ -16,9 +16,6 @@ class MissingClassNameException extends AdviceException */ public function __construct(string $adviceName) { - parent::__construct( - "Advice \"$adviceName\" is being used explicitly and is " . - "missing the class name.", - ); + parent::__construct("Advice \"{$adviceName}\" is being used explicitly and is " . 'missing the class name.'); } } diff --git a/src/Core/Exception/Advice/MissingMethodNameException.php b/src/Core/Exception/Advice/MissingMethodNameException.php index 63c4443..325e9f1 100644 --- a/src/Core/Exception/Advice/MissingMethodNameException.php +++ b/src/Core/Exception/Advice/MissingMethodNameException.php @@ -17,10 +17,10 @@ class MissingMethodNameException extends AdviceException public function __construct(string $adviceName) { parent::__construct( - "Advice \"$adviceName\" is being used explicitly and is missing the method name. \n" . - "Implicit Aspects: Aspects are applied without any modification to the target classes. \n" . - "Explicit Aspects: Aspects are applied to the target class or method directly " . - "by using the aspect as an attribute.", + "Advice \"{$adviceName}\" is being used explicitly and is missing the method name. \n" + . "Implicit Aspects: Aspects are applied without any modification to the target classes. \n" + . 'Explicit Aspects: Aspects are applied to the target class or method directly ' + . 'by using the aspect as an attribute.', ); } } diff --git a/src/Core/Exception/AdviceException.php b/src/Core/Exception/AdviceException.php index 9688439..c1b21c9 100644 --- a/src/Core/Exception/AdviceException.php +++ b/src/Core/Exception/AdviceException.php @@ -7,6 +7,4 @@ * * This exception is thrown when an advice is not valid. */ -abstract class AdviceException extends AopException -{ -} +abstract class AdviceException extends AopException {} diff --git a/src/Core/Exception/AopException.php b/src/Core/Exception/AopException.php index 9880b1b..fe31ad8 100644 --- a/src/Core/Exception/AopException.php +++ b/src/Core/Exception/AopException.php @@ -9,6 +9,4 @@ * * Base exception for all AOP exceptions. */ -abstract class AopException extends RuntimeException -{ -} +abstract class AopException extends RuntimeException {} diff --git a/src/Core/Exception/Aspect/AspectNotFoundException.php b/src/Core/Exception/Aspect/AspectNotFoundException.php index c29ecdd..ddc3666 100644 --- a/src/Core/Exception/Aspect/AspectNotFoundException.php +++ b/src/Core/Exception/Aspect/AspectNotFoundException.php @@ -18,8 +18,6 @@ class AspectNotFoundException extends AspectException */ public function __construct(string $aspectName) { - parent::__construct( - 'Aspect "' . $aspectName . '" not found.', - ); + parent::__construct('Aspect "' . $aspectName . '" not found.'); } } diff --git a/src/Core/Exception/Aspect/InvalidAspectClassNameException.php b/src/Core/Exception/Aspect/InvalidAspectClassNameException.php index a6a8d22..c491ccd 100644 --- a/src/Core/Exception/Aspect/InvalidAspectClassNameException.php +++ b/src/Core/Exception/Aspect/InvalidAspectClassNameException.php @@ -16,8 +16,6 @@ class InvalidAspectClassNameException extends AspectException */ public function __construct() { - parent::__construct( - 'Aspect class name in Kernel must be a string.', - ); + parent::__construct('Aspect class name in Kernel must be a string.'); } } diff --git a/src/Core/Exception/Aspect/MissingAspectAttributeException.php b/src/Core/Exception/Aspect/MissingAspectAttributeException.php index 0924dfe..f3beaae 100644 --- a/src/Core/Exception/Aspect/MissingAspectAttributeException.php +++ b/src/Core/Exception/Aspect/MissingAspectAttributeException.php @@ -20,8 +20,6 @@ class MissingAspectAttributeException extends AspectException */ public function __construct(string $aspectName) { - parent::__construct( - 'Aspect "' . $aspectName . '" is missing the #[Aspect] attribute.' - ); + parent::__construct('Aspect "' . $aspectName . '" is missing the #[Aspect] attribute.'); } } diff --git a/src/Core/Exception/AspectException.php b/src/Core/Exception/AspectException.php index 7b23f93..e3b60c3 100644 --- a/src/Core/Exception/AspectException.php +++ b/src/Core/Exception/AspectException.php @@ -7,6 +7,4 @@ * * This exception is thrown when an aspect is not valid. */ -abstract class AspectException extends AopException -{ -} +abstract class AspectException extends AopException {} diff --git a/src/Core/Factory/InvocationFactory.php b/src/Core/Factory/InvocationFactory.php index d667768..55f1b28 100644 --- a/src/Core/Factory/InvocationFactory.php +++ b/src/Core/Factory/InvocationFactory.php @@ -2,7 +2,9 @@ namespace Okapi\Aop\Core\Factory; -use Okapi\Aop\Attributes\{After, Around, Before}; +use Okapi\Aop\Attributes\After; +use Okapi\Aop\Attributes\Around; +use Okapi\Aop\Attributes\Before; use Okapi\Aop\Core\Container\AdviceType\MethodAdviceContainer; use Okapi\Aop\Invocation\AfterMethodInvocation; use Okapi\Aop\Invocation\AroundMethodInvocation; @@ -33,42 +35,43 @@ class InvocationFactory */ public function getInvocation( MethodAdviceContainer $adviceContainer, - ?object $subject, - string $className, - string $methodName, - mixed $result, - array &$arguments, + ?object $subject, + string $className, + string $methodName, + mixed $result, + array &$arguments, ): MethodInvocation { switch (true) { // Before case $adviceContainer->adviceAttributeInstance instanceof Before: return DI::make(BeforeMethodInvocation::class, [ - 'subject' => $subject, - 'className' => $className, + 'subject' => $subject, + 'className' => $className, 'methodName' => $methodName, - 'result' => $result, - 'arguments' => &$arguments, + 'result' => $result, + 'arguments' => &$arguments, ]); // Around case $adviceContainer->adviceAttributeInstance instanceof Around: return DI::make(AroundMethodInvocation::class, [ - 'subject' => $subject, - 'className' => $className, + 'subject' => $subject, + 'className' => $className, 'methodName' => $methodName, - 'result' => $result, - 'arguments' => &$arguments, + 'result' => $result, + 'arguments' => &$arguments, ]); // After case $adviceContainer->adviceAttributeInstance instanceof After: return DI::make(AfterMethodInvocation::class, [ - 'subject' => $subject, - 'className' => $className, + 'subject' => $subject, + 'className' => $className, 'methodName' => $methodName, - 'result' => $result, - 'arguments' => &$arguments, + 'result' => $result, + 'arguments' => &$arguments, ]); } + throw new \LogicException('Unsupported advice type.'); } } diff --git a/src/Core/Intercept/Interceptor.php b/src/Core/Intercept/Interceptor.php index aebf1ee..9512c55 100644 --- a/src/Core/Intercept/Interceptor.php +++ b/src/Core/Intercept/Interceptor.php @@ -1,4 +1,5 @@ $className, + 'className' => $className, 'joinPoints' => $joinPoints, ]); - $joinPointHandler->handle( - $this->beforeInterceptors, - $this->aroundInterceptors, - $this->afterInterceptors, - ); + $joinPointHandler->handle($this->beforeInterceptors, $this->aroundInterceptors, $this->afterInterceptors); $this->targetRefClass = new BaseReflectionClass($className); } @@ -87,7 +84,7 @@ public function __construct( public function intercept(?object $subject, array $arguments = []): mixed { foreach ($this->beforeInterceptors as $beforeInterceptor) { - $aspectInstance = $beforeInterceptor->aspectInstance; + $aspectInstance = $beforeInterceptor->aspectInstance; $adviceRefMethod = $beforeInterceptor->adviceRefMethod; $invocation = $this->invocationFactory->getInvocation( @@ -104,36 +101,41 @@ className: $this->className, $arguments = $invocation->getArguments(); } + $aroundAdviceChain = null; if ($this->aroundInterceptors) { $aroundAdviceChain = DI::make(AdviceChain::class, [ - 'interceptors' => $this->aroundInterceptors, - 'subject' => $subject, - 'className' => $this->className, - 'methodName' => $this->methodName, - 'arguments' => &$arguments, + 'interceptors' => $this->aroundInterceptors, + 'subject' => $subject, + 'className' => $this->className, + 'methodName' => $this->methodName, + 'arguments' => &$arguments, 'originalMethod' => function () use ($subject, &$arguments) { + /** @var array $arguments */ return $this->callParentMethod($subject, $arguments); }, ]); - - $result = $aroundAdviceChain->proceed(); - } else { - $result = $this->callParentMethod($subject, $arguments); } + /** @var array $arguments */ + /** @var mixed $result */ + $result = $aroundAdviceChain !== null + ? $aroundAdviceChain->proceed() + : $this->callParentMethod($subject, $arguments); if ($this->afterInterceptors) { $afterAdviceChain = DI::make(AdviceChain::class, [ - 'interceptors' => $this->afterInterceptors, - 'subject' => $subject, - 'className' => $this->className, - 'methodName' => $this->methodName, - 'arguments' => &$arguments, - 'originalMethod' => function () use ($subject, &$arguments) { + 'interceptors' => $this->afterInterceptors, + 'subject' => $subject, + 'className' => $this->className, + 'methodName' => $this->methodName, + 'arguments' => &$arguments, + 'originalMethod' => function () use ($subject, &$arguments) { + /** @var array $arguments */ return $this->callParentMethod($subject, $arguments); }, 'resultFromOriginalMethod' => $result, ]); + /** @var mixed $result */ $result = $afterAdviceChain->proceed(); } @@ -155,6 +157,9 @@ private function callParentMethod(?object $subject, array $args): mixed { $parentClass = $this->targetRefClass->getParentClass(); + if ($parentClass === false) { + throw new \LogicException('An intercepted class must have a woven parent.'); + } $parentMethod = $parentClass->getMethod($this->methodName); $this->unwrapVariadicParameters($parentMethod, $args); @@ -179,7 +184,12 @@ private function unwrapVariadicParameters(ReflectionMethod $method, array &$args if ($lastParameter && $lastParameter->isVariadic()) { $lastParameterName = $lastParameter->getName(); - $variadicParameterValues = array_values($args[$lastParameterName]); + /** @var mixed $variadicArguments */ + $variadicArguments = $args[$lastParameterName]; + if (!is_array($variadicArguments)) { + throw new \LogicException('Variadic arguments must be an array.'); + } + $variadicParameterValues = array_values($variadicArguments); unset($args[$lastParameterName]); diff --git a/src/Core/Invocation/AdviceChain.php b/src/Core/Invocation/AdviceChain.php index 69f0026..f9815df 100644 --- a/src/Core/Invocation/AdviceChain.php +++ b/src/Core/Invocation/AdviceChain.php @@ -1,7 +1,9 @@ originalMethod = $originalMethod === null ? null : Closure::fromCallable($originalMethod); if ($this->interceptors[0]->adviceAttributeInstance instanceof After) { $this->setResult($this->resultFromOriginalMethod); } @@ -81,7 +86,7 @@ public function proceed(bool $allowRepeatedCalls = false): mixed $interceptor = $this->interceptors[$this->currentInterceptorIndex]; $this->currentInterceptorIndex++; - $aspectInstance = $interceptor->aspectInstance; + $aspectInstance = $interceptor->aspectInstance; $adviceRefMethod = $interceptor->adviceRefMethod; // Get invocation @@ -99,6 +104,7 @@ className: $this->className, $invocation->setAdviceChain($this); // Call the advice method + /** @var mixed $result */ $result = $adviceRefMethod->invoke($aspectInstance, $invocation); // Check if the advice method will return a value @@ -124,13 +130,14 @@ className: $this->className, // 3. Return the result from the original method if ($this->resultHasBeenSet && !$allowRepeatedCalls) { return $this->result; - } elseif ($this->originalMethod) { + } + if ($this->originalMethod) { + /** @var mixed $result */ $result = ($this->originalMethod)(...array_values($this->arguments)); $this->setResult($result); return $result; - } else { - return $this->resultFromOriginalMethod; } + return $this->resultFromOriginalMethod; } /** diff --git a/src/Core/JoinPoint/JoinPointHandler.php b/src/Core/JoinPoint/JoinPointHandler.php index 1e02a51..5c25fd2 100644 --- a/src/Core/JoinPoint/JoinPointHandler.php +++ b/src/Core/JoinPoint/JoinPointHandler.php @@ -1,4 +1,5 @@ joinPoints as $joinPoint) { $adviceContainers = $this->aspectMatcher->getMatchedAdviceContainersByJoinPoint( $this->className, diff --git a/src/Core/JoinPoint/JoinPointInjector.php b/src/Core/JoinPoint/JoinPointInjector.php index 032f490..e3400ba 100644 --- a/src/Core/JoinPoint/JoinPointInjector.php +++ b/src/Core/JoinPoint/JoinPointInjector.php @@ -30,20 +30,16 @@ public function injectJoinPoints(string $className): void $refClass = new BaseReflectionClass($className); // Read the join points - $staticPropertyValue = $refClass->getStaticPropertyValue( - JoinPoint::JOIN_POINTS_PARAMETER_NAME, - ); + /** @var array<'method', array> $staticPropertyValue */ + $staticPropertyValue = $refClass->getStaticPropertyValue(JoinPoint::JOIN_POINTS_PARAMETER_NAME, null); // Convert to JoinPointContainer $joinPointContainer = DI::make(JoinPointContainer::class, [ - 'className' => $className, + 'className' => $className, 'joinPointPropertyValue' => $staticPropertyValue, ]); // Set the join points - $refClass->setStaticPropertyValue( - JoinPoint::JOIN_POINTS_PARAMETER_NAME, - $joinPointContainer->getValue(), - ); + $refClass->setStaticPropertyValue(JoinPoint::JOIN_POINTS_PARAMETER_NAME, $joinPointContainer->getValue()); } } diff --git a/src/Core/Matcher/AdviceMatcher.php b/src/Core/Matcher/AdviceMatcher.php index d2d1db4..ffd243a 100644 --- a/src/Core/Matcher/AdviceMatcher.php +++ b/src/Core/Matcher/AdviceMatcher.php @@ -1,4 +1,5 @@ methodMatcher->match( - $adviceContainer, - $refClass, - ); + return $this->methodMatcher->match($adviceContainer, $refClass); } + return null; } } diff --git a/src/Core/Matcher/AdviceMatcher/MethodMatcher.php b/src/Core/Matcher/AdviceMatcher/MethodMatcher.php index bcc8827..8c1be55 100644 --- a/src/Core/Matcher/AdviceMatcher/MethodMatcher.php +++ b/src/Core/Matcher/AdviceMatcher/MethodMatcher.php @@ -35,29 +35,15 @@ public function match( // Basically the same as $refClassToMatch->getImmediateMethods(), // but this also includes the methods from traits, because traits // cannot be woven - $declaringClass = $refMethodToMatch->getDeclaringClass(); + $declaringClass = $refMethodToMatch->getDeclaringClass(); $declaringClassName = $declaringClass->getName(); - if (!$declaringClass->isTrait() - && $declaringClassName !== $refClassToMatchName - ) { + if (!$declaringClass->isTrait() && $declaringClassName !== $refClassToMatchName) { continue; } - // Match explicit aspects - if ($methodAdviceContainer->isExplicit()) { - $newMethodAdviceContainer = $this->matchExplicit( - $methodAdviceContainer, - $refMethodToMatch, - $newMethodAdviceContainer, - ); - } else { - // Match implicit aspects - $newMethodAdviceContainer = $this->matchImplicit( - $methodAdviceContainer, - $refMethodToMatch, - $newMethodAdviceContainer, - ); - } + $newMethodAdviceContainer = $methodAdviceContainer->isExplicit() + ? $this->matchExplicit($methodAdviceContainer, $refMethodToMatch, $newMethodAdviceContainer) + : $this->matchImplicit($methodAdviceContainer, $refMethodToMatch, $newMethodAdviceContainer); } return $newMethodAdviceContainer; @@ -79,46 +65,35 @@ protected function matchExplicit( ): ?MethodAdviceContainer { $aspectClassName = $methodAdviceContainer->aspectClassName; - // Match class attributes + // Match class attributes. $declaringClass = $refMethodToMatch->getDeclaringClass(); foreach ($declaringClass->getAttributes() as $refAttribute) { - if ($refAttribute->getName() === $aspectClassName) { - $adviceAttributeInstance = $methodAdviceContainer->adviceAttributeInstance; - - // Advices without method are applied to all methods - if ($adviceAttributeInstance->method === null) { - $newMethodAdviceContainer = $this->createNewMethodAdviceContainer( - $methodAdviceContainer, - $newMethodAdviceContainer, - ); - - $newMethodAdviceContainer->addMatchedMethod($refMethodToMatch); - } else { - $methodNameToMatch = $refMethodToMatch->getName(); - $methodRegex = $adviceAttributeInstance->method; - - if ($methodRegex->matches($methodNameToMatch)) { - $newMethodAdviceContainer = $this->createNewMethodAdviceContainer( - $methodAdviceContainer, - $newMethodAdviceContainer, - ); - - $newMethodAdviceContainer->addMatchedMethod($refMethodToMatch); - } - } + if ($refAttribute->getName() !== $aspectClassName) { + continue; + } + + $methodRegex = $methodAdviceContainer->adviceAttributeInstance->method; + // Advices without a method pattern apply to all methods. + if ($methodRegex !== null && !$methodRegex->matches($refMethodToMatch->getName())) { + continue; } + $newMethodAdviceContainer = $this->createNewMethodAdviceContainer( + $methodAdviceContainer, + $newMethodAdviceContainer, + ); + $newMethodAdviceContainer->addMatchedMethod($refMethodToMatch); } - // Match method attributes + // Match method attributes. foreach ($refMethodToMatch->getAttributes() as $refAttribute) { - if ($refAttribute->getName() === $aspectClassName) { - $newMethodAdviceContainer = $this->createNewMethodAdviceContainer( - $methodAdviceContainer, - $newMethodAdviceContainer, - ); - - $newMethodAdviceContainer->addMatchedMethod($refMethodToMatch); + if ($refAttribute->getName() !== $aspectClassName) { + continue; } + $newMethodAdviceContainer = $this->createNewMethodAdviceContainer( + $methodAdviceContainer, + $newMethodAdviceContainer, + ); + $newMethodAdviceContainer->addMatchedMethod($refMethodToMatch); } return $newMethodAdviceContainer; @@ -141,20 +116,22 @@ protected function matchImplicit( $methodNameToMatch = $refMethodToMatch->getName(); // Only public methods - if ($methodAdviceContainer->adviceAttributeInstance->onlyPublicMethods + if ( + $methodAdviceContainer->adviceAttributeInstance->onlyPublicMethods && !($refMethodToMatch->getModifiers() & BaseReflectionMethod::IS_PUBLIC) ) { return $newMethodAdviceContainer; } // Intercept trait methods - if (!$methodAdviceContainer->adviceAttributeInstance->interceptTraitMethods + if ( + !$methodAdviceContainer->adviceAttributeInstance->interceptTraitMethods && $refMethodToMatch->getDeclaringClass()->isTrait() ) { return $newMethodAdviceContainer; } - if ($methodAdviceContainer->adviceAttributeInstance->method->matches($methodNameToMatch)) { + if ($methodAdviceContainer->adviceAttributeInstance->method?->matches($methodNameToMatch)) { $newMethodAdviceContainer = $this->createNewMethodAdviceContainer( $methodAdviceContainer, $newMethodAdviceContainer, diff --git a/src/Core/Matcher/AspectMatcher.php b/src/Core/Matcher/AspectMatcher.php index 77736fa..031ca4c 100644 --- a/src/Core/Matcher/AspectMatcher.php +++ b/src/Core/Matcher/AspectMatcher.php @@ -1,4 +1,5 @@ isInterface() || $refClass->isTrait()) { - $this->cacheEmptyResult( - $namespacedClass, - $refClass->getFileName(), - ); + $this->cacheEmptyResult($namespacedClass, $refClass->getFileName()); return false; } @@ -107,16 +105,13 @@ public function matchByClassLoaderAndStore(string $namespacedClass): bool $refClass, $adviceContainer, $this->explicitClassAspectTargets[$namespacedClass] ?? false, - (bool)($this->explicitMethodAspectTargets[$namespacedClass] ?? false), + (bool) ($this->explicitMethodAspectTargets[$namespacedClass] ?? false), )) { continue; } // Match advices - $matchedAdviceContainer = $this->adviceMatcher->match( - $adviceContainer, - $refClass, - ); + $matchedAdviceContainer = $this->adviceMatcher->match($adviceContainer, $refClass); if ($matchedAdviceContainer) { $matchedAdviceContainers[] = $matchedAdviceContainer; } @@ -128,13 +123,10 @@ public function matchByClassLoaderAndStore(string $namespacedClass): bool // Cache the result if (!$matchedAdviceContainers) { - $this->cacheEmptyResult( - $namespacedClass, - $refClass->getFileName(), - ); + $this->cacheEmptyResult($namespacedClass, $refClass->getFileName()); } - return (bool)$matchedAdviceContainers; + return (bool) $matchedAdviceContainers; } /** @@ -147,9 +139,8 @@ public function matchByClassLoaderAndStore(string $namespacedClass): bool * * @return void */ - protected function checkForExplicitAdvices( - BetterReflectionClass $refClass, - ): void { + protected function checkForExplicitAdvices(BetterReflectionClass $refClass): void + { $this->checkForExplicitClassAspects($refClass); $this->checkForExplicitMethodAspects($refClass); } @@ -161,14 +152,15 @@ protected function checkForExplicitAdvices( * * @return void */ - protected function checkForExplicitClassAspects( - BetterReflectionClass $refClass, - ): void { + protected function checkForExplicitClassAspects(BetterReflectionClass $refClass): void + { foreach ($refClass->getAttributes() as $refAttribute) { - if ($this->hasAspectAndAttribute($refAttribute)) { - $this->aspectManager->loadAspect($refAttribute->getClass()->getName()); - $this->explicitClassAspectTargets[$refClass->getName()] = true; + if (!$this->hasAspectAndAttribute($refAttribute)) { + continue; } + + $this->aspectManager->loadAspect($refAttribute->getClass()->getName()); + $this->explicitClassAspectTargets[$refClass->getName()] = true; } } @@ -179,15 +171,16 @@ protected function checkForExplicitClassAspects( * * @return void */ - protected function checkForExplicitMethodAspects( - BetterReflectionClass $refClass, - ): void { + protected function checkForExplicitMethodAspects(BetterReflectionClass $refClass): void + { foreach ($refClass->getImmediateMethods() as $refMethod) { foreach ($refMethod->getAttributes() as $refAttribute) { - if ($this->hasAspectAndAttribute($refAttribute)) { - $this->aspectManager->loadAspect($refAttribute->getClass()->getName()); - $this->explicitMethodAspectTargets[$refClass->getName()][] = $refMethod->getName(); + if (!$this->hasAspectAndAttribute($refAttribute)) { + continue; } + + $this->aspectManager->loadAspect($refAttribute->getClass()->getName()); + $this->explicitMethodAspectTargets[$refClass->getName()][] = $refMethod->getName(); } } } @@ -199,9 +192,8 @@ protected function checkForExplicitMethodAspects( * * @return bool */ - protected function hasAspectAndAttribute( - BetterReflectionAttribute $refAttribute, - ): bool { + protected function hasAspectAndAttribute(BetterReflectionAttribute $refAttribute): bool + { try { $attributeClass = $refAttribute->getClass(); } catch (IdentifierNotFound) { @@ -211,12 +203,8 @@ protected function hasAspectAndAttribute( return false; } - $hasAspectAttribute = (bool)$attributeClass->getAttributesByInstance( - Aspect::class, - ); - $hasAttributeAttribute = (bool)$attributeClass->getAttributesByInstance( - Attribute::class, - ); + $hasAspectAttribute = (bool) $attributeClass->getAttributesByInstance(Aspect::class); + $hasAttributeAttribute = (bool) $attributeClass->getAttributesByInstance(Attribute::class); return $hasAspectAttribute && $hasAttributeAttribute; } @@ -229,24 +217,25 @@ protected function hasAspectAndAttribute( * * @return void */ - private function cacheEmptyResult( - string $namespacedClass, - string $filePath, - ): void { - $filePath = Path::resolve($filePath); + private function cacheEmptyResult(string $namespacedClass, ?string $filePath): void + { + if ($filePath === null) { + return; + } + $filePath = Path::resolve($filePath); + if (!is_string($filePath)) { + return; + } $cacheState = DI::make(EmptyResultCacheState::class, [ CacheState::DATA => [ CacheState::ORIGINAL_FILE_PATH_KEY => $filePath, - CacheState::NAMESPACED_CLASS_KEY => $namespacedClass, - CacheState::MODIFICATION_TIME_KEY => filemtime($filePath), + CacheState::NAMESPACED_CLASS_KEY => $namespacedClass, + CacheState::MODIFICATION_TIME_KEY => filemtime($filePath), ], ]); // Set the cache state - $this->cacheStateManager->setCacheState( - $filePath, - $cacheState, - ); + $this->cacheStateManager->setCacheState($filePath, $cacheState); } /** @@ -257,10 +246,8 @@ private function cacheEmptyResult( * * @return void */ - public function addMatchedAdviceContainers( - string $namespacedClass, - array $adviceContainers, - ): void { + public function addMatchedAdviceContainers(string $namespacedClass, array $adviceContainers): void + { $this->matchedAdviceContainers[$namespacedClass] = $adviceContainers; } @@ -284,14 +271,12 @@ public function getMatchedAdviceContainers(string $namespacedClass): array * * @return MethodAdviceContainer[] */ - public function getMatchedAdviceContainersByJoinPoint( - string $targetClassName, - string $joinPoint, - ): array { + public function getMatchedAdviceContainersByJoinPoint(string $targetClassName, string $joinPoint): array + { $matchedAdviceContainers = []; foreach ($this->matchedAdviceContainers[$targetClassName] as $adviceContainer) { - if (!($adviceContainer instanceof MethodAdviceContainer)) { + if (!$adviceContainer instanceof MethodAdviceContainer) { continue; } diff --git a/src/Core/Matcher/ClassMatcher.php b/src/Core/Matcher/ClassMatcher.php index 19e7e3d..eea6128 100644 --- a/src/Core/Matcher/ClassMatcher.php +++ b/src/Core/Matcher/ClassMatcher.php @@ -1,4 +1,5 @@ adviceAttributeInstance; - $classRegex = $adviceAttributeInstance->class; - $namespacedClass = $refClass->getName(); + $classRegex = $adviceAttributeInstance->class; + if ($classRegex === null) { + return false; + } + $namespacedClass = $refClass->getName(); $classMatches = $classRegex->matches($namespacedClass); - $interfacesMatches = $this->matchInterfaces( - $classRegex, - $refClass, - ); + $interfacesMatches = $this->matchInterfaces($classRegex, $refClass); - $parentClassesMatches = $this->matchParentClasses( - $classRegex, - $refClass, - ); + $parentClassesMatches = $this->matchParentClasses($classRegex, $refClass); - $traitsMatches = $this->matchTraits( - $classRegex, - $refClass, - ); + $traitsMatches = $this->matchTraits($classRegex, $refClass); - return $classMatches - || $interfacesMatches - || $parentClassesMatches - || $traitsMatches; + return $classMatches || $interfacesMatches || $parentClassesMatches || $traitsMatches; } /** @@ -76,15 +68,9 @@ public function match( * * @return bool */ - protected function matchInterfaces( - Regex $classRegex, - BetterReflectionClass $reflectionClass, - ): bool { - return $this->matchType( - 'InterfaceNames', - $classRegex, - $reflectionClass, - ); + protected function matchInterfaces(Regex $classRegex, BetterReflectionClass $reflectionClass): bool + { + return $this->matchType('InterfaceNames', $classRegex, $reflectionClass); } /** @@ -95,15 +81,9 @@ protected function matchInterfaces( * * @return bool */ - protected function matchParentClasses( - Regex $classRegex, - BetterReflectionClass $reflectionClass, - ): bool { - return $this->matchType( - 'ParentClassNames', - $classRegex, - $reflectionClass, - ); + protected function matchParentClasses(Regex $classRegex, BetterReflectionClass $reflectionClass): bool + { + return $this->matchType('ParentClassNames', $classRegex, $reflectionClass); } /** @@ -114,15 +94,9 @@ protected function matchParentClasses( * * @return bool */ - protected function matchTraits( - Regex $classRegex, - BetterReflectionClass $reflectionClass, - ): bool { - return $this->matchType( - 'Traits', - $classRegex, - $reflectionClass, - ); + protected function matchTraits(Regex $classRegex, BetterReflectionClass $reflectionClass): bool + { + return $this->matchType('Traits', $classRegex, $reflectionClass); } /** @@ -134,26 +108,24 @@ protected function matchTraits( * * @return bool */ - protected function matchType( - string $type, - Regex $classRegex, - BetterReflectionClass $reflectionClass, - ): bool { + protected function matchType(string $type, Regex $classRegex, BetterReflectionClass $reflectionClass): bool + { try { - $method = 'get' . $type; - /** @var (BetterReflection|string)[] $reflections */ - $reflections = $reflectionClass->$method(); + $reflections = match ($type) { + 'InterfaceNames' => $reflectionClass->getInterfaceNames(), + 'ParentClassNames' => $reflectionClass->getParentClassNames(), + 'Traits' => $reflectionClass->getTraits(), + default => throw new \InvalidArgumentException('Unsupported reflection type.'), + }; foreach ($reflections as $reflection) { if ($classRegex->matches( - $reflection instanceof BetterReflection - ? $reflection->getName() - : $reflection, + $reflection instanceof BetterReflection ? $reflection->getName() : $reflection, )) { return true; } } } catch (IdentifierNotFound) { - // Do nothing + return false; } return false; diff --git a/src/Core/Processor/AspectProcessor.php b/src/Core/Processor/AspectProcessor.php index 1d6a0c3..19ac429 100644 --- a/src/Core/Processor/AspectProcessor.php +++ b/src/Core/Processor/AspectProcessor.php @@ -1,4 +1,5 @@ uri; - $proxyFilePath = $this->cachePaths->getProxyCachePath($originalFilePath); - $wovenFilePath = $this->cachePaths->getWovenCachePath($originalFilePath); - $transformed = $metadata->code->hasChanges(); + $cachePaths = $this->cachePaths; + if (!$cachePaths instanceof \Okapi\Aop\Core\Cache\CachePaths) { + throw new \LogicException('Aspect processing requires AOP cache paths.'); + } + $proxyFilePath = $cachePaths->getProxyCachePath($originalFilePath); + $wovenFilePath = $cachePaths->getWovenCachePath($originalFilePath); + $transformed = $metadata->code->hasChanges(); // Save the transformed code if ($transformed) { // Proxy - Filesystem::writeFile( - $proxyFilePath, - $metadata->code->getNewSource(), - ); + Filesystem::writeFile($proxyFilePath, $metadata->code->getNewSource()); // Weaving if ($wovenFile) { - Filesystem::writeFile( - $wovenFilePath, - $wovenFile, - ); + Filesystem::writeFile($wovenFilePath, $wovenFile); } } @@ -100,38 +100,42 @@ public function transform(Metadata $metadata): void $cacheState = DI::make(WovenCacheState::class, [ CacheState::DATA => [ - CacheState::ORIGINAL_FILE_PATH_KEY => $originalFilePath, - CacheState::NAMESPACED_CLASS_KEY => $namespacedClass, - CacheState::MODIFICATION_TIME_KEY => $modificationTime, - WovenCacheState::PROXY_FILE_PATH_KEY => $proxyFilePath, - WovenCacheState::WOVEN_FILE_PATH_KEY => $wovenFilePath, + CacheState::ORIGINAL_FILE_PATH_KEY => $originalFilePath, + CacheState::NAMESPACED_CLASS_KEY => $namespacedClass, + CacheState::MODIFICATION_TIME_KEY => $modificationTime, + WovenCacheState::PROXY_FILE_PATH_KEY => $proxyFilePath, + WovenCacheState::WOVEN_FILE_PATH_KEY => $wovenFilePath, WovenCacheState::TRANSFORMER_FILE_PATHS_KEY => $transformerFilePaths, - WovenCacheState::ADVICE_NAMES_KEY => $adviceNames, - WovenCacheState::ASPECT_FILE_PATHS_KEY => $aspectFilePaths, - WovenCacheState::ASPECT_CLASS_NAMES_KEY => $aspectClassNames, + WovenCacheState::ADVICE_NAMES_KEY => $adviceNames, + WovenCacheState::ASPECT_FILE_PATHS_KEY => $aspectFilePaths, + WovenCacheState::ASPECT_CLASS_NAMES_KEY => $aspectClassNames, ], ]); - } elseif ($transformed) { + $this->cacheStateManager->setCacheState($originalFilePath, $cacheState); + return; + } + if ($transformed) { $transformerFilePaths = $this->getTransformerFilePaths($transformerContainers); $cacheState = DI::make(TransformedCacheState::class, [ - CacheState::DATA => [ - CacheState::ORIGINAL_FILE_PATH_KEY => $originalFilePath, - CacheState::NAMESPACED_CLASS_KEY => $namespacedClass, - CacheState::MODIFICATION_TIME_KEY => $modificationTime, - TransformedCacheState::TRANSFORMED_FILE_PATH_KEY => $proxyFilePath, - TransformedCacheState::TRANSFORMER_FILE_PATHS_KEY => $transformerFilePaths, - ], - ]); - } else { - $cacheState = DI::make(NoTransformationsCacheState::class, [ CacheState::DATA => [ CacheState::ORIGINAL_FILE_PATH_KEY => $originalFilePath, - CacheState::NAMESPACED_CLASS_KEY => $namespacedClass, - CacheState::MODIFICATION_TIME_KEY => $modificationTime, + CacheState::NAMESPACED_CLASS_KEY => $namespacedClass, + CacheState::MODIFICATION_TIME_KEY => $modificationTime, + TransformedCacheState::TRANSFORMED_FILE_PATH_KEY => $proxyFilePath, + TransformedCacheState::TRANSFORMER_FILE_PATHS_KEY => $transformerFilePaths, ], ]); + $this->cacheStateManager->setCacheState($originalFilePath, $cacheState); + return; } + $cacheState = DI::make(NoTransformationsCacheState::class, [ + CacheState::DATA => [ + CacheState::ORIGINAL_FILE_PATH_KEY => $originalFilePath, + CacheState::NAMESPACED_CLASS_KEY => $namespacedClass, + CacheState::MODIFICATION_TIME_KEY => $modificationTime, + ], + ]); $this->cacheStateManager->setCacheState($originalFilePath, $cacheState); } @@ -144,20 +148,15 @@ public function transform(Metadata $metadata): void * * @return string The woven code */ - private function processAdvices( - Metadata $metadata, - array $adviceContainers, - ): string { + private function processAdvices(Metadata $metadata, array $adviceContainers): string + { // Sort the advices by priority - usort( - $adviceContainers, - function (AdviceContainer $a, AdviceContainer $b) { - $orderA = $a->adviceAttributeInstance->order; - $orderB = $b->adviceAttributeInstance->order; + usort($adviceContainers, static function (AdviceContainer $a, AdviceContainer $b) { + $orderA = $a->adviceAttributeInstance->order; + $orderB = $b->adviceAttributeInstance->order; - return $orderA <=> $orderB; - }, - ); + return $orderA <=> $orderB; + }); $proxiedClassModifier = DI::make(ProxiedClassModifier::class, [ 'metadata' => $metadata, @@ -166,10 +165,7 @@ function (AdviceContainer $a, AdviceContainer $b) { $proxiedClassModifier->modify(); // Create the weaving file - return $this->processAdviceContainers( - $adviceContainers, - $metadata->code, - ); + return $this->processAdviceContainers($adviceContainers, $metadata->code); } /** @@ -180,12 +176,10 @@ function (AdviceContainer $a, AdviceContainer $b) { * * @return string */ - private function processAdviceContainers( - array $adviceContainers, - Code $code, - ): string { + private function processAdviceContainers(array $adviceContainers, Code $code): string + { $weavingClassBuilder = DI::make(WovenClassBuilder::class, [ - 'code' => $code, + 'code' => $code, 'adviceContainers' => $adviceContainers, ]); @@ -202,9 +196,7 @@ private function processAdviceContainers( protected function getAdviceNames(array $adviceContainers): array { return array_unique(array_map( - function (AdviceContainer $adviceContainer) { - return $adviceContainer->getName(); - }, + static fn(AdviceContainer $adviceContainer) => $adviceContainer->getName(), $adviceContainers, )); } @@ -218,12 +210,13 @@ function (AdviceContainer $adviceContainer) { */ protected function getAspectFilePaths(array $adviceContainers): array { - return array_unique(array_map( - function (AdviceContainer $adviceContainer) { - return $adviceContainer->aspectRefClass->getFileName(); - }, - $adviceContainers, - )); + return array_unique(array_map(static function (AdviceContainer $adviceContainer) { + $filePath = $adviceContainer->aspectRefClass->getFileName(); + if ($filePath === false) { + throw new \LogicException('An aspect must have a source file.'); + } + return $filePath; + }, $adviceContainers)); } /** @@ -236,9 +229,7 @@ function (AdviceContainer $adviceContainer) { protected function getAspectClassNames(array $adviceContainers): array { return array_unique(array_map( - function (AdviceContainer $adviceContainer) { - return $adviceContainer->aspectClassName; - }, + static fn(AdviceContainer $adviceContainer) => $adviceContainer->aspectClassName, $adviceContainers, )); } diff --git a/src/Core/Transform/ProxiedClassModifier.php b/src/Core/Transform/ProxiedClassModifier.php index affde9f..406f91d 100644 --- a/src/Core/Transform/ProxiedClassModifier.php +++ b/src/Core/Transform/ProxiedClassModifier.php @@ -1,4 +1,5 @@ [] + * @var list */ private array $tokenCallbacks = []; /** * Callbacks to process nodes. * - * @var callable[] + * @var list */ private array $nodeCallbacks = []; @@ -75,19 +76,15 @@ public function __construct( ) { $cachePaths = DI::get(CachePaths::class); - $this->code = $this->metadata->code; - $this->sourceFileNode = $this->code->getSourceFileNode(); + $this->code = $this->metadata->code; + $this->sourceFileNode = $this->code->getSourceFileNode(); $this->proxiedClassName = $this->code->getClassName() . $cachePaths::PROXIED_SUFFIX; } /** @noinspection PhpUnused Is used at runtime for proxied classes */ public static function resolveStaticClass(string $staticClass): string { - return str_replace( - CachePaths::PROXIED_SUFFIX, - '', - $staticClass, - ); + return str_replace(CachePaths::PROXIED_SUFFIX, '', $staticClass); } /** @@ -107,32 +104,31 @@ public function modify(): void $sourceFileNode = $this->metadata->code->getSourceFileNode(); // Iterate over the nodes - foreach ($sourceFileNode->getDescendantNodes() as $node) { + /** @var iterable $nodes The parser yields nodes; its vendor PHPDoc uses an untyped Generator union. */ + $nodes = $sourceFileNode->getDescendantNodes(); + foreach ($nodes as $node) { foreach ($this->nodeCallbacks as $callback) { $callback($node); } } // Iterate over the tokens - foreach ($sourceFileNode->getDescendantTokens() as $token) { + /** @var iterable $tokens */ + $tokens = $sourceFileNode->getDescendantTokens(); + foreach ($tokens as $token) { foreach ($this->tokenCallbacks as $callback) { $callback($token); } } } - private function edit( - Node|Token $nodeOrToken, - string $replacement, - ): void { + private function edit(Node|Token $nodeOrToken, string $replacement): void + { if (in_array($nodeOrToken, $this->alreadyProcessed, true)) { return; } - $this->code->edit( - $nodeOrToken, - $replacement, - ); + $this->code->edit($nodeOrToken, $replacement); $this->alreadyProcessed[] = $nodeOrToken; } @@ -146,18 +142,15 @@ private function convertToProxy(): void { // Find the class declaration $node = $this->sourceFileNode->getFirstDescendantNode(Node\Statement\ClassDeclaration::class); - assert($node instanceof Node\Statement\ClassDeclaration); + assert($node instanceof Node\Statement\ClassDeclaration, 'The transformed source must declare a class.'); // Replace the class name - $this->edit( - $node->name, - $this->proxiedClassName, - ); + $this->edit($node->name, $this->proxiedClassName); // Append the child class $childClassPath = $this->cachePaths->getWovenCachePath($this->metadata->uri); // language=PHP - $codeToAppend = "\ninclude_once '$childClassPath';"; + $codeToAppend = "\ninclude_once '{$childClassPath}';"; $this->code->append($codeToAppend); } @@ -193,9 +186,11 @@ private function changeVisibility(): void { // A descendant may be loaded after this class. Keep every private property // in its declaring scope, even when no same-name property is known yet. - foreach ($this->sourceFileNode->getDescendantNodes() as $node) { + /** @var iterable $nodes */ + $nodes = $this->sourceFileNode->getDescendantNodes(); + foreach ($nodes as $node) { $modifiers = match (true) { - $node instanceof Node\PropertyDeclaration => $node->modifiers ?? [], + $node instanceof Node\PropertyDeclaration => $node->modifiers, $node instanceof Node\Parameter => array_filter([ $node->visibilityToken, ...($node->modifiers ?? []), @@ -203,20 +198,17 @@ private function changeVisibility(): void default => [], }; foreach ($modifiers as $modifier) { - if ($modifier->kind === TokenKind::PrivateKeyword) { - $this->alreadyProcessed[] = $modifier; + if ($modifier->kind !== TokenKind::PrivateKeyword) { + continue; } + + $this->alreadyProcessed[] = $modifier; } } $this->tokenCallbacks[] = function (Token $token) { - if ($token->kind === TokenKind::PrivateKeyword - || $token->kind === TokenKind::ProtectedKeyword - ) { - $this->edit( - $token, - 'public', - ); + if ($token->kind === TokenKind::PrivateKeyword || $token->kind === TokenKind::ProtectedKeyword) { + $this->edit($token, 'public'); } }; } @@ -232,13 +224,16 @@ private function replaceSelfType(): void { $this->nodeCallbacks[] = function (Node $node) { // Replace return object types with the proxied class name - if ($node instanceof Node\MethodDeclaration + if ( + $node instanceof Node\MethodDeclaration && $node->returnTypeList instanceof Node\DelimitedList\QualifiedNameList ) { foreach ($node->returnTypeList->children as $returnType) { - if ($returnType instanceof Node\QualifiedName) { - $this->replaceReturnSelfType($returnType); + if (!$returnType instanceof Node\QualifiedName) { + continue; } + + $this->replaceReturnSelfType($returnType); } } @@ -258,15 +253,11 @@ private function replaceSelfType(): void * * @return void */ - private function replaceReturnSelfType( - Node\QualifiedName $qualifiedName, - ): void { + private function replaceReturnSelfType(Node\QualifiedName $qualifiedName): void + { // Self if ($qualifiedName->getText() === 'self') { - $this->edit( - $qualifiedName, - $this->proxiedClassName, - ); + $this->edit($qualifiedName, $this->proxiedClassName); } } @@ -277,13 +268,9 @@ private function replaceReturnSelfType( * * @return void */ - private function replaceObjectCreationSelfType( - Node\Expression\ObjectCreationExpression $objectCreationExpression, - ): void { - $this->edit( - $objectCreationExpression->classTypeDesignator, - '\\' . $this->code->getNamespacedClass(), - ); + private function replaceObjectCreationSelfType(Node\Expression\ObjectCreationExpression $objectCreationExpression): void + { + $this->edit($objectCreationExpression->classTypeDesignator, '\\' . $this->code->getNamespacedClass()); } // endregion @@ -305,19 +292,19 @@ private function replaceMagicConstants(): void case '__DIR__': $originalParentDir = dirname($this->getOriginalFileDir()); - $this->edit($node, "'$originalParentDir'"); + $this->edit($node, "'{$originalParentDir}'"); break; case '__FILE__': $originalFileDir = $this->getOriginalFileDir(); - $this->edit($node, "'$originalFileDir'"); + $this->edit($node, "'{$originalFileDir}'"); break; case '__CLASS__': $originalNamespacedClassName = $this->code->getNamespacedClass(); - $this->edit($node, "'$originalNamespacedClassName'"); + $this->edit($node, "'{$originalNamespacedClassName}'"); break; case '__METHOD__': @@ -325,34 +312,25 @@ private function replaceMagicConstants(): void if (!$methodNode) { break; } - assert($methodNode instanceof Node\MethodDeclaration); + assert($methodNode instanceof Node\MethodDeclaration, 'The enclosing node must be a method.'); $originalNamespacedClassName = $this->code->getNamespacedClass(); $originalMethodName = $methodNode->getName(); - $this->edit( - $node, - "'$originalNamespacedClassName::$originalMethodName'", - ); + $this->edit($node, "'{$originalNamespacedClassName}::{$originalMethodName}'"); break; case 'self': $originalClassName = $this->code->getClassName(); - $this->edit( - $node, - $originalClassName, - ); + $this->edit($node, $originalClassName); break; } } if ($node instanceof Node\Expression\ScopedPropertyAccessExpression) { if ($node->getText() === 'static::class') { - $this->edit( - $node, - '\\' . self::class . '::resolveStaticClass(static::class)', - ); + $this->edit($node, '\\' . self::class . '::resolveStaticClass(static::class)'); } } }; diff --git a/src/Core/Transform/WovenClassBuilder.php b/src/Core/Transform/WovenClassBuilder.php index 31f0b0a..0d81971 100644 --- a/src/Core/Transform/WovenClassBuilder.php +++ b/src/Core/Transform/WovenClassBuilder.php @@ -1,4 +1,5 @@ addUse(JoinPointInjector::class); // Build the file - $file = (string)$phpNamespace; + $file = (string) $phpNamespace; // Inject the JoinPoints $this->injectJoinPoints($file); @@ -93,7 +94,7 @@ public function build(): string private function buildNamespace(): PhpNamespace { $reflectionClass = $this->code->getReflectionClass(); - return new PhpNamespace($reflectionClass->getNamespaceName()); + return new PhpNamespace($reflectionClass->getNamespaceName() ?? ''); } /** @@ -114,7 +115,7 @@ private function buildClass(PhpNamespace $phpNamespace): ClassType $class->setName($shortClassName); // Add the use statement - $className = $reflectionClass->getName(); + $className = $reflectionClass->getName(); $proxyClassName = $className . $this->cachePaths::PROXIED_SUFFIX; $phpNamespace->addUse($proxyClassName); @@ -151,18 +152,20 @@ private function buildJoinPoints(): Property // Add interceptors foreach ($this->adviceContainers as $adviceContainer) { - if ($adviceContainer instanceof MethodAdviceContainer) { - $methodType = JoinPoint::TYPE_METHOD; + if (!$adviceContainer instanceof MethodAdviceContainer) { + continue; + } - foreach ($adviceContainer->getMatchedMethods() as $matchedMethod) { - $matchedRefMethod = $matchedMethod->matchedRefMethod; - $matchedMethodName = $matchedRefMethod->getName(); + $methodType = JoinPoint::TYPE_METHOD; - $adviceContainerName = $adviceContainer->getName(); + foreach ($adviceContainer->getMatchedMethods() as $matchedMethod) { + $matchedRefMethod = $matchedMethod->matchedRefMethod; + $matchedMethodName = $matchedRefMethod->getName(); - if (!in_array($adviceContainerName, $value[$methodType][$matchedMethodName] ?? [])) { - $value[$methodType][$matchedMethodName][] = $adviceContainerName; - } + $adviceContainerName = $adviceContainer->getName(); + + if (!in_array($adviceContainerName, $value[$methodType][$matchedMethodName] ?? [], true)) { + $value[$methodType][$matchedMethodName][] = $adviceContainerName; } } } @@ -182,25 +185,27 @@ private function buildMethods(): array $methods = []; foreach ($this->adviceContainers as $adviceContainer) { - if ($adviceContainer instanceof MethodAdviceContainer) { - foreach ($adviceContainer->getMatchedMethods() as $matchedMethod) { - $refMethod = $matchedMethod->matchedRefMethod; - $methodName = $refMethod->getName(); - - // Internal methods cannot be woven, - // so we skip them - if ($refMethod->getDeclaringClass()->isInternal()) { - continue; - } - - // Check if the method was already built - if (array_key_exists($methodName, $methods)) { - continue; - } - - // Build the method - $methods[$methodName] = $this->buildMethod($refMethod); + if (!$adviceContainer instanceof MethodAdviceContainer) { + continue; + } + + foreach ($adviceContainer->getMatchedMethods() as $matchedMethod) { + $refMethod = $matchedMethod->matchedRefMethod; + $methodName = $refMethod->getName(); + + // Internal methods cannot be woven, + // so we skip them + if ($refMethod->getDeclaringClass()->isInternal()) { + continue; } + + // Check if the method was already built + if (array_key_exists($methodName, $methods)) { + continue; + } + + // Build the method + $methods[$methodName] = $this->buildMethod($refMethod); } } @@ -220,48 +225,52 @@ private function buildMethod(BetterReflectionMethod $refMethod): Method { $refMethod = new ReflectionMethod($refMethod); /** @noinspection PhpUnhandledExceptionInspection */ - $method = (new Factory)->fromMethodReflection( - $refMethod, - ); + $method = (new Factory())->fromMethodReflection($refMethod); $methodName = $refMethod->getName(); $parameters = $method->getParameters(); foreach ($parameters as $name => $parameter) { - if ($parameter instanceof PromotedParameter) { - // Promotion belongs to the original constructor, which the - // interceptor invokes. A forwarding method must not own a second slot. - $plain = new Parameter($parameter->getName()); - $plain->setType($parameter->getType()); - $plain->setNullable($parameter->isNullable()); - $plain->setReference($parameter->isReference()); - $plain->setAttributes($parameter->getAttributes()); - if ($parameter->hasDefaultValue()) { - $plain->setDefaultValue($parameter->getDefaultValue()); - } - $parameters[$name] = $plain; + if (!$parameter instanceof PromotedParameter) { + continue; } + + $plain = new Parameter($parameter->getName()); + $plain->setType($parameter->getType()); + $plain->setNullable($parameter->isNullable()); + $plain->setReference($parameter->isReference()); + $plain->setAttributes($parameter->getAttributes()); + if ($parameter->hasDefaultValue()) { + $plain->setDefaultValue($parameter->getDefaultValue()); + } + $parameters[$name] = $plain; } - $method->setParameters($parameters); + $method->setParameters(array_values($parameters)); // Add "return" if the method has a return type - $return = (string)$method->getReturnType() !== 'void' ? 'return ' : ''; + $return = (string) $method->getReturnType() !== 'void' ? 'return ' : ''; // Add parameters as an array with the parameter name as key $parametersArray = $this->getParametersArray($refMethod); - $parameters = $parametersArray ? ", $parametersArray" : ''; + $parameters = $parametersArray ? ", {$parametersArray}" : ''; // Static methods don't have $this $isStatic = $refMethod->isStatic(); - $context = $isStatic ? 'null' : '$this'; + $context = $isStatic ? 'null' : '$this'; - $body = $return + $body = + $return . 'call_user_func_array(' - . 'self::$' . JoinPoint::JOIN_POINTS_PARAMETER_NAME - . '[\'' . JoinPoint::TYPE_METHOD . '\']' - . '[\'' . $methodName . '\'], ' - . "[$context" - . "$parameters]);"; + . 'self::$' + . JoinPoint::JOIN_POINTS_PARAMETER_NAME + . '[\'' + . JoinPoint::TYPE_METHOD + . '\']' + . '[\'' + . $methodName + . '\'], ' + . "[{$context}" + . "{$parameters}]);"; /** * @example @@ -289,7 +298,7 @@ private function buildMethod(BetterReflectionMethod $refMethod): Method private function getParametersArray(ReflectionMethod $method): ?string { $parameters = $method->getParameters(); - if (empty($parameters)) { + if ($parameters === []) { return null; } @@ -313,10 +322,10 @@ private function getParametersArray(ReflectionMethod $method): ?string private function injectJoinPoints(string &$file): void { $reflectionClass = $this->code->getReflectionClass(); - $shortClassName = $reflectionClass->getShortName(); + $shortClassName = $reflectionClass->getShortName(); // language=PHP - $code = "DI::get(JoinPointInjector::class)->injectJoinPoints($shortClassName::class);"; + $code = "DI::get(JoinPointInjector::class)->injectJoinPoints({$shortClassName}::class);"; $file .= "\n" . $code; } diff --git a/src/Invocation/BeforeMethodInvocation.php b/src/Invocation/BeforeMethodInvocation.php index 7000030..9c25cef 100644 --- a/src/Invocation/BeforeMethodInvocation.php +++ b/src/Invocation/BeforeMethodInvocation.php @@ -10,6 +10,4 @@ * This class is used to pass information to {@see Before} advices in form of a * parameter. */ -class BeforeMethodInvocation extends MethodInvocation -{ -} +class BeforeMethodInvocation extends MethodInvocation {} diff --git a/src/Invocation/MethodInvocation.php b/src/Invocation/MethodInvocation.php index b2779d5..64bdb38 100644 --- a/src/Invocation/MethodInvocation.php +++ b/src/Invocation/MethodInvocation.php @@ -22,10 +22,10 @@ abstract class MethodInvocation */ public function __construct( private readonly ?object $subject, - private readonly string $className, - private readonly string $methodName, - protected mixed $result, - private array &$arguments, + private readonly string $className, + private readonly string $methodName, + protected mixed $result, + private array &$arguments, ) {} /** @@ -39,9 +39,8 @@ public function getArgument(int|string $nameOrIndex): mixed { if (is_int($nameOrIndex)) { return array_values($this->arguments)[$nameOrIndex] ?? null; - } else { - return $this->arguments[$nameOrIndex] ?? null; } + return $this->arguments[$nameOrIndex] ?? null; } /** @@ -56,15 +55,15 @@ public function setArgument(int|string $nameOrIndex, mixed $value): void { if (is_int($nameOrIndex)) { $this->arguments[array_keys($this->arguments)[$nameOrIndex]] = $value; - } else { - $this->arguments[$nameOrIndex] = $value; + return; } + $this->arguments[$nameOrIndex] = $value; } /** * Get all arguments. * - * @return array + * @return array */ public function getArguments(): array { @@ -93,13 +92,10 @@ public function setArguments(array $arguments): void public function getAdviceType(): AdviceType { return match (true) { - $this instanceof AroundMethodInvocation => AdviceType::Around, - $this instanceof BeforeMethodInvocation => AdviceType::Before, - $this instanceof AfterMethodInvocation => AdviceType::After, - // TODO: Implement - $this instanceof AfterReturningMethodInvocation => AdviceType::AfterReturning, - // TODO: Implement - $this instanceof AfterThrowingMethodInvocation => AdviceType::AfterThrowing, + $this instanceof AroundMethodInvocation => AdviceType::Around, + $this instanceof BeforeMethodInvocation => AdviceType::Before, + $this instanceof AfterMethodInvocation => AdviceType::After, + default => throw new \UnhandledMatchError('Unsupported advice invocation type.'), }; } @@ -115,7 +111,9 @@ public function getSubject(): ?object return $this->subject; } - /** Access the subject's properties, optionally in an original declaring scope. */ + /** + * Access the subject's properties, optionally in an original declaring scope. + */ public function properties(?string $declaringClass = null): PropertyAccessor { return new PropertyAccessor($this->subject ?? $this->className, $declaringClass); diff --git a/src/Invocation/PropertyAccessor.php b/src/Invocation/PropertyAccessor.php index ab39185..1c1dc0d 100644 --- a/src/Invocation/PropertyAccessor.php +++ b/src/Invocation/PropertyAccessor.php @@ -1,9 +1,13 @@ subject, $name, $this->declaringClass); - return $value; + return PropertyAccess::reference($this->subject, $name, $this->declaringClass); } public function __set(string $name, mixed $value): void diff --git a/src/PropertyAccess.php b/src/PropertyAccess.php index 9f0572e..9a4ecae 100644 --- a/src/PropertyAccess.php +++ b/src/PropertyAccess.php @@ -1,9 +1,10 @@ isInitialized(is_object($subject) ? $subject : null)) { - throw new Error('Property ' . $property->getDeclaringClass()->getName() - . '::$' . $name . ' must not be accessed before initialization'); + throw new Error( + 'Property ' + . $property->getDeclaringClass()->getName() + . '::$' + . $name + . ' must not be accessed before initialization', + ); } return $property->getValue(is_object($subject) ? $subject : null); } @@ -46,14 +52,29 @@ public static function &reference(object|string $subject, string $name, ?string $property = self::resolve($subject, $name, $declaringClass); // Do not initialize nullable properties or invoke __get after explicit unset. if (!$property->isInitialized(is_object($subject) ? $subject : null)) { - throw new Error('Typed property ' . $property->getDeclaringClass()->getName() - . '::$' . $name . ' must not be accessed before initialization'); + throw new Error( + 'Typed property ' + . $property->getDeclaringClass()->getName() + . '::$' + . $name + . ' must not be accessed before initialization', + ); } $scope = $property->getDeclaringClass()->getName(); - $read = $property->isStatic() - ? Closure::bind(static function &() use ($name) { return self::$$name; }, null, $scope) - : Closure::bind(function &() use ($name) { return $this->$name; }, $subject, $scope); - $value =& $read(); + if ($property->isStatic()) { + $read = Closure::bind(static fn&(): mixed => $scope::${$name}, null, $scope); + /** @var mixed $value A property can hold any value; its declared type is checked by PHP. */ + $value = &$read(); + return $value; + } + if (!is_object($subject)) { + throw new LogicException('An instance property requires an object.'); + } + // resolve() validates the declaration and scope; PHP retains the reference and its type constraint. + // @mago-expect analysis:string-member-selector,ambiguous-object-property-access + $read = Closure::bind(static fn&(object $target): mixed => $target->{$name}, null, $scope); + /** @var mixed $value A property can hold any value; its declared type is checked by PHP. */ + $value = &$read($subject); return $value; } @@ -78,23 +99,37 @@ public static function remove(object|string $subject, string $name, ?string $dec return; } if ($property->isStatic()) { - throw new Error("Cannot unset static property \$$name."); + throw new Error("Cannot unset static property \${$name}."); + } + if (!is_object($subject)) { + throw new LogicException('An instance property requires an object.'); } if (!$property->isInitialized($subject)) { return; } - $remove = Closure::bind(function () use ($name): void { - unset($this->$name); - }, $subject, $property->getDeclaringClass()->getName()); - $remove(); + $remove = Closure::bind( + static function (object $target) use ($name): void { + // resolve() has checked this declared property; runtime names are inherent to the accessor API. + // @mago-expect analysis:string-member-selector,ambiguous-object-property-access + unset($target->{$name}); + }, + null, + $property->getDeclaringClass()->getName(), + ); + $remove($subject); } - private static function resolve(object|string $subject, string $name, ?string $declaringClass = null): ReflectionProperty - { + private static function resolve( + object|string $subject, + string $name, + ?string $declaringClass = null, + ): ReflectionProperty { $matches = []; $scope = $declaringClass === null ? null : ltrim($declaringClass, '\\'); - $class = new ReflectionClass($subject); - do { + if (is_string($subject) && !class_exists($subject)) { + throw new ReflectionException("Class {$subject} does not exist."); + } + for ($class = new ReflectionClass($subject); $class !== false; $class = $class->getParentClass()) { $originalName = $class->getName(); if (str_ends_with($originalName, CachePaths::PROXIED_SUFFIX)) { $originalName = substr($originalName, 0, -strlen(CachePaths::PROXIED_SUFFIX)); @@ -115,17 +150,19 @@ private static function resolve(object|string $subject, string $name, ?string $d $key = $independent ? $class->getName() : 'inherited'; $matches[$key] = $property; } - } while ($class = $class->getParentClass()); + } if (!$matches) { - throw new ReflectionException("Property \$$name does not exist in the requested scope."); + throw new ReflectionException("Property \${$name} does not exist in the requested scope."); } if (count($matches) > 1) { - throw new LogicException("Property \$$name is ambiguous; pass its original declaring class to PropertyAccess::get()/set()."); + throw new LogicException( + "Property \${$name} is ambiguous; pass its original declaring class to PropertyAccess::get()/set().", + ); } - $property = reset($matches); + $property = array_values($matches)[0]; if (is_string($subject) && !$property->isStatic()) { - throw new LogicException("An object is required to access instance property \$$name."); + throw new LogicException("An object is required to access instance property \${$name}."); } return $property; } diff --git a/tests/ClassLoaderMockTrait.php b/tests/ClassLoaderMockTrait.php index d39bdc5..8f18910 100644 --- a/tests/ClassLoaderMockTrait.php +++ b/tests/ClassLoaderMockTrait.php @@ -20,7 +20,11 @@ private function findClassMock(string $class): string $this->findClassLoader(); } - return $this->classLoader->findFile($class); + assert($this->classLoader instanceof ClassLoader, 'The invocation must match the configured test fixture.'); + /** @var mixed $file */ + $file = $this->classLoader->findFile($class); + Assert::assertIsString($file); + return $file; } private function findOriginalClassMock(string $class): string @@ -29,66 +33,58 @@ private function findOriginalClassMock(string $class): string $this->findClassLoader(); } + assert($this->classLoader instanceof ClassLoader, 'The invocation must match the configured test fixture.'); $original = new ReflectionProperty(ClassLoader::class, 'originalClassLoader'); - $original = $original->getValue($this->classLoader); - return $original->findFile($class); + /** @var mixed $loader */ + $loader = $original->getValue($this->classLoader); + Assert::assertInstanceOf(\Composer\Autoload\ClassLoader::class, $loader); + $file = $loader->findFile($class); + Assert::assertIsString($file); + return $file; } private function findClassLoader(): void { foreach (spl_autoload_functions() as $function) { - if (is_array($function) && $function[0] instanceof ClassLoader) { - $this->classLoader = $function[0]; - break; + if (!(is_array($function) && $function[0] instanceof ClassLoader)) { + continue; } + + $this->classLoader = $function[0]; + break; } } public function assertWillBeWoven(string $className): void { $originalFilePath = Path::resolve($this->findOriginalClassMock($className)); + Assert::assertIsString($originalFilePath); - $wovenPath = - FilterInjector::PHP_FILTER_READ . - StreamFilter::FILTER_ID . '/resource=' . - $originalFilePath; + $wovenPath = FilterInjector::PHP_FILTER_READ . StreamFilter::FILTER_ID . '/resource=' . $originalFilePath; $filePathMock = $this->findClassMock($className); - Assert::assertEquals( - $wovenPath, - $filePathMock, - "$className will not be woven", - ); + Assert::assertEquals($wovenPath, $filePathMock, "{$className} will not be woven"); } public function assertAspectLoadedFromCache(string $className): void { $filePath = Path::resolve($this->findOriginalClassMock($className)); + Assert::assertIsString($filePath); - $cachePath = - FilterInjector::PHP_FILTER_READ . - CachedStreamFilter::CACHED_FILTER_ID . '/resource=' . - $filePath; + $cachePath = FilterInjector::PHP_FILTER_READ . CachedStreamFilter::CACHED_FILTER_ID . '/resource=' . $filePath; $filePathMock = $this->findClassMock($className); - Assert::assertEquals( - $cachePath, - $filePathMock, - "$className will not be loaded from cache", - ); + Assert::assertEquals($cachePath, $filePathMock, "{$className} will not be loaded from cache"); } public function assertAspectNotApplied(string $className): void { $originalFilePath = Path::resolve($this->findOriginalClassMock($className)); - $filePathMock = $this->findClassMock($className); + Assert::assertIsString($originalFilePath); + $filePathMock = $this->findClassMock($className); - Assert::assertEquals( - $originalFilePath, - $filePathMock, - "$className will be woven", - ); + Assert::assertEquals($originalFilePath, $filePathMock, "{$className} will be woven"); } } diff --git a/tests/Functional/AdviceApplication/AdviceMatchingAbstractMethod/AdviceMatchingAbstractMethodTest.php b/tests/Functional/AdviceApplication/AdviceMatchingAbstractMethod/AdviceMatchingAbstractMethodTest.php index c304684..57c2dba 100644 --- a/tests/Functional/AdviceApplication/AdviceMatchingAbstractMethod/AdviceMatchingAbstractMethodTest.php +++ b/tests/Functional/AdviceApplication/AdviceMatchingAbstractMethod/AdviceMatchingAbstractMethodTest.php @@ -29,9 +29,6 @@ public function testAbstractMethod(): void $result = $uploader->upload('C:\Windows\Temp\file.txt'); /** @noinspection PhpConditionAlreadyCheckedInspection */ - $this->assertEquals( - 'C:/Windows/Temp/file.txt', - $result - ); + static::assertSame('C:/Windows/Temp/file.txt', $result); } } diff --git a/tests/Functional/AdviceApplication/AdviceMatchingAbstractMethod/Aspect/FileUploaderAspect.php b/tests/Functional/AdviceApplication/AdviceMatchingAbstractMethod/Aspect/FileUploaderAspect.php index aaf223d..7995082 100644 --- a/tests/Functional/AdviceApplication/AdviceMatchingAbstractMethod/Aspect/FileUploaderAspect.php +++ b/tests/Functional/AdviceApplication/AdviceMatchingAbstractMethod/Aspect/FileUploaderAspect.php @@ -1,4 +1,5 @@ proceed(); $modifiedResult = str_replace('\\', '/', $result); $invocation->setResult($modifiedResult); diff --git a/tests/Functional/AdviceApplication/AdviceMatchingAbstractMethod/Kernel.php b/tests/Functional/AdviceApplication/AdviceMatchingAbstractMethod/Kernel.php index c76b9f4..13aa8e4 100644 --- a/tests/Functional/AdviceApplication/AdviceMatchingAbstractMethod/Kernel.php +++ b/tests/Functional/AdviceApplication/AdviceMatchingAbstractMethod/Kernel.php @@ -10,6 +10,7 @@ class Kernel extends AopKernel { protected ?string $cacheDir = Util::CACHE_DIR; + /** @var array */ protected array $aspects = [ FileUploaderAspect::class, ]; diff --git a/tests/Functional/AdviceApplication/ExplicitClassLevelAspect/Aspect/LoggingAspect.php b/tests/Functional/AdviceApplication/ExplicitClassLevelAspect/Aspect/LoggingAspect.php index 147091f..46dd58f 100644 --- a/tests/Functional/AdviceApplication/ExplicitClassLevelAspect/Aspect/LoggingAspect.php +++ b/tests/Functional/AdviceApplication/ExplicitClassLevelAspect/Aspect/LoggingAspect.php @@ -1,4 +1,5 @@ getMethodName(); - $logMessage = sprintf( - "Method '%s' executed.", - $methodName, - ); + $logMessage = sprintf("Method '%s' executed.", $methodName); $logger = Logger::getInstance(); $logger->log($logMessage); } - #[Before( - method: 'updateInventory', - )] + #[Before(method: 'updateInventory')] public function logUpdateInventory(BeforeMethodInvocation $invocation): void { $methodName = $invocation->getMethodName(); - $logMessage = sprintf( - "Method '%s' executed.", - $methodName, - ); + $logMessage = sprintf("Method '%s' executed.", $methodName); $logger = Logger::getInstance(); $logger->log($logMessage); diff --git a/tests/Functional/AdviceApplication/ExplicitClassLevelAspect/ExplicitClassLevelAspectTest.php b/tests/Functional/AdviceApplication/ExplicitClassLevelAspect/ExplicitClassLevelAspectTest.php index 16b72cc..43b25c5 100644 --- a/tests/Functional/AdviceApplication/ExplicitClassLevelAspect/ExplicitClassLevelAspectTest.php +++ b/tests/Functional/AdviceApplication/ExplicitClassLevelAspect/ExplicitClassLevelAspectTest.php @@ -50,7 +50,7 @@ private function executeTest(): void $logger = Logger::getInstance(); - $logs = $logger->getLogs(); + $logs = $logger->getLogs(); $this->assertCount(6, $logs); $updateInventoryExecuted = 0; @@ -61,7 +61,8 @@ private function executeTest(): void foreach ($logs as $log) { if ($log === $updateInventoryLog) { $updateInventoryExecuted++; - } elseif ($log === $checkInventoryLog) { + } + if ($log === $checkInventoryLog) { $checkInventoryExecuted++; } } diff --git a/tests/Functional/AdviceApplication/ExplicitClassLevelAspect/Target/InventoryTracker.php b/tests/Functional/AdviceApplication/ExplicitClassLevelAspect/Target/InventoryTracker.php index 5667ea1..90e4810 100644 --- a/tests/Functional/AdviceApplication/ExplicitClassLevelAspect/Target/InventoryTracker.php +++ b/tests/Functional/AdviceApplication/ExplicitClassLevelAspect/Target/InventoryTracker.php @@ -7,6 +7,7 @@ #[LoggingAspect] class InventoryTracker { + /** @var array */ private array $inventory = []; public function updateInventory(int $productId, int $quantity): void diff --git a/tests/Functional/AdviceApplication/ExplicitMethodLevelAspect/Aspect/PerformanceAspect.php b/tests/Functional/AdviceApplication/ExplicitMethodLevelAspect/Aspect/PerformanceAspect.php index 93ec978..6db4e00 100644 --- a/tests/Functional/AdviceApplication/ExplicitMethodLevelAspect/Aspect/PerformanceAspect.php +++ b/tests/Functional/AdviceApplication/ExplicitMethodLevelAspect/Aspect/PerformanceAspect.php @@ -21,15 +21,10 @@ public function measure(AroundMethodInvocation $invocation): void $executionTime = $end - $start; - $class = $invocation->getClassName(); + $class = $invocation->getClassName(); $method = $invocation->getMethodName(); - $logMessage = sprintf( - "Method %s::%s executed in %.2f seconds.", - $class, - $method, - $executionTime, - ); + $logMessage = sprintf('Method %s::%s executed in %.2f seconds.', $class, $method, $executionTime); $logger = Logger::getInstance(); $logger->log($logMessage); diff --git a/tests/Functional/AdviceApplication/ExplicitMethodLevelAspect/ExplicitMethodLevelAspectTest.php b/tests/Functional/AdviceApplication/ExplicitMethodLevelAspect/ExplicitMethodLevelAspectTest.php index 4ec112b..fd92246 100644 --- a/tests/Functional/AdviceApplication/ExplicitMethodLevelAspect/ExplicitMethodLevelAspectTest.php +++ b/tests/Functional/AdviceApplication/ExplicitMethodLevelAspect/ExplicitMethodLevelAspectTest.php @@ -70,8 +70,8 @@ private function executeTest(): void $firstLog = $logs[0]; $wildcard = 'Method *::* executed in * seconds.'; - $regex = Regex::fromWildcard($wildcard); - $matches = $regex->matches($firstLog); + $regex = Regex::fromWildcard($wildcard); + $matches = $regex->matches($firstLog); $this->assertTrue($matches); } } diff --git a/tests/Functional/AdviceApplication/ExplicitMethodLevelAspect/Kernel.php b/tests/Functional/AdviceApplication/ExplicitMethodLevelAspect/Kernel.php index 36c32b0..96396a3 100644 --- a/tests/Functional/AdviceApplication/ExplicitMethodLevelAspect/Kernel.php +++ b/tests/Functional/AdviceApplication/ExplicitMethodLevelAspect/Kernel.php @@ -10,6 +10,7 @@ class Kernel extends AopKernel { protected ?string $cacheDir = Util::CACHE_DIR; + /** @var array */ protected array $aspects = [ PerformanceAspect::class, ]; diff --git a/tests/Functional/AdviceApplication/MultipleExplicitMethodLevelAspects/Aspect/SecurityAspect.php b/tests/Functional/AdviceApplication/MultipleExplicitMethodLevelAspects/Aspect/SecurityAspect.php index 8861727..62519e7 100644 --- a/tests/Functional/AdviceApplication/MultipleExplicitMethodLevelAspects/Aspect/SecurityAspect.php +++ b/tests/Functional/AdviceApplication/MultipleExplicitMethodLevelAspects/Aspect/SecurityAspect.php @@ -16,12 +16,13 @@ class SecurityAspect #[Before] public function applySecurityMeasures(BeforeMethodInvocation $invocation): void { + /** @var non-empty-array $arguments */ $arguments = $invocation->getArguments(); - $firstArgument = reset($arguments); - $firstArgumentKey = key($arguments); + $firstArgumentKey = array_key_first($arguments); + $firstArgument = $arguments[$firstArgumentKey]; - if (gettype($firstArgument) === 'array') { + if (is_array($firstArgument)) { $id = &$firstArgument['id']; $id .= self::SECRET_HASH; @@ -30,7 +31,7 @@ public function applySecurityMeasures(BeforeMethodInvocation $invocation): void $invocation->setArguments($arguments); } - if (gettype($firstArgument) === 'string') { + if (is_string($firstArgument)) { $id = &$firstArgument; $id .= self::SECRET_HASH; diff --git a/tests/Functional/AdviceApplication/MultipleExplicitMethodLevelAspects/MultipleExplicitMethodLevelAspectsTest.php b/tests/Functional/AdviceApplication/MultipleExplicitMethodLevelAspects/MultipleExplicitMethodLevelAspectsTest.php index 15c72ad..d66b45a 100644 --- a/tests/Functional/AdviceApplication/MultipleExplicitMethodLevelAspects/MultipleExplicitMethodLevelAspectsTest.php +++ b/tests/Functional/AdviceApplication/MultipleExplicitMethodLevelAspects/MultipleExplicitMethodLevelAspectsTest.php @@ -32,17 +32,16 @@ public function testMultipleExplicitMethodLevelAspects(): void $accountService->createAccount(['id' => $id]); $accounts = $accountService->getAccounts(); - $this->assertCount(1, $accounts); + static::assertCount(1, $accounts); $firstAccount = $accounts[0]; - $this->assertStringEndsWith(SecurityAspect::SECRET_HASH, $firstAccount); + static::assertStringEndsWith(SecurityAspect::SECRET_HASH, $firstAccount); /** @noinspection PhpUnhandledExceptionInspection */ $accountService->deleteAccount($id); $accounts = $accountService->getAccounts(); - $this->assertCount(0, $accounts); - + static::assertCount(0, $accounts); $this->assertWillBeWoven(TransactionService::class); $transactionService = new TransactionService(); @@ -50,15 +49,15 @@ public function testMultipleExplicitMethodLevelAspects(): void $transactionService->createTransaction(['id' => $id]); $transactions = $transactionService->getTransactions(); - $this->assertCount(1, $transactions); + static::assertCount(1, $transactions); $firstTransaction = $transactions[0]; - $this->assertStringEndsWith(SecurityAspect::SECRET_HASH, $firstTransaction); + static::assertStringEndsWith(SecurityAspect::SECRET_HASH, $firstTransaction); /** @noinspection PhpUnhandledExceptionInspection */ $transactionService->rollbackTransaction($id); $transactions = $transactionService->getTransactions(); - $this->assertCount(0, $transactions); + static::assertCount(0, $transactions); } } diff --git a/tests/Functional/AdviceApplication/MultipleExplicitMethodLevelAspects/Target/AccountService.php b/tests/Functional/AdviceApplication/MultipleExplicitMethodLevelAspects/Target/AccountService.php index 3226ad4..0ffbed5 100644 --- a/tests/Functional/AdviceApplication/MultipleExplicitMethodLevelAspects/Target/AccountService.php +++ b/tests/Functional/AdviceApplication/MultipleExplicitMethodLevelAspects/Target/AccountService.php @@ -7,8 +7,10 @@ class AccountService { + /** @var array */ private array $accounts = []; + /** @param array{id: string} $userData */ #[SecurityAspect] public function createAccount(array $userData): void { @@ -18,16 +20,17 @@ public function createAccount(array $userData): void #[SecurityAspect] public function deleteAccount(string $accountId): void { - $accountIndex = array_search($accountId, $this->accounts); + $accountIndex = array_search($accountId, $this->accounts, true); if ($accountIndex === false) { /** @noinspection PhpUnhandledExceptionInspection */ - throw new Exception("Account with id $accountId not found."); + throw new Exception("Account with id {$accountId} not found."); } unset($this->accounts[$accountIndex]); } + /** @return array */ public function getAccounts(): array { return $this->accounts; diff --git a/tests/Functional/AdviceApplication/MultipleExplicitMethodLevelAspects/Target/TransactionService.php b/tests/Functional/AdviceApplication/MultipleExplicitMethodLevelAspects/Target/TransactionService.php index a8c14fc..0c4b544 100644 --- a/tests/Functional/AdviceApplication/MultipleExplicitMethodLevelAspects/Target/TransactionService.php +++ b/tests/Functional/AdviceApplication/MultipleExplicitMethodLevelAspects/Target/TransactionService.php @@ -7,8 +7,10 @@ class TransactionService { + /** @var array */ private array $transactions = []; + /** @param array{id: string} $transactionData */ #[SecurityAspect] public function createTransaction(array $transactionData): void { @@ -18,16 +20,17 @@ public function createTransaction(array $transactionData): void #[SecurityAspect] public function rollbackTransaction(string $transactionId): void { - $transactionIndex = array_search($transactionId, $this->transactions); + $transactionIndex = array_search($transactionId, $this->transactions, true); if ($transactionIndex === false) { /** @noinspection PhpUnhandledExceptionInspection */ - throw new Exception("Transaction with id $transactionId not found."); + throw new Exception("Transaction with id {$transactionId} not found."); } unset($this->transactions[$transactionIndex]); } + /** @return array */ public function getTransactions(): array { return $this->transactions; diff --git a/tests/Functional/AdviceBehavior/AdviceOrder/AdviceOrderTest.php b/tests/Functional/AdviceBehavior/AdviceOrder/AdviceOrderTest.php index a1fa95c..9375119 100644 --- a/tests/Functional/AdviceBehavior/AdviceOrder/AdviceOrderTest.php +++ b/tests/Functional/AdviceBehavior/AdviceOrder/AdviceOrderTest.php @@ -28,14 +28,11 @@ public function testAdviceOrderTest(): void $this->assertWillBeWoven(ArticleManager::class); $articleManager = new ArticleManager(); - $articleManager->createArticle( - 'Hello World', - 'AOP is awesome!', - ); + $articleManager->createArticle('Hello World', 'AOP is awesome!'); $stackTrace = StackTrace::getInstance(); - $this->assertEquals( + static::assertEquals( [ 'checkForSpam', 'validateContent', diff --git a/tests/Functional/AdviceBehavior/AdviceOrder/Aspect/ArticleModerationAspect.php b/tests/Functional/AdviceBehavior/AdviceOrder/Aspect/ArticleModerationAspect.php index ddb4e2b..72543a9 100644 --- a/tests/Functional/AdviceBehavior/AdviceOrder/Aspect/ArticleModerationAspect.php +++ b/tests/Functional/AdviceBehavior/AdviceOrder/Aspect/ArticleModerationAspect.php @@ -1,4 +1,5 @@ addTrace('validateContent'); } - #[After( - class: ArticleManager::class, - method: 'createArticle', - order: -10, - )] - public function checkForSpam() + #[After(class: ArticleManager::class, method: 'createArticle', order: -10)] + public function checkForSpam(): void { $stackTrace = StackTrace::getInstance(); $stackTrace->addTrace('checkForSpam'); } - #[After( - class: ArticleManager::class, - method: 'createArticle', - order: 10, - )] - public function ensureProperFormatting() + #[After(class: ArticleManager::class, method: 'createArticle', order: 10)] + public function ensureProperFormatting(): void { $stackTrace = StackTrace::getInstance(); $stackTrace->addTrace('ensureProperFormatting'); diff --git a/tests/Functional/AdviceBehavior/AdviceOrder/Kernel.php b/tests/Functional/AdviceBehavior/AdviceOrder/Kernel.php index ba70d54..4957f41 100644 --- a/tests/Functional/AdviceBehavior/AdviceOrder/Kernel.php +++ b/tests/Functional/AdviceBehavior/AdviceOrder/Kernel.php @@ -10,6 +10,7 @@ class Kernel extends AopKernel { protected ?string $cacheDir = Util::CACHE_DIR; + /** @var array */ protected array $aspects = [ ArticleModerationAspect::class, ]; diff --git a/tests/Functional/AdviceBehavior/AdviceOrder/Target/ArticleManager.php b/tests/Functional/AdviceBehavior/AdviceOrder/Target/ArticleManager.php index 9f592b8..72b9bf2 100644 --- a/tests/Functional/AdviceBehavior/AdviceOrder/Target/ArticleManager.php +++ b/tests/Functional/AdviceBehavior/AdviceOrder/Target/ArticleManager.php @@ -4,7 +4,7 @@ class ArticleManager { - public function createArticle(string $title, string $content) + public function createArticle(string $title, string $content): void { // Code to create and save an article object } diff --git a/tests/Functional/AdviceBehavior/BeforeAroundAfterAdviceOnSameAdviceMethod/Aspect/CalculatorLoggerAspect.php b/tests/Functional/AdviceBehavior/BeforeAroundAfterAdviceOnSameAdviceMethod/Aspect/CalculatorLoggerAspect.php index cf2efe9..c411751 100644 --- a/tests/Functional/AdviceBehavior/BeforeAroundAfterAdviceOnSameAdviceMethod/Aspect/CalculatorLoggerAspect.php +++ b/tests/Functional/AdviceBehavior/BeforeAroundAfterAdviceOnSameAdviceMethod/Aspect/CalculatorLoggerAspect.php @@ -1,4 +1,5 @@ getAdviceType(); @@ -38,7 +30,10 @@ public function logCalculation(MethodInvocation $invocation): void } if ($adviceType === AdviceType::Around) { - assert($invocation instanceof AroundMethodInvocation); + assert( + $invocation instanceof AroundMethodInvocation, + 'The invocation must match the configured test fixture.', + ); $startTime = microtime(true); $invocation->proceed(); @@ -50,6 +45,11 @@ public function logCalculation(MethodInvocation $invocation): void } if ($adviceType === AdviceType::After) { + assert( + $invocation instanceof \Okapi\Aop\Invocation\AfterMethodInvocation, + 'The invocation must match the configured test fixture.', + ); + /** @var int $result */ $result = $invocation->proceed(); $message = sprintf('Calculation result: %d', $result); diff --git a/tests/Functional/AdviceBehavior/BeforeAroundAfterAdviceOnSameAdviceMethod/BeforeAroundAfterAdviceOnSameAdviceMethodTest.php b/tests/Functional/AdviceBehavior/BeforeAroundAfterAdviceOnSameAdviceMethod/BeforeAroundAfterAdviceOnSameAdviceMethodTest.php index c253910..46d8521 100644 --- a/tests/Functional/AdviceBehavior/BeforeAroundAfterAdviceOnSameAdviceMethod/BeforeAroundAfterAdviceOnSameAdviceMethodTest.php +++ b/tests/Functional/AdviceBehavior/BeforeAroundAfterAdviceOnSameAdviceMethod/BeforeAroundAfterAdviceOnSameAdviceMethodTest.php @@ -28,23 +28,23 @@ public function testBeforeAroundAfterAdviceOnSameAdviceMethod(): void $calculator = new Calculator(); $result = $calculator->add(2, 3); - $this->assertSame(5, $result); + static::assertSame(5, $result); $logger = Logger::getInstance(); $logs = $logger->getLogs(); - $this->assertCount(3, $logs); + static::assertCount(3, $logs); $log1 = $logs[0]; - $this->assertSame('Starting calculation...', $log1); + static::assertSame('Starting calculation...', $log1); - $log2 = $logs[1]; + $log2 = $logs[1]; $wildcard = 'Calculation took * seconds'; - $regex = Regex::fromWildcard($wildcard); - $matches = $regex->matches($log2); - $this->assertTrue($matches); + $regex = Regex::fromWildcard($wildcard); + $matches = $regex->matches($log2); + static::assertTrue($matches); $log3 = $logs[2]; - $this->assertSame('Calculation result: 5', $log3); + static::assertSame('Calculation result: 5', $log3); } } diff --git a/tests/Functional/AdviceBehavior/BeforeAroundAfterAdviceOnSameAdviceMethod/Kernel.php b/tests/Functional/AdviceBehavior/BeforeAroundAfterAdviceOnSameAdviceMethod/Kernel.php index e00921b..2d5f064 100644 --- a/tests/Functional/AdviceBehavior/BeforeAroundAfterAdviceOnSameAdviceMethod/Kernel.php +++ b/tests/Functional/AdviceBehavior/BeforeAroundAfterAdviceOnSameAdviceMethod/Kernel.php @@ -10,6 +10,7 @@ class Kernel extends AopKernel { protected ?string $cacheDir = Util::CACHE_DIR; + /** @var array */ protected array $aspects = [ CalculatorLoggerAspect::class, ]; diff --git a/tests/Functional/AdviceBehavior/BeforeAroundAfterAdviceOnSameTargetMethod/Aspect/PaymentProcessorAspect.php b/tests/Functional/AdviceBehavior/BeforeAroundAfterAdviceOnSameTargetMethod/Aspect/PaymentProcessorAspect.php index 3b0022d..e9bd555 100644 --- a/tests/Functional/AdviceBehavior/BeforeAroundAfterAdviceOnSameTargetMethod/Aspect/PaymentProcessorAspect.php +++ b/tests/Functional/AdviceBehavior/BeforeAroundAfterAdviceOnSameTargetMethod/Aspect/PaymentProcessorAspect.php @@ -1,4 +1,5 @@ getArgument('amount'); if ($amount < 0) { @@ -30,49 +29,35 @@ public function checkPaymentAmount(BeforeMethodInvocation $invocation): void } } - #[Around( - class: PaymentProcessor::class, - method: 'processPayment', - )] + #[Around(class: PaymentProcessor::class, method: 'processPayment')] public function logPayment(AroundMethodInvocation $invocation): void { $startTime = microtime(true); $invocation->proceed(); - $endTime = microtime(true); + $endTime = microtime(true); $elapsedTime = $endTime - $startTime; + /** @var float $amount */ $amount = $invocation->getArgument('amount'); - $logMessage = sprintf( - 'Payment processed for amount $%.2f in %.2f seconds', - $amount, - $elapsedTime, - ); + $logMessage = sprintf('Payment processed for amount $%.2f in %.2f seconds', $amount, $elapsedTime); $logger = Logger::getInstance(); $logger->log($logMessage); } - #[After( - class: PaymentProcessor::class, - method: 'processPayment', - )] + #[After(class: PaymentProcessor::class, method: 'processPayment')] public function sendEmailNotification(AfterMethodInvocation $invocation): void { + /** @var bool $result */ $result = $invocation->proceed(); + /** @var float $amount */ $amount = $invocation->getArgument('amount'); - $message = sprintf( - 'Payment processed for amount $%.2f', - $amount, - ); - if ($result === true) { - $message .= ' - Payment successful'; - } else { - $message .= ' - Payment failed'; - } + $message = sprintf('Payment processed for amount $%.2f', $amount); + $message .= $result ? ' - Payment successful' : ' - Payment failed'; $mailQueue = MailQueue::getInstance(); $mailQueue->addMail($message); diff --git a/tests/Functional/AdviceBehavior/BeforeAroundAfterAdviceOnSameTargetMethod/BeforeAroundAfterAdviceOnSameTargetMethodTest.php b/tests/Functional/AdviceBehavior/BeforeAroundAfterAdviceOnSameTargetMethod/BeforeAroundAfterAdviceOnSameTargetMethodTest.php index 9bb4d61..760c79f 100644 --- a/tests/Functional/AdviceBehavior/BeforeAroundAfterAdviceOnSameTargetMethod/BeforeAroundAfterAdviceOnSameTargetMethodTest.php +++ b/tests/Functional/AdviceBehavior/BeforeAroundAfterAdviceOnSameTargetMethod/BeforeAroundAfterAdviceOnSameTargetMethodTest.php @@ -1,4 +1,5 @@ processPayment($amount); } catch (InvalidArgumentException $e) { $exceptionThrown = true; - $this->assertSame( - 'Invalid payment amount', - $e->getMessage(), - ); + static::assertSame('Invalid payment amount', $e->getMessage()); } - $this->assertTrue($exceptionThrown); + static::assertTrue($exceptionThrown); // Test with a valid payment amount - $amount = 420.00; + $amount = 420.00; $success = $processor->processPayment($amount); - $this->assertTrue($success); + static::assertTrue($success); // Test that the log message was printed $logger = Logger::getInstance(); - $logs = $logger->getLogs(); - $this->assertCount(1, $logs); + $logs = $logger->getLogs(); + static::assertCount(1, $logs); $logMessage = $logs[0]; - $wildcard = 'Payment processed for amount $* in * seconds'; - $regex = Regex::fromWildcard($wildcard); - $matches = $regex->matches($logMessage); - $this->assertTrue($matches); + $wildcard = 'Payment processed for amount $* in * seconds'; + $regex = Regex::fromWildcard($wildcard); + $matches = $regex->matches($logMessage); + static::assertTrue($matches); // Test that the email notification was sent $mailQueue = MailQueue::getInstance(); - $mails = $mailQueue->getMails(); - $this->assertCount(1, $mails); - $mail = $mails[0]; + $mails = $mailQueue->getMails(); + static::assertCount(1, $mails); + $mail = $mails[0]; $wildcard = 'Payment processed for amount $* - Payment successful'; - $regex = Regex::fromWildcard($wildcard); - $matches = $regex->matches($mail); - $this->assertTrue($matches); + $regex = Regex::fromWildcard($wildcard); + $matches = $regex->matches($mail); + static::assertTrue($matches); } } diff --git a/tests/Functional/AdviceBehavior/BeforeAroundAfterAdviceOnSameTargetMethod/Kernel.php b/tests/Functional/AdviceBehavior/BeforeAroundAfterAdviceOnSameTargetMethod/Kernel.php index 823018a..cadf2b3 100644 --- a/tests/Functional/AdviceBehavior/BeforeAroundAfterAdviceOnSameTargetMethod/Kernel.php +++ b/tests/Functional/AdviceBehavior/BeforeAroundAfterAdviceOnSameTargetMethod/Kernel.php @@ -10,6 +10,7 @@ class Kernel extends AopKernel { protected ?string $cacheDir = Util::CACHE_DIR; + /** @var array */ protected array $aspects = [ PaymentProcessorAspect::class, ]; diff --git a/tests/Functional/AdviceBehavior/BeforeAroundAfterAdviceOnSameTargetMethod/Target/PaymentProcessor.php b/tests/Functional/AdviceBehavior/BeforeAroundAfterAdviceOnSameTargetMethod/Target/PaymentProcessor.php index 4e9a273..6f396f6 100644 --- a/tests/Functional/AdviceBehavior/BeforeAroundAfterAdviceOnSameTargetMethod/Target/PaymentProcessor.php +++ b/tests/Functional/AdviceBehavior/BeforeAroundAfterAdviceOnSameTargetMethod/Target/PaymentProcessor.php @@ -1,4 +1,5 @@ getClassName() === SmsSender::class) { throw new Error('SmsSender should not be intercepted.'); diff --git a/tests/Functional/AdviceBehavior/ClassHierarchyAspect/ClassHierarchyAspectTest.php b/tests/Functional/AdviceBehavior/ClassHierarchyAspect/ClassHierarchyAspectTest.php index b73acd4..3ef236b 100644 --- a/tests/Functional/AdviceBehavior/ClassHierarchyAspect/ClassHierarchyAspectTest.php +++ b/tests/Functional/AdviceBehavior/ClassHierarchyAspect/ClassHierarchyAspectTest.php @@ -27,21 +27,20 @@ public function testClassHierarchyAspect(): void $emailSender = new EmailSender(); $recipient = 'test@test.com'; - $subject = 'Test'; - $body = 'Test'; - $result = $emailSender->send($recipient, $subject, $body); - - $this->assertTrue($result); + $subject = 'Test'; + $body = 'Test'; + $result = $emailSender->send($recipient, $subject, $body); + static::assertTrue($result); $this->assertAspectNotApplied(SmsSender::class); $smsSender = new SmsSender(); $recipient = '123456789'; - $message = 'Test'; - $result = $smsSender->send($recipient, $message); + $message = 'Test'; + $result = $smsSender->send($recipient, $message); // Should not throw an error - $this->assertTrue($result); + static::assertTrue($result); } } diff --git a/tests/Functional/AdviceBehavior/ClassHierarchyAspect/Kernel.php b/tests/Functional/AdviceBehavior/ClassHierarchyAspect/Kernel.php index a98ee87..49a3438 100644 --- a/tests/Functional/AdviceBehavior/ClassHierarchyAspect/Kernel.php +++ b/tests/Functional/AdviceBehavior/ClassHierarchyAspect/Kernel.php @@ -10,6 +10,7 @@ class Kernel extends AopKernel { protected ?string $cacheDir = Util::CACHE_DIR; + /** @var array */ protected array $aspects = [ NotificationAspect::class, ]; diff --git a/tests/Functional/AdviceBehavior/ExceptionInsideAdvice/Aspect/CommentFilterAspect.php b/tests/Functional/AdviceBehavior/ExceptionInsideAdvice/Aspect/CommentFilterAspect.php index 867d496..fc7afe5 100644 --- a/tests/Functional/AdviceBehavior/ExceptionInsideAdvice/Aspect/CommentFilterAspect.php +++ b/tests/Functional/AdviceBehavior/ExceptionInsideAdvice/Aspect/CommentFilterAspect.php @@ -1,4 +1,5 @@ getArgument('comment'); $inappropriateWords = ['bad', 'terrible', 'awful']; diff --git a/tests/Functional/AdviceBehavior/ExceptionInsideAdvice/ExceptionInsideAdviceTest.php b/tests/Functional/AdviceBehavior/ExceptionInsideAdvice/ExceptionInsideAdviceTest.php index b1f7b33..e56e921 100644 --- a/tests/Functional/AdviceBehavior/ExceptionInsideAdvice/ExceptionInsideAdviceTest.php +++ b/tests/Functional/AdviceBehavior/ExceptionInsideAdvice/ExceptionInsideAdviceTest.php @@ -27,7 +27,7 @@ public function testExceptionInsideAdvice(): void $commentController = new CommentController(); $commentController->saveComment('This is a good comment'); - $this->assertTrue(true); + static::assertTrue(true); $this->expectException(Exception::class); $this->expectExceptionMessage('Comment contains inappropriate language!'); diff --git a/tests/Functional/AdviceBehavior/ExceptionInsideAdvice/Kernel.php b/tests/Functional/AdviceBehavior/ExceptionInsideAdvice/Kernel.php index 6aeedde..4bfe036 100644 --- a/tests/Functional/AdviceBehavior/ExceptionInsideAdvice/Kernel.php +++ b/tests/Functional/AdviceBehavior/ExceptionInsideAdvice/Kernel.php @@ -10,6 +10,7 @@ class Kernel extends AopKernel { protected ?string $cacheDir = Util::CACHE_DIR; + /** @var array */ protected array $aspects = [ CommentFilterAspect::class, ]; diff --git a/tests/Functional/AdviceBehavior/Include/Aspect/DatabaseModifierAspect.php b/tests/Functional/AdviceBehavior/Include/Aspect/DatabaseModifierAspect.php index 68535ca..dcb396a 100644 --- a/tests/Functional/AdviceBehavior/Include/Aspect/DatabaseModifierAspect.php +++ b/tests/Functional/AdviceBehavior/Include/Aspect/DatabaseModifierAspect.php @@ -1,25 +1,22 @@ -properties()->data = [ - 'd' => 4, - 'e' => 5, - 'f' => 6, - ]; - } -} +use Okapi\Aop\Tests\Functional\AdviceBehavior\Include\Target\SecureDatabaseService; + +#[Aspect] +class DatabaseModifierAspect +{ + #[After(class: SecureDatabaseService::class, method: 'load')] + public function modifyData(AfterMethodInvocation $invocation): void + { + $invocation->properties()->__set('data', [ + 'd' => 4, + 'e' => 5, + 'f' => 6, + ]); + } +} diff --git a/tests/Functional/AdviceBehavior/Include/Database/data.php b/tests/Functional/AdviceBehavior/Include/Database/data.php index 11908df..e6a11d9 100644 --- a/tests/Functional/AdviceBehavior/Include/Database/data.php +++ b/tests/Functional/AdviceBehavior/Include/Database/data.php @@ -1,7 +1,7 @@ - 1, - 'b' => 2, - 'c' => 3, -]; + 1, + 'b' => 2, + 'c' => 3, +]; diff --git a/tests/Functional/AdviceBehavior/Include/IncludeTest.php b/tests/Functional/AdviceBehavior/Include/IncludeTest.php index 2fc59cf..6269917 100644 --- a/tests/Functional/AdviceBehavior/Include/IncludeTest.php +++ b/tests/Functional/AdviceBehavior/Include/IncludeTest.php @@ -1,41 +1,41 @@ -assertWillBeWoven(SecureDatabaseService::class); - - $service = new SecureDatabaseService(); - $service->load(); - - $data = $service->getData(); - - $this->assertEquals( - [ - 'd' => 4, - 'e' => 5, - 'f' => 6, - ], - $data, - ); - } -} +assertWillBeWoven(SecureDatabaseService::class); + + $service = new SecureDatabaseService(); + $service->load(); + + $data = $service->getData(); + + static::assertEquals( + [ + 'd' => 4, + 'e' => 5, + 'f' => 6, + ], + $data, + ); + } +} diff --git a/tests/Functional/AdviceBehavior/Include/Kernel.php b/tests/Functional/AdviceBehavior/Include/Kernel.php index 572338c..e177578 100644 --- a/tests/Functional/AdviceBehavior/Include/Kernel.php +++ b/tests/Functional/AdviceBehavior/Include/Kernel.php @@ -1,16 +1,17 @@ - */ + protected array $aspects = [ + DatabaseModifierAspect::class, + ]; +} diff --git a/tests/Functional/AdviceBehavior/Include/Target/SecureDatabaseService.php b/tests/Functional/AdviceBehavior/Include/Target/SecureDatabaseService.php index bc2cafa..3045520 100644 --- a/tests/Functional/AdviceBehavior/Include/Target/SecureDatabaseService.php +++ b/tests/Functional/AdviceBehavior/Include/Target/SecureDatabaseService.php @@ -1,26 +1,30 @@ -data === null) { - $this->data = require dirname(__DIR__, 3) . '/AdviceBehavior/Include/Database/data.php'; - } - - return $this; - } - - public function getData(): array - { - if ($this->data === null) { - $this->load(); - } - - return $this->data; - } -} +|null */ + private ?array $data = null; + + public function load(): self + { + if ($this->data === null) { + /** @var array $data */ + $data = require dirname(__DIR__, 3) . '/AdviceBehavior/Include/Database/data.php'; + $this->data = $data; + } + + return $this; + } + + /** @return array */ + public function getData(): array + { + if ($this->data === null) { + $this->load(); + } + + return $this->data; + } +} diff --git a/tests/Functional/AdviceBehavior/InterfaceAdvice/Aspect/UserInterfaceAspect.php b/tests/Functional/AdviceBehavior/InterfaceAdvice/Aspect/UserInterfaceAspect.php index 6afc879..7516770 100644 --- a/tests/Functional/AdviceBehavior/InterfaceAdvice/Aspect/UserInterfaceAspect.php +++ b/tests/Functional/AdviceBehavior/InterfaceAdvice/Aspect/UserInterfaceAspect.php @@ -1,4 +1,5 @@ assertWillBeWoven(User::class); - $user = new User(); + $user = new User(); $userName = $user->getName(); - $this->assertSame('Jane Doe', $userName); + static::assertSame('Jane Doe', $userName); } } diff --git a/tests/Functional/AdviceBehavior/InterfaceAdvice/Kernel.php b/tests/Functional/AdviceBehavior/InterfaceAdvice/Kernel.php index e46c4b8..8e1126d 100644 --- a/tests/Functional/AdviceBehavior/InterfaceAdvice/Kernel.php +++ b/tests/Functional/AdviceBehavior/InterfaceAdvice/Kernel.php @@ -10,6 +10,7 @@ class Kernel extends AopKernel { protected ?string $cacheDir = Util::CACHE_DIR; + /** @var array */ protected array $aspects = [ UserInterfaceAspect::class, ]; diff --git a/tests/Functional/AdviceBehavior/MagicConstants/Aspect/AspectOnClass.php b/tests/Functional/AdviceBehavior/MagicConstants/Aspect/AspectOnClass.php index 5ba2059..35d1390 100644 --- a/tests/Functional/AdviceBehavior/MagicConstants/Aspect/AspectOnClass.php +++ b/tests/Functional/AdviceBehavior/MagicConstants/Aspect/AspectOnClass.php @@ -9,9 +9,6 @@ #[Aspect] class AspectOnClass { - #[After( - class: TargetClass::class . '*', - method: '*' - )] + #[After(class: TargetClass::class . '*', method: '*')] public function doNothing(): void {} } diff --git a/tests/Functional/AdviceBehavior/MagicConstants/Aspect/AspectOnParent.php b/tests/Functional/AdviceBehavior/MagicConstants/Aspect/AspectOnParent.php index 074e837..b328298 100644 --- a/tests/Functional/AdviceBehavior/MagicConstants/Aspect/AspectOnParent.php +++ b/tests/Functional/AdviceBehavior/MagicConstants/Aspect/AspectOnParent.php @@ -9,9 +9,6 @@ #[Aspect] class AspectOnParent { - #[After( - class: TargetParent::class . '*', - method: '*', - )] + #[After(class: TargetParent::class . '*', method: '*')] public function doNothing(): void {} } diff --git a/tests/Functional/AdviceBehavior/MagicConstants/Aspect/AspectOnTrait.php b/tests/Functional/AdviceBehavior/MagicConstants/Aspect/AspectOnTrait.php index b5744c2..4729617 100644 --- a/tests/Functional/AdviceBehavior/MagicConstants/Aspect/AspectOnTrait.php +++ b/tests/Functional/AdviceBehavior/MagicConstants/Aspect/AspectOnTrait.php @@ -9,9 +9,6 @@ #[Aspect] class AspectOnTrait { - #[After( - class: TargetTrait::class . '*', - method: '*', - )] + #[After(class: TargetTrait::class . '*', method: '*')] public function doNothing(): void {} } diff --git a/tests/Functional/AdviceBehavior/MagicConstants/Kernel/KernelOnClass.php b/tests/Functional/AdviceBehavior/MagicConstants/Kernel/KernelOnClass.php index 8b3cbba..c8c6c42 100644 --- a/tests/Functional/AdviceBehavior/MagicConstants/Kernel/KernelOnClass.php +++ b/tests/Functional/AdviceBehavior/MagicConstants/Kernel/KernelOnClass.php @@ -10,6 +10,7 @@ class KernelOnClass extends AopKernel { protected ?string $cacheDir = Util::CACHE_DIR; + /** @var array */ protected array $aspects = [ AspectOnClass::class, ]; diff --git a/tests/Functional/AdviceBehavior/MagicConstants/Kernel/KernelOnClassAndParent.php b/tests/Functional/AdviceBehavior/MagicConstants/Kernel/KernelOnClassAndParent.php index bb8f78a..29aa9a8 100644 --- a/tests/Functional/AdviceBehavior/MagicConstants/Kernel/KernelOnClassAndParent.php +++ b/tests/Functional/AdviceBehavior/MagicConstants/Kernel/KernelOnClassAndParent.php @@ -11,6 +11,7 @@ class KernelOnClassAndParent extends AopKernel { protected ?string $cacheDir = Util::CACHE_DIR; + /** @var array */ protected array $aspects = [ AspectOnClass::class, AspectOnParent::class, diff --git a/tests/Functional/AdviceBehavior/MagicConstants/Kernel/KernelOnClassAndParentAndTrait.php b/tests/Functional/AdviceBehavior/MagicConstants/Kernel/KernelOnClassAndParentAndTrait.php index cf41055..5fdef18 100644 --- a/tests/Functional/AdviceBehavior/MagicConstants/Kernel/KernelOnClassAndParentAndTrait.php +++ b/tests/Functional/AdviceBehavior/MagicConstants/Kernel/KernelOnClassAndParentAndTrait.php @@ -12,6 +12,7 @@ class KernelOnClassAndParentAndTrait extends AopKernel { protected ?string $cacheDir = Util::CACHE_DIR; + /** @var array */ protected array $aspects = [ AspectOnClass::class, AspectOnParent::class, diff --git a/tests/Functional/AdviceBehavior/MagicConstants/Kernel/KernelOnClassAndTrait.php b/tests/Functional/AdviceBehavior/MagicConstants/Kernel/KernelOnClassAndTrait.php index 45b602d..d0d11d4 100644 --- a/tests/Functional/AdviceBehavior/MagicConstants/Kernel/KernelOnClassAndTrait.php +++ b/tests/Functional/AdviceBehavior/MagicConstants/Kernel/KernelOnClassAndTrait.php @@ -11,6 +11,7 @@ class KernelOnClassAndTrait extends AopKernel { protected ?string $cacheDir = Util::CACHE_DIR; + /** @var array */ protected array $aspects = [ AspectOnClass::class, AspectOnTrait::class, diff --git a/tests/Functional/AdviceBehavior/MagicConstants/Kernel/KernelOnParent.php b/tests/Functional/AdviceBehavior/MagicConstants/Kernel/KernelOnParent.php index 561576c..3457a87 100644 --- a/tests/Functional/AdviceBehavior/MagicConstants/Kernel/KernelOnParent.php +++ b/tests/Functional/AdviceBehavior/MagicConstants/Kernel/KernelOnParent.php @@ -10,6 +10,7 @@ class KernelOnParent extends AopKernel { protected ?string $cacheDir = Util::CACHE_DIR; + /** @var array */ protected array $aspects = [ AspectOnParent::class, ]; diff --git a/tests/Functional/AdviceBehavior/MagicConstants/Kernel/KernelOnParentAndTrait.php b/tests/Functional/AdviceBehavior/MagicConstants/Kernel/KernelOnParentAndTrait.php index fc3ea69..4b6fc69 100644 --- a/tests/Functional/AdviceBehavior/MagicConstants/Kernel/KernelOnParentAndTrait.php +++ b/tests/Functional/AdviceBehavior/MagicConstants/Kernel/KernelOnParentAndTrait.php @@ -11,6 +11,7 @@ class KernelOnParentAndTrait extends AopKernel { protected ?string $cacheDir = Util::CACHE_DIR; + /** @var array */ protected array $aspects = [ AspectOnParent::class, AspectOnTrait::class, diff --git a/tests/Functional/AdviceBehavior/MagicConstants/Kernel/KernelOnTrait.php b/tests/Functional/AdviceBehavior/MagicConstants/Kernel/KernelOnTrait.php index 0d9f100..85a3abe 100644 --- a/tests/Functional/AdviceBehavior/MagicConstants/Kernel/KernelOnTrait.php +++ b/tests/Functional/AdviceBehavior/MagicConstants/Kernel/KernelOnTrait.php @@ -10,6 +10,7 @@ class KernelOnTrait extends AopKernel { protected ?string $cacheDir = Util::CACHE_DIR; + /** @var array */ protected array $aspects = [ AspectOnTrait::class, ]; diff --git a/tests/Functional/AdviceBehavior/MagicConstants/MagicConstantsTest.php b/tests/Functional/AdviceBehavior/MagicConstants/MagicConstantsTest.php index 9493967..4a08a22 100644 --- a/tests/Functional/AdviceBehavior/MagicConstants/MagicConstantsTest.php +++ b/tests/Functional/AdviceBehavior/MagicConstants/MagicConstantsTest.php @@ -3,19 +3,19 @@ namespace Okapi\Aop\Tests\Functional\AdviceBehavior\MagicConstants; use Okapi\Aop\Tests\ClassLoaderMockTrait; -use Okapi\Aop\Tests\Functional\AdviceBehavior\MagicConstants\Kernel\{KernelOnClass, - KernelOnClassAndParent, - KernelOnClassAndParentAndTrait, - KernelOnClassAndTrait, - KernelOnParent, - KernelOnParentAndTrait, - KernelOnTrait}; -use Okapi\Aop\Tests\Functional\AdviceBehavior\MagicConstants\Target\{TargetClass, - TargetClass82, - TargetParent, - TargetParent82, - TargetTrait, - TargetTrait82}; +use Okapi\Aop\Tests\Functional\AdviceBehavior\MagicConstants\Kernel\KernelOnClass; +use Okapi\Aop\Tests\Functional\AdviceBehavior\MagicConstants\Kernel\KernelOnClassAndParent; +use Okapi\Aop\Tests\Functional\AdviceBehavior\MagicConstants\Kernel\KernelOnClassAndParentAndTrait; +use Okapi\Aop\Tests\Functional\AdviceBehavior\MagicConstants\Kernel\KernelOnClassAndTrait; +use Okapi\Aop\Tests\Functional\AdviceBehavior\MagicConstants\Kernel\KernelOnParent; +use Okapi\Aop\Tests\Functional\AdviceBehavior\MagicConstants\Kernel\KernelOnParentAndTrait; +use Okapi\Aop\Tests\Functional\AdviceBehavior\MagicConstants\Kernel\KernelOnTrait; +use Okapi\Aop\Tests\Functional\AdviceBehavior\MagicConstants\Target\TargetClass; +use Okapi\Aop\Tests\Functional\AdviceBehavior\MagicConstants\Target\TargetClass82; +use Okapi\Aop\Tests\Functional\AdviceBehavior\MagicConstants\Target\TargetParent; +use Okapi\Aop\Tests\Functional\AdviceBehavior\MagicConstants\Target\TargetParent82; +use Okapi\Aop\Tests\Functional\AdviceBehavior\MagicConstants\Target\TargetTrait; +use Okapi\Aop\Tests\Functional\AdviceBehavior\MagicConstants\Target\TargetTrait82; use Okapi\Aop\Tests\Util; use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses; use PHPUnit\Framework\ExpectationFailedException; @@ -32,19 +32,24 @@ class MagicConstantsTest extends TestCase { use ClassLoaderMockTrait; + /** @var class-string */ private string $targetClass = TargetClass::class; + /** @var class-string */ private string $targetParentClass = TargetParent::class; - private const PREFIX_TARGET_PATH = '/tests/Functional/AdviceBehavior/MagicConstants/Target'; + private const PREFIX_TARGET_PATH = '/tests/Functional/AdviceBehavior/MagicConstants/Target'; + private string $prefixTargetClassPath = self::PREFIX_TARGET_PATH . '/TargetClass.php'; private string $prefixTargetTraitPath = self::PREFIX_TARGET_PATH . '/TargetTrait.php'; private string $prefixTargetParentPath = self::PREFIX_TARGET_PATH . '/TargetParent.php'; - private const NAMESPACE_TARGET = 'Okapi\Aop\Tests\Functional\AdviceBehavior\MagicConstants\Target'; + private const NAMESPACE_TARGET = 'Okapi\Aop\Tests\Functional\AdviceBehavior\MagicConstants\Target'; + private string $namespaceTargetClass = TargetClass::class; private string $namespaceTargetTrait = TargetTrait::class; private string $namespaceTargetParent = TargetParent::class; + /** @param non-empty-string $name */ public function __construct(string $name) { parent::__construct($name); @@ -66,7 +71,7 @@ public function __construct(string $name) public function testMagicConstantsWithoutAop(): void { - $this->test(new $this->targetClass); + $this->test($this->newTarget()); } // Class @@ -76,7 +81,7 @@ public function testMagicConstantsWithAopOnClass(): void KernelOnClass::init(); $this->assertWillBeWoven($this->targetClass); - $this->test(new $this->targetClass); + $this->test($this->newTarget()); } // Class (Cached) @@ -85,7 +90,7 @@ public function testMagicConstantsWithAopOnClassCached(): void KernelOnClass::init(); $this->assertAspectLoadedFromCache($this->targetClass); - $this->test(new $this->targetClass); + $this->test($this->newTarget()); } // Parent @@ -96,7 +101,7 @@ public function testMagicConstantsWithAopOnParent(): void $this->assertWillBeWoven($this->targetParentClass); $this->assertWillBeWoven($this->targetClass); - $this->test(new $this->targetClass, $this->namespaceTargetParent); + $this->test($this->newTarget(), $this->namespaceTargetParent); } // Parent (Cached) @@ -106,7 +111,7 @@ public function testMagicConstantsWithAopOnParentCached(): void $this->assertAspectLoadedFromCache($this->targetParentClass); $this->assertAspectLoadedFromCache($this->targetClass); - $this->test(new $this->targetClass, $this->namespaceTargetParent); + $this->test($this->newTarget(), $this->namespaceTargetParent); } // Trait @@ -116,7 +121,7 @@ public function testMagicConstantsWithAopOnTrait(): void KernelOnTrait::init(); $this->assertWillBeWoven($this->targetClass); - $this->test(new $this->targetClass); + $this->test($this->newTarget()); } // Trait (Cached) @@ -125,7 +130,7 @@ public function testMagicConstantsWithAopOnTraitCached(): void KernelOnTrait::init(); $this->assertAspectLoadedFromCache($this->targetClass); - $this->test(new $this->targetClass); + $this->test($this->newTarget()); } // Class + Parent @@ -136,7 +141,7 @@ public function testMagicConstantsWithAopOnClassAndParent(): void $this->assertWillBeWoven($this->targetParentClass); $this->assertWillBeWoven($this->targetClass); - $this->test(new $this->targetClass, $this->namespaceTargetParent); + $this->test($this->newTarget(), $this->namespaceTargetParent); } // Class + Parent (Cached) @@ -146,7 +151,7 @@ public function testMagicConstantsWithAopOnClassAndParentCached(): void $this->assertAspectLoadedFromCache($this->targetParentClass); $this->assertAspectLoadedFromCache($this->targetClass); - $this->test(new $this->targetClass, $this->namespaceTargetParent); + $this->test($this->newTarget(), $this->namespaceTargetParent); } // Class + Trait @@ -156,7 +161,7 @@ public function testMagicConstantsWithAopOnClassAndTrait(): void KernelOnClassAndTrait::init(); $this->assertWillBeWoven($this->targetClass); - $this->test(new $this->targetClass); + $this->test($this->newTarget()); } // Class + Trait (Cached) @@ -165,7 +170,7 @@ public function testMagicConstantsWithAopOnClassAndTraitCached(): void KernelOnClassAndTrait::init(); $this->assertAspectLoadedFromCache($this->targetClass); - $this->test(new $this->targetClass); + $this->test($this->newTarget()); } // Parent + Trait @@ -176,7 +181,7 @@ public function testMagicConstantsWithAopOnParentAndTrait(): void $this->assertWillBeWoven($this->targetParentClass); $this->assertWillBeWoven($this->targetClass); - $this->test(new $this->targetClass, $this->namespaceTargetParent); + $this->test($this->newTarget(), $this->namespaceTargetParent); } // Parent + Trait (Cached) @@ -186,7 +191,7 @@ public function testMagicConstantsWithAopOnParentAndTraitCached(): void $this->assertAspectLoadedFromCache($this->targetParentClass); $this->assertAspectLoadedFromCache($this->targetClass); - $this->test(new $this->targetClass, $this->namespaceTargetParent); + $this->test($this->newTarget(), $this->namespaceTargetParent); } // Class + Parent + Trait @@ -197,7 +202,7 @@ public function testMagicConstantsWithAopOnClassAndParentAndTrait(): void $this->assertWillBeWoven($this->targetParentClass); $this->assertWillBeWoven($this->targetClass); - $this->test(new $this->targetClass, $this->namespaceTargetParent); + $this->test($this->newTarget(), $this->namespaceTargetParent); } // Class + Parent + Trait (Cached) @@ -207,20 +212,18 @@ public function testMagicConstantsWithAopOnClassAndParentAndTraitCached(): void $this->assertAspectLoadedFromCache($this->targetParentClass); $this->assertAspectLoadedFromCache($this->targetClass); - $this->test(new $this->targetClass, $this->namespaceTargetParent); + $this->test($this->newTarget(), $this->namespaceTargetParent); } - private function test( - TargetClass|TargetClass82 $target, - ?string $staticClass = null - ): void { + private function test(TargetClass|TargetClass82 $target, ?string $staticClass = null): void + { if (!$staticClass) { $staticClass = $this->namespaceTargetClass; } $constantExceptions = $this->testConstants($target); $propertyExceptions = $this->testProperty($target); - $methodExceptions = $this->testMethod($target, $staticClass); + $methodExceptions = $this->testMethod($target, $staticClass); $exceptions = [ ...$constantExceptions, @@ -229,56 +232,60 @@ private function test( ]; if ($exceptions) { - $this->markTestIncomplete( - 'Some tests skipped: ' . - 'https://github.com/okapi-web/php-aop/issues/69#issuecomment-1806817698' - ); + $this->markTestIncomplete('Some tests skipped: ' + . 'https://github.com/okapi-web/php-aop/issues/69#issuecomment-1806817698'); } } + private function newTarget(): TargetClass|TargetClass82 + { + return $this->targetClass === TargetClass82::class ? new TargetClass82() : new TargetClass(); + } + + /** @return list */ private function testConstants(TargetClass|TargetClass82 $target): array { $exceptions = []; $expected = [ - 'dir' => $this->np($this->rootPath() . self::PREFIX_TARGET_PATH), - 'file' => $this->np($this->rootPath() . $this->prefixTargetClassPath), - 'function' => '', - 'class' => $this->namespaceTargetClass, - 'trait' => '', - 'method' => '', - 'namespace' => self::NAMESPACE_TARGET, - 'targetClassClass' => $this->namespaceTargetClass, - 'targetTraitClass' => $this->namespaceTargetTrait, + 'dir' => $this->np($this->rootPath() . self::PREFIX_TARGET_PATH), + 'file' => $this->np($this->rootPath() . $this->prefixTargetClassPath), + 'function' => '', + 'class' => $this->namespaceTargetClass, + 'trait' => '', + 'method' => '', + 'namespace' => self::NAMESPACE_TARGET, + 'targetClassClass' => $this->namespaceTargetClass, + 'targetTraitClass' => $this->namespaceTargetTrait, 'targetParentClass' => $this->namespaceTargetParent, - 'selfClass' => $this->namespaceTargetClass, + 'selfClass' => $this->namespaceTargetClass, ]; // region Class - $this->assertSame($expected, $target::CONST); + static::assertSame($expected, $target::CONST); // endregion // region Parent - $expected['file'] = $this->np($this->rootPath() . $this->prefixTargetParentPath); - $expected['class'] = $this->namespaceTargetParent; + $expected['file'] = $this->np($this->rootPath() . $this->prefixTargetParentPath); + $expected['class'] = $this->namespaceTargetParent; $expected['selfClass'] = $this->namespaceTargetParent; - $this->assertSame($expected, $target::PARENT_CONST); + static::assertSame($expected, $target::PARENT_CONST); // endregion // region Trait // Only PHP >= 8.2 has trait constants - if (version_compare(PHP_VERSION, '8.2.0', '>=')) { - $expected['file'] = $this->np($this->rootPath() . $this->prefixTargetTraitPath); + if ($target instanceof TargetClass82) { + $expected['file'] = $this->np($this->rootPath() . $this->prefixTargetTraitPath); $expected['trait'] = $this->namespaceTargetTrait; try { - $this->assertSame($expected, $target::TRAIT_CONST); + static::assertSame($expected, $target::TRAIT_CONST); } catch (ExpectationFailedException $e) { $exceptions[] = $e; } @@ -289,50 +296,51 @@ private function testConstants(TargetClass|TargetClass82 $target): array return $exceptions; } + /** @return list */ private function testProperty(TargetClass|TargetClass82 $target): array { $exceptions = []; $expected = [ - 'dir' => $this->np($this->rootPath() . self::PREFIX_TARGET_PATH), - 'file' => $this->np($this->rootPath() . $this->prefixTargetClassPath), - 'function' => '', - 'class' => $this->namespaceTargetClass, - 'trait' => '', - 'method' => '', - 'namespace' => self::NAMESPACE_TARGET, - 'targetClassClass' => $this->namespaceTargetClass, - 'targetTraitClass' => $this->namespaceTargetTrait, + 'dir' => $this->np($this->rootPath() . self::PREFIX_TARGET_PATH), + 'file' => $this->np($this->rootPath() . $this->prefixTargetClassPath), + 'function' => '', + 'class' => $this->namespaceTargetClass, + 'trait' => '', + 'method' => '', + 'namespace' => self::NAMESPACE_TARGET, + 'targetClassClass' => $this->namespaceTargetClass, + 'targetTraitClass' => $this->namespaceTargetTrait, 'targetParentClass' => $this->namespaceTargetParent, - 'selfClass' => $this->namespaceTargetClass, + 'selfClass' => $this->namespaceTargetClass, ]; // region Class - $this->assertSame($expected, $target->property); - $this->assertSame($expected, $target::$staticProperty); + static::assertSame($expected, $target->property); + static::assertSame($expected, $target::$staticProperty); // endregion // region Parent - $expected['file'] = $this->np($this->rootPath() . $this->prefixTargetParentPath); - $expected['class'] = $this->namespaceTargetParent; + $expected['file'] = $this->np($this->rootPath() . $this->prefixTargetParentPath); + $expected['class'] = $this->namespaceTargetParent; $expected['selfClass'] = $this->namespaceTargetParent; - $this->assertSame($expected, $target->parentProperty); - $this->assertSame($expected, $target::$parentStaticProperty); + static::assertSame($expected, $target->parentProperty); + static::assertSame($expected, $target::$parentStaticProperty); // endregion // region Trait - $expected['file'] = $this->np($this->rootPath() . $this->prefixTargetTraitPath); + $expected['file'] = $this->np($this->rootPath() . $this->prefixTargetTraitPath); $expected['trait'] = $this->namespaceTargetTrait; try { - $this->assertSame($expected, $target->traitProperty); - $this->assertSame($expected, $target::$traitStaticProperty); + static::assertSame($expected, $target->traitProperty); + static::assertSame($expected, $target::$traitStaticProperty); } catch (ExpectationFailedException $e) { $exceptions[] = $e; } @@ -342,10 +350,9 @@ private function testProperty(TargetClass|TargetClass82 $target): array return $exceptions; } - private function testMethod( - TargetClass|TargetClass82 $target, - ?string $staticClass - ): array { + /** @return list */ + private function testMethod(TargetClass|TargetClass82 $target, ?string $staticClass): array + { if (!$staticClass) { $staticClass = $this->namespaceTargetClass; } @@ -353,63 +360,63 @@ private function testMethod( $exceptions = []; $expected = [ - 'dir' => $this->np($this->rootPath() . self::PREFIX_TARGET_PATH), - 'file' => $this->np($this->rootPath() . $this->prefixTargetClassPath), - 'function' => 'method', - 'class' => $this->namespaceTargetClass, - 'trait' => '', - 'method' => $this->namespaceTargetClass . '::method', - 'namespace' => self::NAMESPACE_TARGET, - 'targetClassClass' => $this->namespaceTargetClass, - 'targetTraitClass' => $this->namespaceTargetTrait, + 'dir' => $this->np($this->rootPath() . self::PREFIX_TARGET_PATH), + 'file' => $this->np($this->rootPath() . $this->prefixTargetClassPath), + 'function' => 'method', + 'class' => $this->namespaceTargetClass, + 'trait' => '', + 'method' => $this->namespaceTargetClass . '::method', + 'namespace' => self::NAMESPACE_TARGET, + 'targetClassClass' => $this->namespaceTargetClass, + 'targetTraitClass' => $this->namespaceTargetTrait, 'targetParentClass' => $this->namespaceTargetParent, - 'selfClass' => $this->namespaceTargetClass, - 'staticClass' => $this->namespaceTargetClass, + 'selfClass' => $this->namespaceTargetClass, + 'staticClass' => $this->namespaceTargetClass, ]; // region Class - $this->assertSame($expected, $target->method()); + static::assertSame($expected, $target->method()); $expected['function'] = 'staticMethod'; - $expected['method'] = $this->namespaceTargetClass . '::staticMethod'; + $expected['method'] = $this->namespaceTargetClass . '::staticMethod'; - $this->assertSame($expected, $target::staticMethod()); + static::assertSame($expected, $target::staticMethod()); // endregion // region Parent - $expected['file'] = $this->np($this->rootPath() . $this->prefixTargetParentPath); - $expected['function'] = 'parentMethod'; - $expected['class'] = $this->namespaceTargetParent; - $expected['method'] = $this->namespaceTargetParent . '::parentMethod'; + $expected['file'] = $this->np($this->rootPath() . $this->prefixTargetParentPath); + $expected['function'] = 'parentMethod'; + $expected['class'] = $this->namespaceTargetParent; + $expected['method'] = $this->namespaceTargetParent . '::parentMethod'; $expected['selfClass'] = $this->namespaceTargetParent; - $this->assertSame($expected, $target->parentMethod()); + static::assertSame($expected, $target->parentMethod()); - $expected['function'] = 'parentStaticMethod'; - $expected['method'] = $this->namespaceTargetParent . '::parentStaticMethod'; + $expected['function'] = 'parentStaticMethod'; + $expected['method'] = $this->namespaceTargetParent . '::parentStaticMethod'; $expected['staticClass'] = $staticClass; - $this->assertSame($expected, $target::parentStaticMethod()); + static::assertSame($expected, $target::parentStaticMethod()); // endregion // region Trait try { - $expected['file'] = $this->np($this->rootPath() . $this->prefixTargetTraitPath); + $expected['file'] = $this->np($this->rootPath() . $this->prefixTargetTraitPath); $expected['function'] = 'traitMethod'; - $expected['trait'] = $this->namespaceTargetTrait; - $expected['method'] = $this->namespaceTargetTrait . '::traitMethod'; + $expected['trait'] = $this->namespaceTargetTrait; + $expected['method'] = $this->namespaceTargetTrait . '::traitMethod'; - $this->assertSame($expected, $target->traitMethod()); + static::assertSame($expected, $target->traitMethod()); $expected['function'] = 'traitStaticMethod'; - $expected['method'] = $this->namespaceTargetTrait . '::traitStaticMethod'; + $expected['method'] = $this->namespaceTargetTrait . '::traitStaticMethod'; - $this->assertSame($expected, $target::traitStaticMethod()); + static::assertSame($expected, $target::traitStaticMethod()); } catch (ExpectationFailedException $e) { $exceptions[] = $e; } diff --git a/tests/Functional/AdviceBehavior/MagicConstants/Target/TargetClass.php b/tests/Functional/AdviceBehavior/MagicConstants/Target/TargetClass.php index 7606a8b..45f2987 100644 --- a/tests/Functional/AdviceBehavior/MagicConstants/Target/TargetClass.php +++ b/tests/Functional/AdviceBehavior/MagicConstants/Target/TargetClass.php @@ -7,80 +7,84 @@ class TargetClass extends TargetParent use TargetTrait; public const CONST = [ - 'dir' => __DIR__, - 'file' => __FILE__, - 'function' => __FUNCTION__, - 'class' => __CLASS__, - 'trait' => __TRAIT__, - 'method' => __METHOD__, - 'namespace' => __NAMESPACE__, - 'targetClassClass' => TargetClass::class, - 'targetTraitClass' => TargetTrait::class, + 'dir' => __DIR__, + 'file' => __FILE__, + 'function' => __FUNCTION__, + 'class' => __CLASS__, + 'trait' => __TRAIT__, + 'method' => __METHOD__, + 'namespace' => __NAMESPACE__, + 'targetClassClass' => TargetClass::class, + 'targetTraitClass' => TargetTrait::class, 'targetParentClass' => TargetParent::class, - 'selfClass' => self::class, + 'selfClass' => self::class, ]; + /** @var array */ public array $property = [ - 'dir' => __DIR__, - 'file' => __FILE__, - 'function' => __FUNCTION__, - 'class' => __CLASS__, - 'trait' => __TRAIT__, - 'method' => __METHOD__, - 'namespace' => __NAMESPACE__, - 'targetClassClass' => TargetClass::class, - 'targetTraitClass' => TargetTrait::class, + 'dir' => __DIR__, + 'file' => __FILE__, + 'function' => __FUNCTION__, + 'class' => __CLASS__, + 'trait' => __TRAIT__, + 'method' => __METHOD__, + 'namespace' => __NAMESPACE__, + 'targetClassClass' => TargetClass::class, + 'targetTraitClass' => TargetTrait::class, 'targetParentClass' => TargetParent::class, - 'selfClass' => self::class, + 'selfClass' => self::class, ]; + /** @var array */ public static array $staticProperty = [ - 'dir' => __DIR__, - 'file' => __FILE__, - 'function' => __FUNCTION__, - 'class' => __CLASS__, - 'trait' => __TRAIT__, - 'method' => __METHOD__, - 'namespace' => __NAMESPACE__, - 'targetClassClass' => TargetClass::class, - 'targetTraitClass' => TargetTrait::class, + 'dir' => __DIR__, + 'file' => __FILE__, + 'function' => __FUNCTION__, + 'class' => __CLASS__, + 'trait' => __TRAIT__, + 'method' => __METHOD__, + 'namespace' => __NAMESPACE__, + 'targetClassClass' => TargetClass::class, + 'targetTraitClass' => TargetTrait::class, 'targetParentClass' => TargetParent::class, - 'selfClass' => self::class, + 'selfClass' => self::class, ]; + /** @return array */ public function method(): array { return [ - 'dir' => __DIR__, - 'file' => __FILE__, - 'function' => __FUNCTION__, - 'class' => __CLASS__, - 'trait' => __TRAIT__, - 'method' => __METHOD__, - 'namespace' => __NAMESPACE__, - 'targetClassClass' => TargetClass::class, - 'targetTraitClass' => TargetTrait::class, + 'dir' => __DIR__, + 'file' => __FILE__, + 'function' => __FUNCTION__, + 'class' => __CLASS__, + 'trait' => __TRAIT__, + 'method' => __METHOD__, + 'namespace' => __NAMESPACE__, + 'targetClassClass' => TargetClass::class, + 'targetTraitClass' => TargetTrait::class, 'targetParentClass' => TargetParent::class, - 'selfClass' => self::class, - 'staticClass' => static::class, + 'selfClass' => self::class, + 'staticClass' => static::class, ]; } + /** @return array */ public static function staticMethod(): array { return [ - 'dir' => __DIR__, - 'file' => __FILE__, - 'function' => __FUNCTION__, - 'class' => __CLASS__, - 'trait' => __TRAIT__, - 'method' => __METHOD__, - 'namespace' => __NAMESPACE__, - 'targetClassClass' => TargetClass::class, - 'targetTraitClass' => TargetTrait::class, + 'dir' => __DIR__, + 'file' => __FILE__, + 'function' => __FUNCTION__, + 'class' => __CLASS__, + 'trait' => __TRAIT__, + 'method' => __METHOD__, + 'namespace' => __NAMESPACE__, + 'targetClassClass' => TargetClass::class, + 'targetTraitClass' => TargetTrait::class, 'targetParentClass' => TargetParent::class, - 'selfClass' => self::class, - 'staticClass' => static::class, + 'selfClass' => self::class, + 'staticClass' => static::class, ]; } } diff --git a/tests/Functional/AdviceBehavior/MagicConstants/Target/TargetClass82.php b/tests/Functional/AdviceBehavior/MagicConstants/Target/TargetClass82.php index b5225e0..f88582e 100644 --- a/tests/Functional/AdviceBehavior/MagicConstants/Target/TargetClass82.php +++ b/tests/Functional/AdviceBehavior/MagicConstants/Target/TargetClass82.php @@ -7,80 +7,84 @@ class TargetClass82 extends TargetParent82 use TargetTrait82; public const CONST = [ - 'dir' => __DIR__, - 'file' => __FILE__, - 'function' => __FUNCTION__, - 'class' => __CLASS__, - 'trait' => __TRAIT__, - 'method' => __METHOD__, - 'namespace' => __NAMESPACE__, - 'targetClassClass' => TargetClass82::class, - 'targetTraitClass' => TargetTrait82::class, + 'dir' => __DIR__, + 'file' => __FILE__, + 'function' => __FUNCTION__, + 'class' => __CLASS__, + 'trait' => __TRAIT__, + 'method' => __METHOD__, + 'namespace' => __NAMESPACE__, + 'targetClassClass' => TargetClass82::class, + 'targetTraitClass' => TargetTrait82::class, 'targetParentClass' => TargetParent82::class, - 'selfClass' => self::class, + 'selfClass' => self::class, ]; + /** @var array */ public array $property = [ - 'dir' => __DIR__, - 'file' => __FILE__, - 'function' => __FUNCTION__, - 'class' => __CLASS__, - 'trait' => __TRAIT__, - 'method' => __METHOD__, - 'namespace' => __NAMESPACE__, - 'targetClassClass' => TargetClass82::class, - 'targetTraitClass' => TargetTrait82::class, + 'dir' => __DIR__, + 'file' => __FILE__, + 'function' => __FUNCTION__, + 'class' => __CLASS__, + 'trait' => __TRAIT__, + 'method' => __METHOD__, + 'namespace' => __NAMESPACE__, + 'targetClassClass' => TargetClass82::class, + 'targetTraitClass' => TargetTrait82::class, 'targetParentClass' => TargetParent82::class, - 'selfClass' => self::class, + 'selfClass' => self::class, ]; + /** @var array */ public static array $staticProperty = [ - 'dir' => __DIR__, - 'file' => __FILE__, - 'function' => __FUNCTION__, - 'class' => __CLASS__, - 'trait' => __TRAIT__, - 'method' => __METHOD__, - 'namespace' => __NAMESPACE__, - 'targetClassClass' => TargetClass82::class, - 'targetTraitClass' => TargetTrait82::class, + 'dir' => __DIR__, + 'file' => __FILE__, + 'function' => __FUNCTION__, + 'class' => __CLASS__, + 'trait' => __TRAIT__, + 'method' => __METHOD__, + 'namespace' => __NAMESPACE__, + 'targetClassClass' => TargetClass82::class, + 'targetTraitClass' => TargetTrait82::class, 'targetParentClass' => TargetParent82::class, - 'selfClass' => self::class, + 'selfClass' => self::class, ]; + /** @return array */ public function method(): array { return [ - 'dir' => __DIR__, - 'file' => __FILE__, - 'function' => __FUNCTION__, - 'class' => __CLASS__, - 'trait' => __TRAIT__, - 'method' => __METHOD__, - 'namespace' => __NAMESPACE__, - 'targetClassClass' => TargetClass82::class, - 'targetTraitClass' => TargetTrait82::class, + 'dir' => __DIR__, + 'file' => __FILE__, + 'function' => __FUNCTION__, + 'class' => __CLASS__, + 'trait' => __TRAIT__, + 'method' => __METHOD__, + 'namespace' => __NAMESPACE__, + 'targetClassClass' => TargetClass82::class, + 'targetTraitClass' => TargetTrait82::class, 'targetParentClass' => TargetParent82::class, - 'selfClass' => self::class, - 'staticClass' => static::class, + 'selfClass' => self::class, + 'staticClass' => static::class, ]; } + /** @return array */ public static function staticMethod(): array { return [ - 'dir' => __DIR__, - 'file' => __FILE__, - 'function' => __FUNCTION__, - 'class' => __CLASS__, - 'trait' => __TRAIT__, - 'method' => __METHOD__, - 'namespace' => __NAMESPACE__, - 'targetClassClass' => TargetClass82::class, - 'targetTraitClass' => TargetTrait82::class, + 'dir' => __DIR__, + 'file' => __FILE__, + 'function' => __FUNCTION__, + 'class' => __CLASS__, + 'trait' => __TRAIT__, + 'method' => __METHOD__, + 'namespace' => __NAMESPACE__, + 'targetClassClass' => TargetClass82::class, + 'targetTraitClass' => TargetTrait82::class, 'targetParentClass' => TargetParent82::class, - 'selfClass' => self::class, - 'staticClass' => static::class, + 'selfClass' => self::class, + 'staticClass' => static::class, ]; } } diff --git a/tests/Functional/AdviceBehavior/MagicConstants/Target/TargetParent.php b/tests/Functional/AdviceBehavior/MagicConstants/Target/TargetParent.php index 47e4dee..5eebcda 100644 --- a/tests/Functional/AdviceBehavior/MagicConstants/Target/TargetParent.php +++ b/tests/Functional/AdviceBehavior/MagicConstants/Target/TargetParent.php @@ -5,80 +5,84 @@ class TargetParent { public const PARENT_CONST = [ - 'dir' => __DIR__, - 'file' => __FILE__, - 'function' => __FUNCTION__, - 'class' => __CLASS__, - 'trait' => __TRAIT__, - 'method' => __METHOD__, - 'namespace' => __NAMESPACE__, - 'targetClassClass' => TargetClass::class, - 'targetTraitClass' => TargetTrait::class, + 'dir' => __DIR__, + 'file' => __FILE__, + 'function' => __FUNCTION__, + 'class' => __CLASS__, + 'trait' => __TRAIT__, + 'method' => __METHOD__, + 'namespace' => __NAMESPACE__, + 'targetClassClass' => TargetClass::class, + 'targetTraitClass' => TargetTrait::class, 'targetParentClass' => TargetParent::class, - 'selfClass' => self::class, + 'selfClass' => self::class, ]; + /** @var array */ public array $parentProperty = [ - 'dir' => __DIR__, - 'file' => __FILE__, - 'function' => __FUNCTION__, - 'class' => __CLASS__, - 'trait' => __TRAIT__, - 'method' => __METHOD__, - 'namespace' => __NAMESPACE__, - 'targetClassClass' => TargetClass::class, - 'targetTraitClass' => TargetTrait::class, + 'dir' => __DIR__, + 'file' => __FILE__, + 'function' => __FUNCTION__, + 'class' => __CLASS__, + 'trait' => __TRAIT__, + 'method' => __METHOD__, + 'namespace' => __NAMESPACE__, + 'targetClassClass' => TargetClass::class, + 'targetTraitClass' => TargetTrait::class, 'targetParentClass' => TargetParent::class, - 'selfClass' => self::class, + 'selfClass' => self::class, ]; + /** @var array */ public static array $parentStaticProperty = [ - 'dir' => __DIR__, - 'file' => __FILE__, - 'function' => __FUNCTION__, - 'class' => __CLASS__, - 'trait' => __TRAIT__, - 'method' => __METHOD__, - 'namespace' => __NAMESPACE__, - 'targetClassClass' => TargetClass::class, - 'targetTraitClass' => TargetTrait::class, + 'dir' => __DIR__, + 'file' => __FILE__, + 'function' => __FUNCTION__, + 'class' => __CLASS__, + 'trait' => __TRAIT__, + 'method' => __METHOD__, + 'namespace' => __NAMESPACE__, + 'targetClassClass' => TargetClass::class, + 'targetTraitClass' => TargetTrait::class, 'targetParentClass' => TargetParent::class, - 'selfClass' => self::class, + 'selfClass' => self::class, ]; + /** @return array */ public function parentMethod(): array { return [ - 'dir' => __DIR__, - 'file' => __FILE__, - 'function' => __FUNCTION__, - 'class' => __CLASS__, - 'trait' => __TRAIT__, - 'method' => __METHOD__, - 'namespace' => __NAMESPACE__, - 'targetClassClass' => TargetClass::class, - 'targetTraitClass' => TargetTrait::class, + 'dir' => __DIR__, + 'file' => __FILE__, + 'function' => __FUNCTION__, + 'class' => __CLASS__, + 'trait' => __TRAIT__, + 'method' => __METHOD__, + 'namespace' => __NAMESPACE__, + 'targetClassClass' => TargetClass::class, + 'targetTraitClass' => TargetTrait::class, 'targetParentClass' => TargetParent::class, - 'selfClass' => self::class, - 'staticClass' => static::class, + 'selfClass' => self::class, + 'staticClass' => static::class, ]; } + /** @return array */ public static function parentStaticMethod(): array { return [ - 'dir' => __DIR__, - 'file' => __FILE__, - 'function' => __FUNCTION__, - 'class' => __CLASS__, - 'trait' => __TRAIT__, - 'method' => __METHOD__, - 'namespace' => __NAMESPACE__, - 'targetClassClass' => TargetClass::class, - 'targetTraitClass' => TargetTrait::class, + 'dir' => __DIR__, + 'file' => __FILE__, + 'function' => __FUNCTION__, + 'class' => __CLASS__, + 'trait' => __TRAIT__, + 'method' => __METHOD__, + 'namespace' => __NAMESPACE__, + 'targetClassClass' => TargetClass::class, + 'targetTraitClass' => TargetTrait::class, 'targetParentClass' => TargetParent::class, - 'selfClass' => self::class, - 'staticClass' => static::class, + 'selfClass' => self::class, + 'staticClass' => static::class, ]; } } diff --git a/tests/Functional/AdviceBehavior/MagicConstants/Target/TargetParent82.php b/tests/Functional/AdviceBehavior/MagicConstants/Target/TargetParent82.php index 4b9067c..b5ffcaf 100644 --- a/tests/Functional/AdviceBehavior/MagicConstants/Target/TargetParent82.php +++ b/tests/Functional/AdviceBehavior/MagicConstants/Target/TargetParent82.php @@ -5,80 +5,84 @@ class TargetParent82 { public const PARENT_CONST = [ - 'dir' => __DIR__, - 'file' => __FILE__, - 'function' => __FUNCTION__, - 'class' => __CLASS__, - 'trait' => __TRAIT__, - 'method' => __METHOD__, - 'namespace' => __NAMESPACE__, - 'targetClassClass' => TargetClass82::class, - 'targetTraitClass' => TargetTrait82::class, + 'dir' => __DIR__, + 'file' => __FILE__, + 'function' => __FUNCTION__, + 'class' => __CLASS__, + 'trait' => __TRAIT__, + 'method' => __METHOD__, + 'namespace' => __NAMESPACE__, + 'targetClassClass' => TargetClass82::class, + 'targetTraitClass' => TargetTrait82::class, 'targetParentClass' => TargetParent82::class, - 'selfClass' => self::class, + 'selfClass' => self::class, ]; + /** @var array */ public array $parentProperty = [ - 'dir' => __DIR__, - 'file' => __FILE__, - 'function' => __FUNCTION__, - 'class' => __CLASS__, - 'trait' => __TRAIT__, - 'method' => __METHOD__, - 'namespace' => __NAMESPACE__, - 'targetClassClass' => TargetClass82::class, - 'targetTraitClass' => TargetTrait82::class, + 'dir' => __DIR__, + 'file' => __FILE__, + 'function' => __FUNCTION__, + 'class' => __CLASS__, + 'trait' => __TRAIT__, + 'method' => __METHOD__, + 'namespace' => __NAMESPACE__, + 'targetClassClass' => TargetClass82::class, + 'targetTraitClass' => TargetTrait82::class, 'targetParentClass' => TargetParent82::class, - 'selfClass' => self::class, + 'selfClass' => self::class, ]; + /** @var array */ public static array $parentStaticProperty = [ - 'dir' => __DIR__, - 'file' => __FILE__, - 'function' => __FUNCTION__, - 'class' => __CLASS__, - 'trait' => __TRAIT__, - 'method' => __METHOD__, - 'namespace' => __NAMESPACE__, - 'targetClassClass' => TargetClass82::class, - 'targetTraitClass' => TargetTrait82::class, + 'dir' => __DIR__, + 'file' => __FILE__, + 'function' => __FUNCTION__, + 'class' => __CLASS__, + 'trait' => __TRAIT__, + 'method' => __METHOD__, + 'namespace' => __NAMESPACE__, + 'targetClassClass' => TargetClass82::class, + 'targetTraitClass' => TargetTrait82::class, 'targetParentClass' => TargetParent82::class, - 'selfClass' => self::class, + 'selfClass' => self::class, ]; + /** @return array */ public function parentMethod(): array { return [ - 'dir' => __DIR__, - 'file' => __FILE__, - 'function' => __FUNCTION__, - 'class' => __CLASS__, - 'trait' => __TRAIT__, - 'method' => __METHOD__, - 'namespace' => __NAMESPACE__, - 'targetClassClass' => TargetClass82::class, - 'targetTraitClass' => TargetTrait82::class, + 'dir' => __DIR__, + 'file' => __FILE__, + 'function' => __FUNCTION__, + 'class' => __CLASS__, + 'trait' => __TRAIT__, + 'method' => __METHOD__, + 'namespace' => __NAMESPACE__, + 'targetClassClass' => TargetClass82::class, + 'targetTraitClass' => TargetTrait82::class, 'targetParentClass' => TargetParent82::class, - 'selfClass' => self::class, - 'staticClass' => static::class, + 'selfClass' => self::class, + 'staticClass' => static::class, ]; } + /** @return array */ public static function parentStaticMethod(): array { return [ - 'dir' => __DIR__, - 'file' => __FILE__, - 'function' => __FUNCTION__, - 'class' => __CLASS__, - 'trait' => __TRAIT__, - 'method' => __METHOD__, - 'namespace' => __NAMESPACE__, - 'targetClassClass' => TargetClass82::class, - 'targetTraitClass' => TargetTrait82::class, + 'dir' => __DIR__, + 'file' => __FILE__, + 'function' => __FUNCTION__, + 'class' => __CLASS__, + 'trait' => __TRAIT__, + 'method' => __METHOD__, + 'namespace' => __NAMESPACE__, + 'targetClassClass' => TargetClass82::class, + 'targetTraitClass' => TargetTrait82::class, 'targetParentClass' => TargetParent82::class, - 'selfClass' => self::class, - 'staticClass' => static::class, + 'selfClass' => self::class, + 'staticClass' => static::class, ]; } } diff --git a/tests/Functional/AdviceBehavior/MagicConstants/Target/TargetTrait.php b/tests/Functional/AdviceBehavior/MagicConstants/Target/TargetTrait.php index 0dcb9a2..5b7627a 100644 --- a/tests/Functional/AdviceBehavior/MagicConstants/Target/TargetTrait.php +++ b/tests/Functional/AdviceBehavior/MagicConstants/Target/TargetTrait.php @@ -4,67 +4,71 @@ trait TargetTrait { + /** @var array */ public array $traitProperty = [ - 'dir' => __DIR__, - 'file' => __FILE__, - 'function' => __FUNCTION__, - 'class' => __CLASS__, - 'trait' => __TRAIT__, - 'method' => __METHOD__, - 'namespace' => __NAMESPACE__, - 'targetClassClass' => TargetClass::class, - 'targetTraitClass' => TargetTrait::class, + 'dir' => __DIR__, + 'file' => __FILE__, + 'function' => __FUNCTION__, + 'class' => __CLASS__, + 'trait' => __TRAIT__, + 'method' => __METHOD__, + 'namespace' => __NAMESPACE__, + 'targetClassClass' => TargetClass::class, + 'targetTraitClass' => TargetTrait::class, 'targetParentClass' => TargetParent::class, - 'selfClass' => self::class, + 'selfClass' => self::class, ]; + /** @var array */ public static array $traitStaticProperty = [ - 'dir' => __DIR__, - 'file' => __FILE__, - 'function' => __FUNCTION__, - 'class' => __CLASS__, - 'trait' => __TRAIT__, - 'method' => __METHOD__, - 'namespace' => __NAMESPACE__, - 'targetClassClass' => TargetClass::class, - 'targetTraitClass' => TargetTrait::class, + 'dir' => __DIR__, + 'file' => __FILE__, + 'function' => __FUNCTION__, + 'class' => __CLASS__, + 'trait' => __TRAIT__, + 'method' => __METHOD__, + 'namespace' => __NAMESPACE__, + 'targetClassClass' => TargetClass::class, + 'targetTraitClass' => TargetTrait::class, 'targetParentClass' => TargetParent::class, - 'selfClass' => self::class, + 'selfClass' => self::class, ]; + /** @return array */ public function traitMethod(): array { return [ - 'dir' => __DIR__, - 'file' => __FILE__, - 'function' => __FUNCTION__, - 'class' => __CLASS__, - 'trait' => __TRAIT__, - 'method' => __METHOD__, - 'namespace' => __NAMESPACE__, - 'targetClassClass' => TargetClass::class, - 'targetTraitClass' => TargetTrait::class, + 'dir' => __DIR__, + 'file' => __FILE__, + 'function' => __FUNCTION__, + 'class' => __CLASS__, + 'trait' => __TRAIT__, + 'method' => __METHOD__, + 'namespace' => __NAMESPACE__, + 'targetClassClass' => TargetClass::class, + 'targetTraitClass' => TargetTrait::class, 'targetParentClass' => TargetParent::class, - 'selfClass' => self::class, - 'staticClass' => static::class, + 'selfClass' => self::class, + 'staticClass' => static::class, ]; } + /** @return array */ public static function traitStaticMethod(): array { return [ - 'dir' => __DIR__, - 'file' => __FILE__, - 'function' => __FUNCTION__, - 'class' => __CLASS__, - 'trait' => __TRAIT__, - 'method' => __METHOD__, - 'namespace' => __NAMESPACE__, - 'targetClassClass' => TargetClass::class, - 'targetTraitClass' => TargetTrait::class, + 'dir' => __DIR__, + 'file' => __FILE__, + 'function' => __FUNCTION__, + 'class' => __CLASS__, + 'trait' => __TRAIT__, + 'method' => __METHOD__, + 'namespace' => __NAMESPACE__, + 'targetClassClass' => TargetClass::class, + 'targetTraitClass' => TargetTrait::class, 'targetParentClass' => TargetParent::class, - 'selfClass' => self::class, - 'staticClass' => static::class, + 'selfClass' => self::class, + 'staticClass' => static::class, ]; } } diff --git a/tests/Functional/AdviceBehavior/MagicConstants/Target/TargetTrait82.php b/tests/Functional/AdviceBehavior/MagicConstants/Target/TargetTrait82.php index 6057690..595eda6 100644 --- a/tests/Functional/AdviceBehavior/MagicConstants/Target/TargetTrait82.php +++ b/tests/Functional/AdviceBehavior/MagicConstants/Target/TargetTrait82.php @@ -1,84 +1,89 @@ __DIR__, - 'file' => __FILE__, - 'function' => __FUNCTION__, - 'class' => __CLASS__, - 'trait' => __TRAIT__, - 'method' => __METHOD__, - 'namespace' => __NAMESPACE__, - 'targetClassClass' => TargetClass82::class, - 'targetTraitClass' => TargetTrait82::class, + 'dir' => __DIR__, + 'file' => __FILE__, + 'function' => __FUNCTION__, + 'class' => __CLASS__, + 'trait' => __TRAIT__, + 'method' => __METHOD__, + 'namespace' => __NAMESPACE__, + 'targetClassClass' => TargetClass82::class, + 'targetTraitClass' => TargetTrait82::class, 'targetParentClass' => TargetParent82::class, - 'selfClass' => self::class, + 'selfClass' => self::class, ]; + /** @var array */ public array $traitProperty = [ - 'dir' => __DIR__, - 'file' => __FILE__, - 'function' => __FUNCTION__, - 'class' => __CLASS__, - 'trait' => __TRAIT__, - 'method' => __METHOD__, - 'namespace' => __NAMESPACE__, - 'targetClassClass' => TargetClass82::class, - 'targetTraitClass' => TargetTrait82::class, + 'dir' => __DIR__, + 'file' => __FILE__, + 'function' => __FUNCTION__, + 'class' => __CLASS__, + 'trait' => __TRAIT__, + 'method' => __METHOD__, + 'namespace' => __NAMESPACE__, + 'targetClassClass' => TargetClass82::class, + 'targetTraitClass' => TargetTrait82::class, 'targetParentClass' => TargetParent82::class, - 'selfClass' => self::class, + 'selfClass' => self::class, ]; + /** @var array */ public static array $traitStaticProperty = [ - 'dir' => __DIR__, - 'file' => __FILE__, - 'function' => __FUNCTION__, - 'class' => __CLASS__, - 'trait' => __TRAIT__, - 'method' => __METHOD__, - 'namespace' => __NAMESPACE__, - 'targetClassClass' => TargetClass82::class, - 'targetTraitClass' => TargetTrait82::class, + 'dir' => __DIR__, + 'file' => __FILE__, + 'function' => __FUNCTION__, + 'class' => __CLASS__, + 'trait' => __TRAIT__, + 'method' => __METHOD__, + 'namespace' => __NAMESPACE__, + 'targetClassClass' => TargetClass82::class, + 'targetTraitClass' => TargetTrait82::class, 'targetParentClass' => TargetParent82::class, - 'selfClass' => self::class, + 'selfClass' => self::class, ]; + /** @return array */ public function traitMethod(): array { return [ - 'dir' => __DIR__, - 'file' => __FILE__, - 'function' => __FUNCTION__, - 'class' => __CLASS__, - 'trait' => __TRAIT__, - 'method' => __METHOD__, - 'namespace' => __NAMESPACE__, - 'targetClassClass' => TargetClass82::class, - 'targetTraitClass' => TargetTrait82::class, + 'dir' => __DIR__, + 'file' => __FILE__, + 'function' => __FUNCTION__, + 'class' => __CLASS__, + 'trait' => __TRAIT__, + 'method' => __METHOD__, + 'namespace' => __NAMESPACE__, + 'targetClassClass' => TargetClass82::class, + 'targetTraitClass' => TargetTrait82::class, 'targetParentClass' => TargetParent82::class, - 'selfClass' => self::class, - 'staticClass' => static::class, + 'selfClass' => self::class, + 'staticClass' => static::class, ]; } + /** @return array */ public static function traitStaticMethod(): array { return [ - 'dir' => __DIR__, - 'file' => __FILE__, - 'function' => __FUNCTION__, - 'class' => __CLASS__, - 'trait' => __TRAIT__, - 'method' => __METHOD__, - 'namespace' => __NAMESPACE__, - 'targetClassClass' => TargetClass82::class, - 'targetTraitClass' => TargetTrait82::class, + 'dir' => __DIR__, + 'file' => __FILE__, + 'function' => __FUNCTION__, + 'class' => __CLASS__, + 'trait' => __TRAIT__, + 'method' => __METHOD__, + 'namespace' => __NAMESPACE__, + 'targetClassClass' => TargetClass82::class, + 'targetTraitClass' => TargetTrait82::class, 'targetParentClass' => TargetParent82::class, - 'selfClass' => self::class, - 'staticClass' => static::class, + 'selfClass' => self::class, + 'staticClass' => static::class, ]; } } diff --git a/tests/Functional/AdviceBehavior/ModifyArgument/Aspect/NumberHelperAspect.php b/tests/Functional/AdviceBehavior/ModifyArgument/Aspect/NumberHelperAspect.php index a2cc41c..80887ab 100644 --- a/tests/Functional/AdviceBehavior/ModifyArgument/Aspect/NumberHelperAspect.php +++ b/tests/Functional/AdviceBehavior/ModifyArgument/Aspect/NumberHelperAspect.php @@ -1,4 +1,5 @@ $numbers */ $numbers = $invocation->getArgument(0); - $numbers = array_filter($numbers, fn($number) => $number >= 0); + $numbers = array_filter($numbers, static fn($number) => $number >= 0); $invocation->setArgument(0, $numbers); } } diff --git a/tests/Functional/AdviceBehavior/ModifyArgument/Kernel.php b/tests/Functional/AdviceBehavior/ModifyArgument/Kernel.php index 0b51ca1..7be9719 100644 --- a/tests/Functional/AdviceBehavior/ModifyArgument/Kernel.php +++ b/tests/Functional/AdviceBehavior/ModifyArgument/Kernel.php @@ -10,6 +10,7 @@ class Kernel extends AopKernel { protected ?string $cacheDir = Util::CACHE_DIR; + /** @var array */ protected array $aspects = [ NumberHelperAspect::class, ]; diff --git a/tests/Functional/AdviceBehavior/ModifyArgument/ModifyArgumentTest.php b/tests/Functional/AdviceBehavior/ModifyArgument/ModifyArgumentTest.php index 41bdd3d..dc65d76 100644 --- a/tests/Functional/AdviceBehavior/ModifyArgument/ModifyArgumentTest.php +++ b/tests/Functional/AdviceBehavior/ModifyArgument/ModifyArgumentTest.php @@ -28,11 +28,11 @@ public function testModifyArgument(): void $numbers = [1, 2, 3, 4, 5]; $expected = 15; $actual = $numberHelper->sumArray($numbers); - $this->assertEquals($expected, $actual); + static::assertEquals($expected, $actual); $numbers = [1, 2, -3, 4, 5]; $expected = 12; $actual = $numberHelper->sumArray($numbers); - $this->assertEquals($expected, $actual); + static::assertEquals($expected, $actual); } } diff --git a/tests/Functional/AdviceBehavior/ModifyArgument/Target/NumberHelper.php b/tests/Functional/AdviceBehavior/ModifyArgument/Target/NumberHelper.php index d702a2b..423b691 100644 --- a/tests/Functional/AdviceBehavior/ModifyArgument/Target/NumberHelper.php +++ b/tests/Functional/AdviceBehavior/ModifyArgument/Target/NumberHelper.php @@ -4,6 +4,7 @@ class NumberHelper { + /** @param array $numbers */ public function sumArray(array $numbers): int { return array_sum($numbers); diff --git a/tests/Functional/AdviceBehavior/ModifyArgumentPassedByReference/Aspect/AddMetadataToArrayAspect.php b/tests/Functional/AdviceBehavior/ModifyArgumentPassedByReference/Aspect/AddMetadataToArrayAspect.php index 84e8bdc..06baecc 100644 --- a/tests/Functional/AdviceBehavior/ModifyArgumentPassedByReference/Aspect/AddMetadataToArrayAspect.php +++ b/tests/Functional/AdviceBehavior/ModifyArgumentPassedByReference/Aspect/AddMetadataToArrayAspect.php @@ -1,24 +1,22 @@ -getArgument('data'); - $array['metadata'] = 'metadata'; - $invocation->setArgument('data', $array); - } -} +getArgument('data'); + $array['metadata'] = 'metadata'; + $invocation->setArgument('data', $array); + } +} diff --git a/tests/Functional/AdviceBehavior/ModifyArgumentPassedByReference/Kernel.php b/tests/Functional/AdviceBehavior/ModifyArgumentPassedByReference/Kernel.php index aae0053..c02410f 100644 --- a/tests/Functional/AdviceBehavior/ModifyArgumentPassedByReference/Kernel.php +++ b/tests/Functional/AdviceBehavior/ModifyArgumentPassedByReference/Kernel.php @@ -1,16 +1,17 @@ - */ + protected array $aspects = [ + AddMetadataToArrayAspect::class, + ]; +} diff --git a/tests/Functional/AdviceBehavior/ModifyArgumentPassedByReference/ModifyArgumentPassedByReferenceTest.php b/tests/Functional/AdviceBehavior/ModifyArgumentPassedByReference/ModifyArgumentPassedByReferenceTest.php index 274ef12..227d751 100644 --- a/tests/Functional/AdviceBehavior/ModifyArgumentPassedByReference/ModifyArgumentPassedByReferenceTest.php +++ b/tests/Functional/AdviceBehavior/ModifyArgumentPassedByReference/ModifyArgumentPassedByReferenceTest.php @@ -1,32 +1,30 @@ -assertWillBeWoven(ArrayCreator::class); - $idCreator = new ArrayCreator(); - - $data = 'my-awesome-data'; - $idCreator->createArray($data); - /** @var array $data */ - - $this->assertIsArray($data); - $this->assertArrayHasKey('metadata', $data); - $this->assertEquals('metadata', $data['metadata']); - } -} +assertWillBeWoven(ArrayCreator::class); + $idCreator = new ArrayCreator(); + + $data = 'my-awesome-data'; + $idCreator->createArray($data); + + static::assertArrayHasKey('metadata', $data); + static::assertSame('metadata', $data['metadata']); + } +} diff --git a/tests/Functional/AdviceBehavior/ModifyArgumentPassedByReference/Target/ArrayCreator.php b/tests/Functional/AdviceBehavior/ModifyArgumentPassedByReference/Target/ArrayCreator.php index 9edccf2..5799788 100644 --- a/tests/Functional/AdviceBehavior/ModifyArgumentPassedByReference/Target/ArrayCreator.php +++ b/tests/Functional/AdviceBehavior/ModifyArgumentPassedByReference/Target/ArrayCreator.php @@ -1,11 +1,12 @@ - $data]; - } -} + $data */ + public function createArray(mixed &$data): void + { + $data = ['data' => $data]; + } +} diff --git a/tests/Functional/AdviceBehavior/MultipleAdvicesWithSameAdviceTypeOnSameTargetMethod/Aspect/ProfilePictureValidatorAspect.php b/tests/Functional/AdviceBehavior/MultipleAdvicesWithSameAdviceTypeOnSameTargetMethod/Aspect/ProfilePictureValidatorAspect.php index e5d95b4..0ab2a43 100644 --- a/tests/Functional/AdviceBehavior/MultipleAdvicesWithSameAdviceTypeOnSameTargetMethod/Aspect/ProfilePictureValidatorAspect.php +++ b/tests/Functional/AdviceBehavior/MultipleAdvicesWithSameAdviceTypeOnSameTargetMethod/Aspect/ProfilePictureValidatorAspect.php @@ -1,4 +1,5 @@ getArgument('image'); $imageInfo = getimagesize($image); $allowedFormats = [IMAGETYPE_PNG]; - if (!$imageInfo || !in_array($imageInfo[2], $allowedFormats)) { + if (!$imageInfo || !in_array($imageInfo[2], $allowedFormats, true)) { throw new Exception('Invalid image format'); } } @@ -33,19 +32,17 @@ public function checkImageFormat(BeforeMethodInvocation $invocation): void /** * @throws Exception */ - #[Before( - class: ProfileController::class, - method: 'uploadProfilePicture', - )] + #[Before(class: ProfileController::class, method: 'uploadProfilePicture')] public function checkImageSize(BeforeMethodInvocation $invocation): void { + /** @var string $image */ $image = $invocation->getArgument('image'); $imageSize = filesize($image); // 1 MB - $maxSize = 1048576; + $maxSize = 1_048_576; - if ($imageSize > $maxSize) { + if ($imageSize !== false && $imageSize > $maxSize) { throw new Exception('Image is too big'); } } diff --git a/tests/Functional/AdviceBehavior/MultipleAdvicesWithSameAdviceTypeOnSameTargetMethod/Kernel.php b/tests/Functional/AdviceBehavior/MultipleAdvicesWithSameAdviceTypeOnSameTargetMethod/Kernel.php index 9f6687f..9986a1d 100644 --- a/tests/Functional/AdviceBehavior/MultipleAdvicesWithSameAdviceTypeOnSameTargetMethod/Kernel.php +++ b/tests/Functional/AdviceBehavior/MultipleAdvicesWithSameAdviceTypeOnSameTargetMethod/Kernel.php @@ -10,6 +10,7 @@ class Kernel extends AopKernel { protected ?string $cacheDir = Util::CACHE_DIR; + /** @var array */ protected array $aspects = [ ProfilePictureValidatorAspect::class, ]; diff --git a/tests/Functional/AdviceBehavior/MultipleAdvicesWithSameAdviceTypeOnSameTargetMethod/MultipleAdvicesWithSameAdviceTypeOnSameTargetMethodTest.php b/tests/Functional/AdviceBehavior/MultipleAdvicesWithSameAdviceTypeOnSameTargetMethod/MultipleAdvicesWithSameAdviceTypeOnSameTargetMethodTest.php index 3b39dbf..f604009 100644 --- a/tests/Functional/AdviceBehavior/MultipleAdvicesWithSameAdviceTypeOnSameTargetMethod/MultipleAdvicesWithSameAdviceTypeOnSameTargetMethodTest.php +++ b/tests/Functional/AdviceBehavior/MultipleAdvicesWithSameAdviceTypeOnSameTargetMethod/MultipleAdvicesWithSameAdviceTypeOnSameTargetMethodTest.php @@ -1,4 +1,5 @@ uploadProfilePicture( - 'avatar', - self::AVATAR_PATH, - ); + $path = $profileController->uploadProfilePicture('avatar', self::AVATAR_PATH); // No exception thrown - $this->assertTrue(true); + static::assertTrue(true); - $this->assertSame( - 'https://example.com/avatar', - $path, - ); + static::assertSame('https://example.com/avatar', $path); // Invalid avatar size $exceptionThrown = false; try { - $profileController->uploadProfilePicture( - 'avatar', - self::AVATAR_HQ_PATH, - ); + $profileController->uploadProfilePicture('avatar', self::AVATAR_HQ_PATH); } catch (Exception $e) { $exceptionThrown = true; - $this->assertSame( - 'Image is too big', - $e->getMessage(), - ); + static::assertSame('Image is too big', $e->getMessage()); } - $this->assertTrue($exceptionThrown); + static::assertTrue($exceptionThrown); // Invalid avatar format $exceptionThrown = false; try { - $profileController->uploadProfilePicture( - 'avatar', - self::AVATAR_WRONG_FORMAT_PATH, - ); + $profileController->uploadProfilePicture('avatar', self::AVATAR_WRONG_FORMAT_PATH); } catch (Exception $e) { $exceptionThrown = true; - $this->assertSame( - 'Invalid image format', - $e->getMessage(), - ); + static::assertSame('Invalid image format', $e->getMessage()); } - $this->assertTrue($exceptionThrown); + static::assertTrue($exceptionThrown); } } diff --git a/tests/Functional/AdviceBehavior/MultipleAdvicesWithSameAdviceTypeOnSameTargetMethod/Target/ProfileController.php b/tests/Functional/AdviceBehavior/MultipleAdvicesWithSameAdviceTypeOnSameTargetMethod/Target/ProfileController.php index e3570b2..b06e60d 100644 --- a/tests/Functional/AdviceBehavior/MultipleAdvicesWithSameAdviceTypeOnSameTargetMethod/Target/ProfileController.php +++ b/tests/Functional/AdviceBehavior/MultipleAdvicesWithSameAdviceTypeOnSameTargetMethod/Target/ProfileController.php @@ -1,4 +1,5 @@ */ + protected array $aspects = [ + ModifyGroupPolicyAspect::class, + ]; +} diff --git a/tests/Functional/AdviceBehavior/NewClassCreationWithProxiedClasses/NewClassCreationWithProxiedClassesTest.php b/tests/Functional/AdviceBehavior/NewClassCreationWithProxiedClasses/NewClassCreationWithProxiedClassesTest.php index 091861d..3d68ae6 100644 --- a/tests/Functional/AdviceBehavior/NewClassCreationWithProxiedClasses/NewClassCreationWithProxiedClassesTest.php +++ b/tests/Functional/AdviceBehavior/NewClassCreationWithProxiedClasses/NewClassCreationWithProxiedClassesTest.php @@ -1,71 +1,69 @@ -addDefinitions([ - GroupPolicy::class => DI\create(GroupPolicy::class), - GroupMemberService::class => DI\autowire(), - ]); - - $container = $containerBuilder->build(); - - $service = $container->get(GroupMemberService::class); - - $this->assertInstanceOf(GroupMemberService::class, $service); - $this->assertEquals( - 'Original Policy Details', - $service->getPolicyDetails(), - ); - } - - public function testManualDefinition(): void - { - Util::clearCache(); - Kernel::init(); - - $containerBuilder = new ContainerBuilder(); - $containerBuilder->addDefinitions([ - GroupPolicy::class => DI\create(GroupPolicy::class), - GroupMemberService::class => static function (ContainerInterface $container) { - return new GroupMemberService( - $container->get(GroupPolicy::class) - ); - } - ]); - - $container = $containerBuilder->build(); - - $service = $container->get(GroupMemberService::class); - - $this->assertInstanceOf(GroupMemberService::class, $service); - $this->assertEquals( - 'Original Policy Details', - $service->getPolicyDetails(), - ); - } -} +addDefinitions([ + GroupPolicy::class => DI\create(GroupPolicy::class), + GroupMemberService::class => DI\autowire(), + ]); + + $container = $containerBuilder->build(); + + /** @var mixed $service */ + $service = $container->get(GroupMemberService::class); + + static::assertInstanceOf(GroupMemberService::class, $service); + static::assertSame('Original Policy Details', $service->getPolicyDetails()); + } + + public function testManualDefinition(): void + { + Util::clearCache(); + Kernel::init(); + + $containerBuilder = new ContainerBuilder(); + $containerBuilder->addDefinitions([ + GroupPolicy::class => DI\create(GroupPolicy::class), + GroupMemberService::class => static function (ContainerInterface $container): GroupMemberService { + /** @var mixed $policy */ + $policy = $container->get(GroupPolicy::class); + static::assertInstanceOf(GroupPolicy::class, $policy); + return new GroupMemberService($policy); + }, + ]); + + $container = $containerBuilder->build(); + + /** @var mixed $service */ + $service = $container->get(GroupMemberService::class); + + static::assertInstanceOf(GroupMemberService::class, $service); + static::assertSame('Original Policy Details', $service->getPolicyDetails()); + } +} diff --git a/tests/Functional/AdviceBehavior/NewClassCreationWithProxiedClasses/Target/GroupMemberService.php b/tests/Functional/AdviceBehavior/NewClassCreationWithProxiedClasses/Target/GroupMemberService.php index dbdae6d..be6d4f3 100644 --- a/tests/Functional/AdviceBehavior/NewClassCreationWithProxiedClasses/Target/GroupMemberService.php +++ b/tests/Functional/AdviceBehavior/NewClassCreationWithProxiedClasses/Target/GroupMemberService.php @@ -1,18 +1,18 @@ -groupPolicy = $groupPolicy; - } - - public function getPolicyDetails(): string - { - return $this->groupPolicy->getPolicyDetails(); - } -} +groupPolicy = $groupPolicy; + } + + public function getPolicyDetails(): string + { + return $this->groupPolicy->getPolicyDetails(); + } +} diff --git a/tests/Functional/AdviceBehavior/NewClassCreationWithProxiedClasses/Target/GroupPolicy.php b/tests/Functional/AdviceBehavior/NewClassCreationWithProxiedClasses/Target/GroupPolicy.php index ad71de0..a5049a6 100644 --- a/tests/Functional/AdviceBehavior/NewClassCreationWithProxiedClasses/Target/GroupPolicy.php +++ b/tests/Functional/AdviceBehavior/NewClassCreationWithProxiedClasses/Target/GroupPolicy.php @@ -1,11 +1,11 @@ -addTrace('DefaultAspect '.$invocation->getMethodName()); + $stackTrace->addTrace('DefaultAspect ' . $invocation->getMethodName()); } } diff --git a/tests/Functional/AdviceBehavior/OnlyPublicMethods/Aspect/OnlyPublicMethodsAspect.php b/tests/Functional/AdviceBehavior/OnlyPublicMethods/Aspect/OnlyPublicMethodsAspect.php index 889be0d..5630757 100644 --- a/tests/Functional/AdviceBehavior/OnlyPublicMethods/Aspect/OnlyPublicMethodsAspect.php +++ b/tests/Functional/AdviceBehavior/OnlyPublicMethods/Aspect/OnlyPublicMethodsAspect.php @@ -1,4 +1,5 @@ addTrace('OnlyPublicMethodsAspect '.$invocation->getMethodName()); + $stackTrace->addTrace('OnlyPublicMethodsAspect ' . $invocation->getMethodName()); } } diff --git a/tests/Functional/AdviceBehavior/OnlyPublicMethods/Kernel.php b/tests/Functional/AdviceBehavior/OnlyPublicMethods/Kernel.php index 8fe2c60..9cd2ae7 100644 --- a/tests/Functional/AdviceBehavior/OnlyPublicMethods/Kernel.php +++ b/tests/Functional/AdviceBehavior/OnlyPublicMethods/Kernel.php @@ -11,6 +11,7 @@ class Kernel extends AopKernel { protected ?string $cacheDir = Util::CACHE_DIR; + /** @var array */ protected array $aspects = [ DefaultAspect::class, OnlyPublicMethodsAspect::class, diff --git a/tests/Functional/AdviceBehavior/OnlyPublicMethods/OnlyPublicMethodsTest.php b/tests/Functional/AdviceBehavior/OnlyPublicMethods/OnlyPublicMethodsTest.php index 7ca8a8b..6df4e39 100644 --- a/tests/Functional/AdviceBehavior/OnlyPublicMethods/OnlyPublicMethodsTest.php +++ b/tests/Functional/AdviceBehavior/OnlyPublicMethods/OnlyPublicMethodsTest.php @@ -35,7 +35,7 @@ public function testOnlyPublicMethodsAreWoven(): void $targetClass->askTraitHelloHere(); $stackTrace = StackTrace::getInstance(); - $this->assertEquals( + static::assertEquals( [ // Call to $targetClass->helloWorld() = 2 Advice invocations 'DefaultAspect helloWorld', diff --git a/tests/Functional/AdviceBehavior/OnlyPublicMethods/Target/TargetClass.php b/tests/Functional/AdviceBehavior/OnlyPublicMethods/Target/TargetClass.php index ecec896..1352fca 100644 --- a/tests/Functional/AdviceBehavior/OnlyPublicMethods/Target/TargetClass.php +++ b/tests/Functional/AdviceBehavior/OnlyPublicMethods/Target/TargetClass.php @@ -6,9 +6,9 @@ class TargetClass extends TargetParentClass { use TargetTrait; - public function helloWorld() {} + public function helloWorld(): void {} - protected function helloHere() {} + protected function helloHere(): void {} public function askParentHelloHere(): void { diff --git a/tests/Functional/AdviceBehavior/OnlyPublicMethods/Target/TargetParentClass.php b/tests/Functional/AdviceBehavior/OnlyPublicMethods/Target/TargetParentClass.php index 15eb0e4..eaab43b 100644 --- a/tests/Functional/AdviceBehavior/OnlyPublicMethods/Target/TargetParentClass.php +++ b/tests/Functional/AdviceBehavior/OnlyPublicMethods/Target/TargetParentClass.php @@ -4,8 +4,7 @@ class TargetParentClass { - public function parentHelloWorld() {} + public function parentHelloWorld(): void {} - protected function parentHelloHere() {} + protected function parentHelloHere(): void {} } - diff --git a/tests/Functional/AdviceBehavior/OnlyPublicMethods/Target/TargetTrait.php b/tests/Functional/AdviceBehavior/OnlyPublicMethods/Target/TargetTrait.php index 7617f1e..98ff972 100644 --- a/tests/Functional/AdviceBehavior/OnlyPublicMethods/Target/TargetTrait.php +++ b/tests/Functional/AdviceBehavior/OnlyPublicMethods/Target/TargetTrait.php @@ -4,8 +4,7 @@ trait TargetTrait { - public function traitHelloWorld() {} + public function traitHelloWorld(): void {} - protected function traitHelloHere() {} + protected function traitHelloHere(): void {} } - diff --git a/tests/Functional/AdviceBehavior/PrivateProperties/EverythingAspect.php b/tests/Functional/AdviceBehavior/PrivateProperties/EverythingAspect.php index 104d1c5..ed9fd15 100644 --- a/tests/Functional/AdviceBehavior/PrivateProperties/EverythingAspect.php +++ b/tests/Functional/AdviceBehavior/PrivateProperties/EverythingAspect.php @@ -1,4 +1,5 @@ */ protected array $aspects = [EverythingAspect::class]; } diff --git a/tests/Functional/AdviceBehavior/PrivateProperties/PrivatePropertiesTest.php b/tests/Functional/AdviceBehavior/PrivateProperties/PrivatePropertiesTest.php index 206cb59..428885b 100644 --- a/tests/Functional/AdviceBehavior/PrivateProperties/PrivatePropertiesTest.php +++ b/tests/Functional/AdviceBehavior/PrivateProperties/PrivatePropertiesTest.php @@ -1,17 +1,18 @@ parentTokens()); + static::assertSame(['parent'], (new ParentInput())->parentTokens()); $child = new ChildInput(); - self::assertSame(['parent'], $child->parentTokens()); - self::assertSame('child', $child->childTokens()); - self::assertGreaterThanOrEqual(3, EverythingAspect::$calls); + static::assertSame(['parent'], $child->parentTokens()); + static::assertSame('child', $child->childTokens()); + static::assertGreaterThanOrEqual(3, EverythingAspect::$calls); } public function testSameTypesRetainIndependentValues(): void { $child = new SameTypeInput(); - self::assertSame(['parent'], $child->parentTokens()); - self::assertSame(['child'], $child->childTokens()); + static::assertSame(['parent'], $child->parentTokens()); + static::assertSame(['child'], $child->childTokens()); } public function testPromotedPropertyIsNotDuplicatedByForwardingConstructor(): void { $child = new PromotedInput('initial'); PropertyAccess::set($child, 'tokens', 'changed', PromotedInput::class); - self::assertSame('changed', $child->childTokens()); - self::assertSame(['parent'], $child->parentTokens()); + static::assertSame('changed', $child->childTokens()); + static::assertSame(['parent'], $child->parentTokens()); } public function testUnambiguousPropertyAccessDoesNotRequireScope(): void { $parent = new ParentInput(); + /** @var list $values */ $values = PropertyAccess::get($parent, 'unique'); $values[] = 'appended'; PropertyAccess::set($parent, 'unique', $values); - self::assertSame(['unique', 'appended'], $parent->unique()); + static::assertSame(['unique', 'appended'], $parent->unique()); PropertyAccess::set($parent, 'unique', ['assigned']); - self::assertSame(['assigned'], $parent->unique()); + static::assertSame(['assigned'], $parent->unique()); } public function testExplicitScopeSelectsTheOriginalDeclaration(): void @@ -65,9 +73,9 @@ public function testExplicitScopeSelectsTheOriginalDeclaration(): void $child = new ChildInput(); PropertyAccess::set($child, 'tokens', ['updated parent'], ParentInput::class); PropertyAccess::set($child, 'tokens', 'updated child', ChildInput::class); - self::assertSame(['updated parent'], $child->parentTokens()); - self::assertSame('updated child', $child->childTokens()); - self::assertSame(['updated parent'], PropertyAccess::get($child, 'tokens', ParentInput::class)); + static::assertSame(['updated parent'], $child->parentTokens()); + static::assertSame('updated child', $child->childTokens()); + static::assertSame(['updated parent'], PropertyAccess::get($child, 'tokens', ParentInput::class)); } public function testAmbiguousAccessRequiresExplicitScope(): void @@ -81,32 +89,32 @@ public function testAmbiguousAccessRequiresExplicitScope(): void public function testTraitPropertyRemainsSeparate(): void { $child = new TraitInput(); - self::assertSame(['parent'], $child->parentTokens()); - self::assertSame('trait', $child->childTokens()); + static::assertSame(['parent'], $child->parentTokens()); + static::assertSame('trait', $child->childTokens()); PropertyAccess::set($child, 'tokens', 'updated trait', TraitInput::class); - self::assertSame('updated trait', $child->childTokens()); + static::assertSame('updated trait', $child->childTokens()); } public function testCustomMagicAccessorsKeepVirtualPropertyBehavior(): void { $input = new MagicInput(); - self::assertSame('virtual', $input->example); + static::assertSame('virtual', $input->example); $input->example = 'assigned'; - self::assertSame('assigned', $input->example); - self::assertTrue(isset($input->example)); - self::assertSame(['example' => 'assigned'], PropertyAccess::get($input, 'values', MagicInput::class)); + static::assertSame('assigned', $input->example); + static::assertTrue(isset($input->example)); + static::assertSame(['example' => 'assigned'], PropertyAccess::get($input, 'values', MagicInput::class)); unset($input->example); - self::assertFalse(isset($input->example)); + static::assertFalse(isset($input->example)); } public function testStaticPrivatePropertiesHaveIndependentStorage(): void { - self::assertSame(['parent'], StaticChild::parentTokens()); - self::assertSame('child', StaticChild::childTokens()); + static::assertSame(['parent'], StaticChild::parentTokens()); + static::assertSame('child', StaticChild::childTokens()); PropertyAccess::set(StaticChild::class, 'tokens', ['updated'], StaticParent::class); PropertyAccess::set(StaticChild::class, 'tokens', 'updated child', StaticChild::class); - self::assertSame(['updated'], StaticChild::parentTokens()); - self::assertSame('updated child', PropertyAccess::get(StaticChild::class, 'tokens', StaticChild::class)); + static::assertSame(['updated'], StaticChild::parentTokens()); + static::assertSame('updated child', PropertyAccess::get(StaticChild::class, 'tokens', StaticChild::class)); } public function testUnknownScopeDoesNotFallBackToAnotherDeclaration(): void @@ -131,32 +139,35 @@ public function testPrivateParentAndPublicChildStayIndependent(): void { $child = new PublicInput(); $child->tokens = 'direct write'; - self::assertSame(['parent'], $child->parentTokens()); - self::assertSame('direct write', $child->childTokens()); - self::assertSame('direct write', PropertyAccess::get($child, 'tokens', PublicInput::class)); + static::assertSame(['parent'], $child->parentTokens()); + static::assertSame('direct write', $child->childTokens()); + static::assertSame('direct write', PropertyAccess::get($child, 'tokens', PublicInput::class)); } public function testNonPrivateOverridesShareStorage(): void { $child = new PublicGrandchild(); PropertyAccess::set($child, 'tokens', 'shared', PublicInput::class); - self::assertSame('shared', $child->tokens); - self::assertSame('shared', PropertyAccess::get($child, 'tokens', PublicGrandchild::class)); - self::assertSame(['parent'], $child->parentTokens()); + static::assertSame('shared', $child->tokens); + static::assertSame('shared', PropertyAccess::get($child, 'tokens', PublicGrandchild::class)); + static::assertSame(['parent'], $child->parentTokens()); } public function testUninitializedNullableReadThrowsWithoutInitializingProperty(): void { $child = new PublicInput(); $property = new \ReflectionProperty(PublicInput::class . '__AopProxied', 'uninitialized'); - self::assertFalse($property->isInitialized($child)); + static::assertFalse($property->isInitialized($child)); + static::assertFalse($child->hasUninitializedValue()); try { PropertyAccess::get($child, 'uninitialized', PublicInput::class); - self::fail('Expected an uninitialized property error.'); - } catch (\Error $error) { - self::assertStringContainsString('must not be accessed before initialization', $error->getMessage()); + static::fail('Expected an uninitialized property error.'); + } catch (\Throwable $error) { + static::assertInstanceOf(\Error::class, $error); + static::assertStringContainsString('must not be accessed before initialization', $error->getMessage()); } - self::assertFalse($property->isInitialized($child)); + static::assertFalse($property->isInitialized($child)); + static::assertFalse($child->hasUninitializedValue()); } public function testInvocationAccessorMutatesActualSubjectAndSupportsReferences(): void @@ -164,18 +175,19 @@ public function testInvocationAccessorMutatesActualSubjectAndSupportsReferences( $subject = new ParentInput(); $subject->unique(); $invocation = EverythingAspect::$invocation; - self::assertSame($subject, $invocation->getSubject()); - $properties = $invocation->properties(); + static::assertSame($subject, $invocation->getSubject()); + /** @var object{unique: list, missing?: mixed} $properties */ + $properties = $this->propertyView($invocation->properties()); $properties->unique[] = 'appended'; - $reference =& $properties->unique; + $reference = &$properties->unique; $reference[] = 'reference'; - self::assertSame(['unique', 'appended', 'reference'], $subject->unique()); - self::assertTrue(isset($properties->unique)); + static::assertSame(['unique', 'appended', 'reference'], $subject->unique()); + static::assertTrue(isset($properties->unique)); $properties->unique = ['assigned']; - self::assertSame(['assigned'], $subject->unique()); + static::assertSame(['assigned'], $subject->unique()); unset($properties->unique); - self::assertFalse(isset($properties->unique)); - self::assertFalse(isset($properties->missing)); + static::assertFalse(isset($properties->unique)); + static::assertFalse(isset($properties->missing)); } public function testInvocationAccessorSelectsParentAndChildScope(): void @@ -183,23 +195,24 @@ public function testInvocationAccessorSelectsParentAndChildScope(): void $subject = new ChildInput(); $subject->childTokens(); $invocation = EverythingAspect::$invocation; - $invocation->properties(ParentInput::class)->tokens = ['changed parent']; - $invocation->properties(ChildInput::class)->tokens = 'changed child'; - self::assertSame(['changed parent'], $subject->parentTokens()); - self::assertSame('changed child', $subject->childTokens()); + $invocation->properties(ParentInput::class)->__set('tokens', ['changed parent']); + $invocation->properties(ChildInput::class)->__set('tokens', 'changed child'); + static::assertSame(['changed parent'], $subject->parentTokens()); + static::assertSame('changed child', $subject->childTokens()); $this->expectException(\LogicException::class); - $invocation->properties()->tokens; + $invocation->properties()->__get('tokens'); } public function testStaticInvocationAccessor(): void { StaticChild::childTokens(); $invocation = EverythingAspect::$invocation; - self::assertNull($invocation->getSubject()); - $properties = $invocation->properties(StaticChild::class); + static::assertNull($invocation->getSubject()); + /** @var object{tokens: string} $properties */ + $properties = $this->propertyView($invocation->properties(StaticChild::class)); $properties->tokens = 'assigned'; - self::assertSame('assigned', $properties->tokens); - self::assertSame('assigned', StaticChild::childTokens()); + static::assertSame('assigned', $properties->tokens); + static::assertSame('assigned', StaticChild::childTokens()); $this->expectException(\Error::class); unset($properties->tokens); } @@ -208,10 +221,11 @@ public function testAccessorDoesNotInitializeNullablePropertyOnRead(): void { $subject = new PublicInput(); $subject->childTokens(); - $properties = EverythingAspect::$invocation->properties(PublicInput::class); - self::assertFalse(isset($properties->uninitialized)); + /** @var object{uninitialized: ?string} $properties */ + $properties = $this->propertyView(EverythingAspect::$invocation->properties(PublicInput::class)); + static::assertFalse(isset($properties->uninitialized)); $this->expectException(\Error::class); - $properties->uninitialized; + static::fail('Expected an uninitialized property error: ' . var_export($properties->uninitialized, true)); } public function testUnsetPropertyReadDoesNotInvokeSubjectMagicGetter(): void @@ -219,15 +233,21 @@ public function testUnsetPropertyReadDoesNotInvokeSubjectMagicGetter(): void $subject = new class { private string $value = 'initial'; public int $calls = 0; - public function __get(string $name): mixed { $this->calls++; return 'magic'; } + + public function __get(string $name): mixed + { + $this->calls++; + return 'magic'; + } }; - $properties = new \Okapi\Aop\Invocation\PropertyAccessor($subject); + /** @var object{value: string} $properties */ + $properties = $this->propertyView(new \Okapi\Aop\Invocation\PropertyAccessor($subject)); unset($properties->value); try { - $properties->value; - self::fail('Expected uninitialized property error.'); - } catch (\Error) { - self::assertSame(0, $subject->calls); + static::fail('Expected uninitialized property error: ' . $properties->value); + } catch (\Throwable $error) { + static::assertInstanceOf(\Error::class, $error); + static::assertSame(0, $subject->calls); } } @@ -235,10 +255,11 @@ public function testRedeclaredPublicStaticPropertiesRequireScope(): void { $parent = Target\SharedStaticParent::class; $child = Target\SharedStaticChild::class; - $properties = new \Okapi\Aop\Invocation\PropertyAccessor($child, $parent); + /** @var object{value: int} $properties */ + $properties = $this->propertyView(new \Okapi\Aop\Invocation\PropertyAccessor($child, $parent)); $properties->value = 3; - self::assertSame(3, $parent::$value); - self::assertSame(2, $child::$value); + static::assertSame(3, $parent::$value); + static::assertSame(2, $child::$value); $this->expectException(\LogicException::class); PropertyAccess::get($child, 'value'); } @@ -248,12 +269,17 @@ public function testWriteAfterUnsetPreservesNativeMagicSetterBehavior(): void $subject = new class { private string $value = 'initial'; public int $calls = 0; - public function __set(string $name, mixed $value): void { $this->calls++; } + + public function __set(string $name, mixed $value): void + { + $this->calls++; + } }; - $properties = new \Okapi\Aop\Invocation\PropertyAccessor($subject); + /** @var object{value: string} $properties */ + $properties = $this->propertyView(new \Okapi\Aop\Invocation\PropertyAccessor($subject)); unset($properties->value); $properties->value = 'new'; - self::assertSame(1, $subject->calls); - self::assertFalse(isset($properties->value)); + static::assertSame(1, $subject->calls); + static::assertFalse(isset($properties->value)); } } diff --git a/tests/Functional/AdviceBehavior/PrivateProperties/Target/ChildInput.php b/tests/Functional/AdviceBehavior/PrivateProperties/Target/ChildInput.php index 606a56e..b581914 100644 --- a/tests/Functional/AdviceBehavior/PrivateProperties/Target/ChildInput.php +++ b/tests/Functional/AdviceBehavior/PrivateProperties/Target/ChildInput.php @@ -1,8 +1,13 @@ tokens; } + + public function childTokens(): mixed + { + return $this->tokens; + } } diff --git a/tests/Functional/AdviceBehavior/PrivateProperties/Target/MagicInput.php b/tests/Functional/AdviceBehavior/PrivateProperties/Target/MagicInput.php index 7929e1c..d5374f8 100644 --- a/tests/Functional/AdviceBehavior/PrivateProperties/Target/MagicInput.php +++ b/tests/Functional/AdviceBehavior/PrivateProperties/Target/MagicInput.php @@ -1,11 +1,30 @@ */ private array $values = []; - public function __get(string $name): mixed { return $this->values[$name] ?? 'virtual'; } - public function __set(string $name, mixed $value): void { $this->values[$name] = $value; } - public function __isset(string $name): bool { return isset($this->values[$name]); } - public function __unset(string $name): void { unset($this->values[$name]); } + + public function __get(string $name): mixed + { + return $this->values[$name] ?? 'virtual'; + } + + public function __set(string $name, mixed $value): void + { + $this->values[$name] = $value; + } + + public function __isset(string $name): bool + { + return isset($this->values[$name]); + } + + public function __unset(string $name): void + { + unset($this->values[$name]); + } } diff --git a/tests/Functional/AdviceBehavior/PrivateProperties/Target/ParentInput.php b/tests/Functional/AdviceBehavior/PrivateProperties/Target/ParentInput.php index 4081e05..32bb10b 100644 --- a/tests/Functional/AdviceBehavior/PrivateProperties/Target/ParentInput.php +++ b/tests/Functional/AdviceBehavior/PrivateProperties/Target/ParentInput.php @@ -1,11 +1,23 @@ */ private array $tokens = ['parent']; + /** @var list */ private array $unique = ['unique']; - public function parentTokens(): array { return $this->tokens; } - public function unique(): array { return $this->unique; } + /** @return list */ + public function parentTokens(): array + { + return $this->tokens; + } + + /** @return list */ + public function unique(): array + { + return $this->unique; + } } diff --git a/tests/Functional/AdviceBehavior/PrivateProperties/Target/PromotedInput.php b/tests/Functional/AdviceBehavior/PrivateProperties/Target/PromotedInput.php index a1c7e7e..a10e812 100644 --- a/tests/Functional/AdviceBehavior/PrivateProperties/Target/PromotedInput.php +++ b/tests/Functional/AdviceBehavior/PrivateProperties/Target/PromotedInput.php @@ -1,8 +1,15 @@ tokens; } + public function __construct( + private string $tokens = 'promoted', + ) {} + + public function childTokens(): string + { + return $this->tokens; + } } diff --git a/tests/Functional/AdviceBehavior/PrivateProperties/Target/PublicGrandchild.php b/tests/Functional/AdviceBehavior/PrivateProperties/Target/PublicGrandchild.php index 1bce681..dca2ca8 100644 --- a/tests/Functional/AdviceBehavior/PrivateProperties/Target/PublicGrandchild.php +++ b/tests/Functional/AdviceBehavior/PrivateProperties/Target/PublicGrandchild.php @@ -1,4 +1,5 @@ tokens; } + + public function hasUninitializedValue(): bool + { + return isset($this->uninitialized); + } + + public function childTokens(): string + { + return $this->tokens; + } } diff --git a/tests/Functional/AdviceBehavior/PrivateProperties/Target/SameTypeInput.php b/tests/Functional/AdviceBehavior/PrivateProperties/Target/SameTypeInput.php index 4191e49..26210f8 100644 --- a/tests/Functional/AdviceBehavior/PrivateProperties/Target/SameTypeInput.php +++ b/tests/Functional/AdviceBehavior/PrivateProperties/Target/SameTypeInput.php @@ -1,8 +1,15 @@ */ private array $tokens = ['child']; - public function childTokens(): array { return $this->tokens; } + + /** @return list */ + public function childTokens(): array + { + return $this->tokens; + } } diff --git a/tests/Functional/AdviceBehavior/PrivateProperties/Target/SharedStaticChild.php b/tests/Functional/AdviceBehavior/PrivateProperties/Target/SharedStaticChild.php index 454564e..a9e89e5 100644 --- a/tests/Functional/AdviceBehavior/PrivateProperties/Target/SharedStaticChild.php +++ b/tests/Functional/AdviceBehavior/PrivateProperties/Target/SharedStaticChild.php @@ -1,4 +1,5 @@ */ private static array $tokens = ['parent']; - public static function parentTokens(): array { return self::$tokens; } + + /** @return list */ + public static function parentTokens(): array + { + return self::$tokens; + } } diff --git a/tests/Functional/AdviceBehavior/PrivateProperties/Target/TokenTrait.php b/tests/Functional/AdviceBehavior/PrivateProperties/Target/TokenTrait.php index c474ee2..b467db3 100644 --- a/tests/Functional/AdviceBehavior/PrivateProperties/Target/TokenTrait.php +++ b/tests/Functional/AdviceBehavior/PrivateProperties/Target/TokenTrait.php @@ -1,8 +1,13 @@ tokens; } + + public function childTokens(): string + { + return $this->tokens; + } } diff --git a/tests/Functional/AdviceBehavior/PrivateProperties/Target/TraitInput.php b/tests/Functional/AdviceBehavior/PrivateProperties/Target/TraitInput.php index 2b3d550..9ff7e8c 100644 --- a/tests/Functional/AdviceBehavior/PrivateProperties/Target/TraitInput.php +++ b/tests/Functional/AdviceBehavior/PrivateProperties/Target/TraitInput.php @@ -1,4 +1,5 @@ proceed(); - $result = $result / (1 - BankingSystem::DEPOSIT_FEE_PERCENTAGE / 100); + $result /= 1 - (BankingSystem::DEPOSIT_FEE_PERCENTAGE / 100); $invocation->setResult($result); } - #[After( - BankingSystem::class, - 'addFeeToWithdraw', - )] + #[After(BankingSystem::class, 'addFeeToWithdraw')] public function removeFeeFromWithdraw(AfterMethodInvocation $invocation): void { + /** @var float $result */ $result = $invocation->proceed(); - $result = $result / (1 + BankingSystem::WITHDRAW_FEE_PERCENTAGE / 100); + $result /= 1 + (BankingSystem::WITHDRAW_FEE_PERCENTAGE / 100); $invocation->setResult($result); } diff --git a/tests/Functional/AdviceBehavior/ProtectedAndPrivateMethods/Kernel.php b/tests/Functional/AdviceBehavior/ProtectedAndPrivateMethods/Kernel.php index 4e3e6ed..53597bc 100644 --- a/tests/Functional/AdviceBehavior/ProtectedAndPrivateMethods/Kernel.php +++ b/tests/Functional/AdviceBehavior/ProtectedAndPrivateMethods/Kernel.php @@ -10,6 +10,7 @@ class Kernel extends AopKernel { protected ?string $cacheDir = Util::CACHE_DIR; + /** @var array */ protected array $aspects = [ BankingAspect::class, ]; diff --git a/tests/Functional/AdviceBehavior/ProtectedAndPrivateMethods/ProtectedAndPrivateMethodsTest.php b/tests/Functional/AdviceBehavior/ProtectedAndPrivateMethods/ProtectedAndPrivateMethodsTest.php index 574cfe9..1d2d6db 100644 --- a/tests/Functional/AdviceBehavior/ProtectedAndPrivateMethods/ProtectedAndPrivateMethodsTest.php +++ b/tests/Functional/AdviceBehavior/ProtectedAndPrivateMethods/ProtectedAndPrivateMethodsTest.php @@ -29,17 +29,11 @@ public function testProtectedAndPrivateMethods(): void $bankingSystem->deposit(100.0); $balance = $bankingSystem->getBalance(); - $this->assertEquals( - 100.0, - $balance, - ); + static::assertSame(100.0, $balance); $bankingSystem->withdraw(50.0); $balance = $bankingSystem->getBalance(); - $this->assertEquals( - 50.0, - $balance, - ); + static::assertSame(50.0, $balance); } } diff --git a/tests/Functional/AdviceBehavior/ProtectedAndPrivateMethods/Target/BankingSystem.php b/tests/Functional/AdviceBehavior/ProtectedAndPrivateMethods/Target/BankingSystem.php index 059cb5d..a6b5138 100644 --- a/tests/Functional/AdviceBehavior/ProtectedAndPrivateMethods/Target/BankingSystem.php +++ b/tests/Functional/AdviceBehavior/ProtectedAndPrivateMethods/Target/BankingSystem.php @@ -16,7 +16,7 @@ public function deposit(float $amount): void protected function removeFeeFromDeposit(float $amount): float { - return $amount - ($amount * self::DEPOSIT_FEE_PERCENTAGE / 100); + return $amount - (($amount * self::DEPOSIT_FEE_PERCENTAGE) / 100); } public function withdraw(float $amount): void @@ -26,7 +26,7 @@ public function withdraw(float $amount): void private function addFeeToWithdraw(float $amount): float { - return $amount + ($amount * self::WITHDRAW_FEE_PERCENTAGE / 100); + return $amount + (($amount * self::WITHDRAW_FEE_PERCENTAGE) / 100); } public function getBalance(): float diff --git a/tests/Functional/AdviceBehavior/Readonly/Aspect/ReadonlyAspect.php b/tests/Functional/AdviceBehavior/Readonly/Aspect/ReadonlyAspect.php index 8f3bfc2..72acd8e 100644 --- a/tests/Functional/AdviceBehavior/Readonly/Aspect/ReadonlyAspect.php +++ b/tests/Functional/AdviceBehavior/Readonly/Aspect/ReadonlyAspect.php @@ -1,16 +1,14 @@ - */ + protected array $aspects = [ + ReadonlyAspect::class, + ]; +} diff --git a/tests/Functional/AdviceBehavior/Readonly/ReadonlyTest.php b/tests/Functional/AdviceBehavior/Readonly/ReadonlyTest.php index 17485dc..84f5f2a 100644 --- a/tests/Functional/AdviceBehavior/Readonly/ReadonlyTest.php +++ b/tests/Functional/AdviceBehavior/Readonly/ReadonlyTest.php @@ -1,48 +1,49 @@ -markTestSkipped('Readonly classes are supported only in PHP 8.2 and later.'); - } - - Util::clearCache(); - Kernel::init(); - - $this->assertWillBeWoven(ReadonlyClass::class); - - new ReadonlyClass(); - - $this->assertTrue(true); - } - - public function testReadonlyPromotedProperties(): void - { - Util::clearCache(); - Kernel::init(); - - $this->assertWillBeWoven(ReadonlyPromotedProperties::class); - - new ReadonlyPromotedProperties('Walter Woshid', 42); - - $this->assertTrue(true); - } -} +assertWillBeWoven(ReadonlyClass::class); + + new ReadonlyClass(); + + static::assertTrue(true); + } + + public function testReadonlyPromotedProperties(): void + { + Util::clearCache(); + Kernel::init(); + + $this->assertWillBeWoven(ReadonlyPromotedProperties::class); + + new ReadonlyPromotedProperties('Walter Woshid', 42); + + static::assertTrue(true); + } +} diff --git a/tests/Functional/AdviceBehavior/Readonly/Target/ReadonlyClass.php b/tests/Functional/AdviceBehavior/Readonly/Target/ReadonlyClass.php index 10a242e..c57ca52 100644 --- a/tests/Functional/AdviceBehavior/Readonly/Target/ReadonlyClass.php +++ b/tests/Functional/AdviceBehavior/Readonly/Target/ReadonlyClass.php @@ -1,8 +1,9 @@ ->> */ public static array $cachedRoutes = []; - #[Around( - class: RouteCaching::class, - method: 'getRoutes', - )] + #[Around(class: RouteCaching::class, method: 'getRoutes')] public function cacheRoutes(AroundMethodInvocation $invocation): void { $arguments = $invocation->getArguments(); @@ -28,16 +27,19 @@ public function cacheRoutes(AroundMethodInvocation $invocation): void return; } + /** @var array> $routes */ $routes = $invocation->proceed(); $this->storeInCache($cacheKey, $routes); } + /** @return array>|null */ private function getFromCache(string $cacheKey): ?array { return self::$cachedRoutes[$cacheKey] ?? null; } + /** @param array> $routes */ private function storeInCache(string $cacheKey, array $routes): void { self::$cachedRoutes[$cacheKey] = $routes; diff --git a/tests/Functional/AdviceBehavior/TraitAdvice/Kernel.php b/tests/Functional/AdviceBehavior/TraitAdvice/Kernel.php index b73ff98..8b7984b 100644 --- a/tests/Functional/AdviceBehavior/TraitAdvice/Kernel.php +++ b/tests/Functional/AdviceBehavior/TraitAdvice/Kernel.php @@ -10,6 +10,7 @@ class Kernel extends AopKernel { protected ?string $cacheDir = Util::CACHE_DIR; + /** @var array */ protected array $aspects = [ RouteCachingAspect::class, ]; diff --git a/tests/Functional/AdviceBehavior/TraitAdvice/Target/RouteCaching.php b/tests/Functional/AdviceBehavior/TraitAdvice/Target/RouteCaching.php index ce871b8..890c345 100644 --- a/tests/Functional/AdviceBehavior/TraitAdvice/Target/RouteCaching.php +++ b/tests/Functional/AdviceBehavior/TraitAdvice/Target/RouteCaching.php @@ -4,6 +4,7 @@ trait RouteCaching { + /** @return array> */ public function getRoutes(): array { return [ diff --git a/tests/Functional/AdviceBehavior/TraitAdvice/TraitAdviceTest.php b/tests/Functional/AdviceBehavior/TraitAdvice/TraitAdviceTest.php index 48308ad..6325875 100644 --- a/tests/Functional/AdviceBehavior/TraitAdvice/TraitAdviceTest.php +++ b/tests/Functional/AdviceBehavior/TraitAdvice/TraitAdviceTest.php @@ -26,9 +26,9 @@ public function testTraitAdvice(): void $router = new Router(); $routes = $router->getRoutes(); - $this->assertCount(1, $routes); + static::assertCount(1, $routes); $cachedRoutes = RouteCachingAspect::$cachedRoutes; - $this->assertCount(1, $cachedRoutes); + static::assertCount(1, $cachedRoutes); } } diff --git a/tests/Functional/AdviceBehavior/VariadicParameters/Aspect/StringPrefixerAspect.php b/tests/Functional/AdviceBehavior/VariadicParameters/Aspect/StringPrefixerAspect.php index d2aceda..94c0cab 100644 --- a/tests/Functional/AdviceBehavior/VariadicParameters/Aspect/StringPrefixerAspect.php +++ b/tests/Functional/AdviceBehavior/VariadicParameters/Aspect/StringPrefixerAspect.php @@ -1,4 +1,5 @@ getArgument('prefix'); + /** @var list $ids */ $ids = $invocation->getArgument('ids'); foreach ($ids as &$id) { diff --git a/tests/Functional/AdviceBehavior/VariadicParameters/Target/IdHelper.php b/tests/Functional/AdviceBehavior/VariadicParameters/Target/IdHelper.php index 1dff828..81c94ae 100644 --- a/tests/Functional/AdviceBehavior/VariadicParameters/Target/IdHelper.php +++ b/tests/Functional/AdviceBehavior/VariadicParameters/Target/IdHelper.php @@ -2,11 +2,11 @@ namespace Okapi\Aop\Tests\Functional\AdviceBehavior\VariadicParameters\Target; - use Okapi\Aop\Tests\Functional\AdviceBehavior\VariadicParameters\Aspect\StringPrefixerAspect; class IdHelper { + /** @return array */ #[StringPrefixerAspect] public function createIds(string $prefix, string ...$ids): array { diff --git a/tests/Functional/AdviceBehavior/VariadicParameters/VariadicParametersTest.php b/tests/Functional/AdviceBehavior/VariadicParameters/VariadicParametersTest.php index 175a53a..6ecf25a 100644 --- a/tests/Functional/AdviceBehavior/VariadicParameters/VariadicParametersTest.php +++ b/tests/Functional/AdviceBehavior/VariadicParameters/VariadicParametersTest.php @@ -32,7 +32,7 @@ public function testVariadicParameters(): void $expectedResult = ['prefix-id1', 'prefix-id2', 'prefix-id3']; - $this->assertSame($expectedResult, $result); + static::assertSame($expectedResult, $result); } public function testVariadicParametersWithoutAop(): void @@ -47,6 +47,6 @@ public function testVariadicParametersWithoutAop(): void $expectedResult = ['id1', 'id2', 'id3']; - $this->assertSame($expectedResult, $result); + static::assertSame($expectedResult, $result); } } diff --git a/tests/Functional/AspectMatching/AdviceMatchingMultipleClassesAndMethods/AdviceMatchingMultipleClassesAndMethodsTest.php b/tests/Functional/AspectMatching/AdviceMatchingMultipleClassesAndMethods/AdviceMatchingMultipleClassesAndMethodsTest.php index 51791ea..40e5a0d 100644 --- a/tests/Functional/AspectMatching/AdviceMatchingMultipleClassesAndMethods/AdviceMatchingMultipleClassesAndMethodsTest.php +++ b/tests/Functional/AspectMatching/AdviceMatchingMultipleClassesAndMethods/AdviceMatchingMultipleClassesAndMethodsTest.php @@ -26,12 +26,12 @@ public function testAdviceMatchingMultipleClassesAndMethods(): void $this->assertWillBeWoven(Product::class); $product = new Product(); $productPrice = $product->getPrice(); - $this->assertEquals(90.00, $productPrice); + static::assertSame(90.00, $productPrice); $this->assertWillBeWoven(Order::class); $order = new Order(); $orderTotal = $order->getTotal(); - $this->assertEquals(400.00, $orderTotal); + static::assertSame(400.00, $orderTotal); } public function testCachedAdviceMatchingMultipleClassesAndMethods(): void @@ -41,11 +41,11 @@ public function testCachedAdviceMatchingMultipleClassesAndMethods(): void $this->assertAspectLoadedFromCache(Product::class); $product = new Product(); $productPrice = $product->getPrice(); - $this->assertEquals(90.00, $productPrice); + static::assertSame(90.00, $productPrice); $this->assertAspectLoadedFromCache(Order::class); $order = new Order(); $orderTotal = $order->getTotal(); - $this->assertEquals(400.00, $orderTotal); + static::assertSame(400.00, $orderTotal); } } diff --git a/tests/Functional/AspectMatching/AdviceMatchingMultipleClassesAndMethods/Aspect/DiscountAspect.php b/tests/Functional/AspectMatching/AdviceMatchingMultipleClassesAndMethods/Aspect/DiscountAspect.php index d378b47..582056c 100644 --- a/tests/Functional/AspectMatching/AdviceMatchingMultipleClassesAndMethods/Aspect/DiscountAspect.php +++ b/tests/Functional/AspectMatching/AdviceMatchingMultipleClassesAndMethods/Aspect/DiscountAspect.php @@ -1,4 +1,5 @@ getSubject(); $productDiscount = 0.1; - $orderDiscount = 0.2; + $orderDiscount = 0.2; if ($subject instanceof Product) { + /** @var float $oldPrice */ $oldPrice = $invocation->proceed(); $newPrice = $oldPrice - ($oldPrice * $productDiscount); @@ -30,6 +29,7 @@ public function applyDiscount(AfterMethodInvocation $invocation): void } if ($subject instanceof Order) { + /** @var float $oldTotal */ $oldTotal = $invocation->proceed(); $newTotal = $oldTotal - ($oldTotal * $orderDiscount); diff --git a/tests/Functional/AspectMatching/AdviceMatchingMultipleClassesAndMethods/Kernel.php b/tests/Functional/AspectMatching/AdviceMatchingMultipleClassesAndMethods/Kernel.php index 5c447e6..375a5eb 100644 --- a/tests/Functional/AspectMatching/AdviceMatchingMultipleClassesAndMethods/Kernel.php +++ b/tests/Functional/AspectMatching/AdviceMatchingMultipleClassesAndMethods/Kernel.php @@ -10,6 +10,7 @@ class Kernel extends AopKernel { protected ?string $cacheDir = Util::CACHE_DIR; + /** @var array */ protected array $aspects = [ DiscountAspect::class, ]; diff --git a/tests/Functional/AspectMatching/ClassHierarchyOnlyInvokedOnce/Aspect.php b/tests/Functional/AspectMatching/ClassHierarchyOnlyInvokedOnce/Aspect.php index 7fabc56..da61060 100644 --- a/tests/Functional/AspectMatching/ClassHierarchyOnlyInvokedOnce/Aspect.php +++ b/tests/Functional/AspectMatching/ClassHierarchyOnlyInvokedOnce/Aspect.php @@ -1,4 +1,5 @@ addTrace("Method call $count"); + $stackTrace->addTrace("Method call {$count}"); } } diff --git a/tests/Functional/AspectMatching/ClassHierarchyOnlyInvokedOnce/ClassHierarchyOnlyInvokedOnceTest.php b/tests/Functional/AspectMatching/ClassHierarchyOnlyInvokedOnce/ClassHierarchyOnlyInvokedOnceTest.php index 744d6af..62561e2 100644 --- a/tests/Functional/AspectMatching/ClassHierarchyOnlyInvokedOnce/ClassHierarchyOnlyInvokedOnceTest.php +++ b/tests/Functional/AspectMatching/ClassHierarchyOnlyInvokedOnce/ClassHierarchyOnlyInvokedOnceTest.php @@ -30,7 +30,7 @@ public function testAdviceIsInvokedOnlyOnce(): void $stackTrace = StackTrace::getInstance(); - $this->assertEquals( + static::assertEquals( [ 'Method call 1', 'Method call 2', diff --git a/tests/Functional/AspectMatching/ClassHierarchyOnlyInvokedOnce/Kernel.php b/tests/Functional/AspectMatching/ClassHierarchyOnlyInvokedOnce/Kernel.php index 56eafaf..bac64c7 100644 --- a/tests/Functional/AspectMatching/ClassHierarchyOnlyInvokedOnce/Kernel.php +++ b/tests/Functional/AspectMatching/ClassHierarchyOnlyInvokedOnce/Kernel.php @@ -9,6 +9,7 @@ class Kernel extends AopKernel { protected ?string $cacheDir = Util::CACHE_DIR; + /** @var array */ protected array $aspects = [ Aspect::class, ]; diff --git a/tests/Functional/AspectMatching/ClassHierarchyOnlyInvokedOnce/Target/TargetClassA.php b/tests/Functional/AspectMatching/ClassHierarchyOnlyInvokedOnce/Target/TargetClassA.php index 8547ce2..098adde 100644 --- a/tests/Functional/AspectMatching/ClassHierarchyOnlyInvokedOnce/Target/TargetClassA.php +++ b/tests/Functional/AspectMatching/ClassHierarchyOnlyInvokedOnce/Target/TargetClassA.php @@ -2,6 +2,4 @@ namespace Okapi\Aop\Tests\Functional\AspectMatching\ClassHierarchyOnlyInvokedOnce\Target; -class TargetClassA extends TargetClassB -{ -} +class TargetClassA extends TargetClassB {} diff --git a/tests/Functional/AspectMatching/ClassHierarchyOnlyInvokedOnce/Target/TargetClassB.php b/tests/Functional/AspectMatching/ClassHierarchyOnlyInvokedOnce/Target/TargetClassB.php index 3ea5836..92e977d 100644 --- a/tests/Functional/AspectMatching/ClassHierarchyOnlyInvokedOnce/Target/TargetClassB.php +++ b/tests/Functional/AspectMatching/ClassHierarchyOnlyInvokedOnce/Target/TargetClassB.php @@ -2,6 +2,4 @@ namespace Okapi\Aop\Tests\Functional\AspectMatching\ClassHierarchyOnlyInvokedOnce\Target; -class TargetClassB extends TargetClassC -{ -} +class TargetClassB extends TargetClassC {} diff --git a/tests/Functional/AspectMatching/ClassHierarchyOnlyInvokedOnce/Target/TargetClassC.php b/tests/Functional/AspectMatching/ClassHierarchyOnlyInvokedOnce/Target/TargetClassC.php index 1b22fd9..11af849 100644 --- a/tests/Functional/AspectMatching/ClassHierarchyOnlyInvokedOnce/Target/TargetClassC.php +++ b/tests/Functional/AspectMatching/ClassHierarchyOnlyInvokedOnce/Target/TargetClassC.php @@ -4,5 +4,5 @@ class TargetClassC { - public function helloWorld() {} + public function helloWorld(): void {} } diff --git a/tests/Functional/AspectMatching/InterceptTraitMethods/AdviceInterceptTraitMethodsTest.php b/tests/Functional/AspectMatching/InterceptTraitMethods/AdviceInterceptTraitMethodsTest.php index 57299d3..56b064e 100644 --- a/tests/Functional/AspectMatching/InterceptTraitMethods/AdviceInterceptTraitMethodsTest.php +++ b/tests/Functional/AspectMatching/InterceptTraitMethods/AdviceInterceptTraitMethodsTest.php @@ -34,7 +34,7 @@ public function testTraitMethodsNotWoven(): void $targetClass->helloHere(); $stackTrace = StackTrace::getInstance(); - $this->assertEquals( + static::assertEquals( [ // First call to TargetClass::helloWorld() 'DefaultAspect', diff --git a/tests/Functional/AspectMatching/InterceptTraitMethods/Aspect/DefaultAspect.php b/tests/Functional/AspectMatching/InterceptTraitMethods/Aspect/DefaultAspect.php index e30e90a..b942817 100644 --- a/tests/Functional/AspectMatching/InterceptTraitMethods/Aspect/DefaultAspect.php +++ b/tests/Functional/AspectMatching/InterceptTraitMethods/Aspect/DefaultAspect.php @@ -1,4 +1,5 @@ */ protected array $aspects = [ DefaultAspect::class, InterceptTraitMethodsAspect::class, diff --git a/tests/Functional/AspectMatching/InterceptTraitMethods/Target/TargetClass.php b/tests/Functional/AspectMatching/InterceptTraitMethods/Target/TargetClass.php index e77258c..c16a022 100644 --- a/tests/Functional/AspectMatching/InterceptTraitMethods/Target/TargetClass.php +++ b/tests/Functional/AspectMatching/InterceptTraitMethods/Target/TargetClass.php @@ -6,5 +6,5 @@ class TargetClass { use TargetTrait; - public function helloWorld() {} + public function helloWorld(): void {} } diff --git a/tests/Functional/AspectMatching/InterceptTraitMethods/Target/TargetTrait.php b/tests/Functional/AspectMatching/InterceptTraitMethods/Target/TargetTrait.php index 8e4efe6..d9e6ba5 100644 --- a/tests/Functional/AspectMatching/InterceptTraitMethods/Target/TargetTrait.php +++ b/tests/Functional/AspectMatching/InterceptTraitMethods/Target/TargetTrait.php @@ -4,6 +4,5 @@ trait TargetTrait { - public function helloHere() {} + public function helloHere(): void {} } - diff --git a/tests/Functional/AspectMatching/SelfType/Aspect/SalaryIncreaserAspect.php b/tests/Functional/AspectMatching/SelfType/Aspect/SalaryIncreaserAspect.php index a826533..76aac77 100644 --- a/tests/Functional/AspectMatching/SelfType/Aspect/SalaryIncreaserAspect.php +++ b/tests/Functional/AspectMatching/SelfType/Aspect/SalaryIncreaserAspect.php @@ -1,4 +1,5 @@ getArgument('salaryIncrease'); - $invocation->setArgument( - 'salaryIncrease', - $salary * 2, - ); + $invocation->setArgument('salaryIncrease', $salary * 2); } } diff --git a/tests/Functional/AspectMatching/SelfType/Kernel.php b/tests/Functional/AspectMatching/SelfType/Kernel.php index 6d9013f..07bbbc6 100644 --- a/tests/Functional/AspectMatching/SelfType/Kernel.php +++ b/tests/Functional/AspectMatching/SelfType/Kernel.php @@ -10,6 +10,7 @@ class Kernel extends AopKernel { protected ?string $cacheDir = Util::CACHE_DIR; + /** @var array */ protected array $aspects = [ SalaryIncreaserAspect::class, ]; diff --git a/tests/Functional/AspectMatching/SelfType/SelfTypeTest.php b/tests/Functional/AspectMatching/SelfType/SelfTypeTest.php index 022121c..5258f69 100644 --- a/tests/Functional/AspectMatching/SelfType/SelfTypeTest.php +++ b/tests/Functional/AspectMatching/SelfType/SelfTypeTest.php @@ -32,32 +32,19 @@ public function testSelfType(): void $promotedEmployee = $employee->promote($employee, $salaryIncrease); - $this->assertInstanceOf(Employee::class, $promotedEmployee); - $this->assertInstanceOf(AbstractEmployee::class, $promotedEmployee); - $this->assertSame( - $employee->getName(), - $promotedEmployee->getName(), - ); - $this->assertSame( - $employee->getSalary() + ($salaryIncrease * 2), - $promotedEmployee->getSalary(), - ); - + static::assertInstanceOf(Employee::class, $promotedEmployee); + static::assertInstanceOf(AbstractEmployee::class, $promotedEmployee); + static::assertSame($employee->getName(), $promotedEmployee->getName()); + static::assertSame($employee->getSalary() + ($salaryIncrease * 2), $promotedEmployee->getSalary()); $salaryDecrease = 1000.0; $demotedEmployee = $promotedEmployee->demote($promotedEmployee, $salaryDecrease); - $this->assertInstanceOf(PartTimeEmployee::class, $demotedEmployee); - $this->assertInstanceOf(Employee::class, $demotedEmployee); - $this->assertInstanceOf(AbstractEmployee::class, $demotedEmployee); - $this->assertSame( - $promotedEmployee->getName(), - $demotedEmployee->getName(), - ); - $this->assertSame( - $promotedEmployee->getSalary() - $salaryDecrease, - $demotedEmployee->getSalary(), - ); + static::assertInstanceOf(PartTimeEmployee::class, $demotedEmployee); + static::assertInstanceOf(Employee::class, $demotedEmployee); + static::assertInstanceOf(AbstractEmployee::class, $demotedEmployee); + static::assertSame($promotedEmployee->getName(), $demotedEmployee->getName()); + static::assertSame($promotedEmployee->getSalary() - $salaryDecrease, $demotedEmployee->getSalary()); } } diff --git a/tests/Functional/AspectMatching/SelfType/Target/Employee.php b/tests/Functional/AspectMatching/SelfType/Target/Employee.php index c208ed0..8d4feb0 100644 --- a/tests/Functional/AspectMatching/SelfType/Target/Employee.php +++ b/tests/Functional/AspectMatching/SelfType/Target/Employee.php @@ -11,6 +11,10 @@ public function __construct( public function promote(AbstractEmployee|int $employee, float $salaryIncrease): self|int { + if (is_int($employee)) { + return $employee; + } + $promotedSalary = $employee->getSalary() + $salaryIncrease; return new self($employee->getName(), $promotedSalary); diff --git a/tests/Functional/AspectMatching/SelfType/Target/PartTimeEmployee.php b/tests/Functional/AspectMatching/SelfType/Target/PartTimeEmployee.php index dfb3eb1..2f75847 100644 --- a/tests/Functional/AspectMatching/SelfType/Target/PartTimeEmployee.php +++ b/tests/Functional/AspectMatching/SelfType/Target/PartTimeEmployee.php @@ -2,6 +2,4 @@ namespace Okapi\Aop\Tests\Functional\AspectMatching\SelfType\Target; -class PartTimeEmployee extends Employee -{ -} +class PartTimeEmployee extends Employee {} diff --git a/tests/Functional/ErrorHandling/InvalidAspect/Aspect/InvalidAspect.php b/tests/Functional/ErrorHandling/InvalidAspect/Aspect/InvalidAspect.php index 705bc82..20b50b0 100644 --- a/tests/Functional/ErrorHandling/InvalidAspect/Aspect/InvalidAspect.php +++ b/tests/Functional/ErrorHandling/InvalidAspect/Aspect/InvalidAspect.php @@ -6,10 +6,7 @@ class InvalidAspect { - #[Before( - class: 'Nice', - method: 'test', - )] + #[Before(class: 'Nice', method: 'test')] public function test(): string { return 'I am invalid!'; diff --git a/tests/Functional/ErrorHandling/InvalidAspect/Kernel/InvalidAspectClassKernel.php b/tests/Functional/ErrorHandling/InvalidAspect/Kernel/InvalidAspectClassKernel.php index 7d3c111..4ea632b 100644 --- a/tests/Functional/ErrorHandling/InvalidAspect/Kernel/InvalidAspectClassKernel.php +++ b/tests/Functional/ErrorHandling/InvalidAspect/Kernel/InvalidAspectClassKernel.php @@ -7,6 +7,7 @@ class InvalidAspectClassKernel extends AopKernel { + /** @var array */ protected array $aspects = [ InvalidAspect::class, ]; diff --git a/tests/Functional/ErrorHandling/InvalidAspect/Kernel/InvalidAspectClassNameKernel.php b/tests/Functional/ErrorHandling/InvalidAspect/Kernel/InvalidAspectClassNameKernel.php index b189cf8..6f98035 100644 --- a/tests/Functional/ErrorHandling/InvalidAspect/Kernel/InvalidAspectClassNameKernel.php +++ b/tests/Functional/ErrorHandling/InvalidAspect/Kernel/InvalidAspectClassNameKernel.php @@ -6,6 +6,7 @@ class InvalidAspectClassNameKernel extends AopKernel { + /** @var array */ protected array $aspects = [ 'InvalidAspect', ]; diff --git a/tests/Functional/ErrorHandling/InvalidAspect/Kernel/InvalidAspectsTypeKernel.php b/tests/Functional/ErrorHandling/InvalidAspect/Kernel/InvalidAspectsTypeKernel.php index 392968d..ecd1012 100644 --- a/tests/Functional/ErrorHandling/InvalidAspect/Kernel/InvalidAspectsTypeKernel.php +++ b/tests/Functional/ErrorHandling/InvalidAspect/Kernel/InvalidAspectsTypeKernel.php @@ -6,6 +6,7 @@ class InvalidAspectsTypeKernel extends AopKernel { + /** @var array */ protected array $aspects = [ 42, ]; diff --git a/tests/Functional/ErrorHandling/MissingClassOrMethod/Aspect/AddItemLoggerAspect.php b/tests/Functional/ErrorHandling/MissingClassOrMethod/Aspect/AddItemLoggerAspect.php index d53d5e4..7b459e3 100644 --- a/tests/Functional/ErrorHandling/MissingClassOrMethod/Aspect/AddItemLoggerAspect.php +++ b/tests/Functional/ErrorHandling/MissingClassOrMethod/Aspect/AddItemLoggerAspect.php @@ -10,19 +10,15 @@ #[Aspect] class AddItemLoggerAspect { - #[After( - method: 'addItem', - )] + #[After(method: 'addItem')] public function logAddItem(AfterMethodInvocation $invocation): void { + /** @var string $itemName */ $itemName = $invocation->getArgument('itemName'); + /** @var int $quantity */ $quantity = $invocation->getArgument('quantity'); - $logMessage = sprintf( - "Item %s added to inventory with quantity %d.", - $itemName, - $quantity, - ); + $logMessage = sprintf('Item %s added to inventory with quantity %d.', $itemName, $quantity); $logger = Logger::getInstance(); $logger->log($logMessage); diff --git a/tests/Functional/ErrorHandling/MissingClassOrMethod/Aspect/GetQuantityLoggerAspect.php b/tests/Functional/ErrorHandling/MissingClassOrMethod/Aspect/GetQuantityLoggerAspect.php index 8c87ba8..c657d1c 100644 --- a/tests/Functional/ErrorHandling/MissingClassOrMethod/Aspect/GetQuantityLoggerAspect.php +++ b/tests/Functional/ErrorHandling/MissingClassOrMethod/Aspect/GetQuantityLoggerAspect.php @@ -1,4 +1,5 @@ getArgument('itemName'); + /** @var int $quantity */ $quantity = $invocation->proceed(); - $logMessage = sprintf( - "Item %s has quantity %d.", - $itemName, - $quantity, - ); + $logMessage = sprintf('Item %s has quantity %d.', $itemName, $quantity); $logger = Logger::getInstance(); $logger->log($logMessage); diff --git a/tests/Functional/ErrorHandling/MissingClassOrMethod/Aspect/RemoveItemLoggerAspect.php b/tests/Functional/ErrorHandling/MissingClassOrMethod/Aspect/RemoveItemLoggerAspect.php index d81664c..4082dc3 100644 --- a/tests/Functional/ErrorHandling/MissingClassOrMethod/Aspect/RemoveItemLoggerAspect.php +++ b/tests/Functional/ErrorHandling/MissingClassOrMethod/Aspect/RemoveItemLoggerAspect.php @@ -1,4 +1,5 @@ getArgument('itemName'); - $logMessage = sprintf( - "Item %s removed from inventory.", - $itemName, - ); + $logMessage = sprintf('Item %s removed from inventory.', $itemName); $logger = Logger::getInstance(); $logger->log($logMessage); diff --git a/tests/Functional/ErrorHandling/MissingClassOrMethod/Kernel/AddItemKernel.php b/tests/Functional/ErrorHandling/MissingClassOrMethod/Kernel/AddItemKernel.php index c53ad28..4a0449e 100644 --- a/tests/Functional/ErrorHandling/MissingClassOrMethod/Kernel/AddItemKernel.php +++ b/tests/Functional/ErrorHandling/MissingClassOrMethod/Kernel/AddItemKernel.php @@ -10,6 +10,7 @@ class AddItemKernel extends AopKernel { protected ?string $cacheDir = Util::CACHE_DIR; + /** @var array */ protected array $aspects = [ AddItemLoggerAspect::class, ]; diff --git a/tests/Functional/ErrorHandling/MissingClassOrMethod/Kernel/GetQuantityKernel.php b/tests/Functional/ErrorHandling/MissingClassOrMethod/Kernel/GetQuantityKernel.php index da271f2..64504d3 100644 --- a/tests/Functional/ErrorHandling/MissingClassOrMethod/Kernel/GetQuantityKernel.php +++ b/tests/Functional/ErrorHandling/MissingClassOrMethod/Kernel/GetQuantityKernel.php @@ -10,6 +10,7 @@ class GetQuantityKernel extends AopKernel { protected ?string $cacheDir = Util::CACHE_DIR; + /** @var array */ protected array $aspects = [ GetQuantityLoggerAspect::class, ]; diff --git a/tests/Functional/ErrorHandling/MissingClassOrMethod/Kernel/RemoveItemKernel.php b/tests/Functional/ErrorHandling/MissingClassOrMethod/Kernel/RemoveItemKernel.php index 5b51ae4..90eaf4a 100644 --- a/tests/Functional/ErrorHandling/MissingClassOrMethod/Kernel/RemoveItemKernel.php +++ b/tests/Functional/ErrorHandling/MissingClassOrMethod/Kernel/RemoveItemKernel.php @@ -10,6 +10,7 @@ class RemoveItemKernel extends AopKernel { protected ?string $cacheDir = Util::CACHE_DIR; + /** @var array */ protected array $aspects = [ RemoveItemLoggerAspect::class, ]; diff --git a/tests/Functional/ErrorHandling/MissingClassOrMethod/MissingClassOrMethodTest.php b/tests/Functional/ErrorHandling/MissingClassOrMethod/MissingClassOrMethodTest.php index cff9a9c..ab734c0 100644 --- a/tests/Functional/ErrorHandling/MissingClassOrMethod/MissingClassOrMethodTest.php +++ b/tests/Functional/ErrorHandling/MissingClassOrMethod/MissingClassOrMethodTest.php @@ -1,4 +1,5 @@ assertInstanceOf( - MissingClassNameException::class, - $error, - ); + static::assertInstanceOf(MissingClassNameException::class, $error); } /** @@ -57,10 +55,7 @@ public function testMissingMethodName(): void $error = $e; } - $this->assertInstanceOf( - MissingMethodNameException::class, - $error, - ); + static::assertInstanceOf(MissingMethodNameException::class, $error); } /** @@ -82,8 +77,6 @@ public function testMissingClassAndMethodName(): void $missingClassNameException = $error instanceof MissingClassNameException; $missingMethodNameException = $error instanceof MissingMethodNameException; - $this->assertTrue( - $missingClassNameException || $missingMethodNameException, - ); + static::assertTrue($missingClassNameException || $missingMethodNameException); } } diff --git a/tests/Functional/ErrorHandling/MissingClassOrMethod/Target/InventoryManager.php b/tests/Functional/ErrorHandling/MissingClassOrMethod/Target/InventoryManager.php index 04ad80a..25f5d14 100644 --- a/tests/Functional/ErrorHandling/MissingClassOrMethod/Target/InventoryManager.php +++ b/tests/Functional/ErrorHandling/MissingClassOrMethod/Target/InventoryManager.php @@ -1,9 +1,11 @@ */ private array $items = []; public function addItem(string $itemName, int $quantity): void diff --git a/tests/Functional/Kernel/CustomDependencyInjectionHandler/Aspect.php b/tests/Functional/Kernel/CustomDependencyInjectionHandler/Aspect.php index ee0aeb5..d05f43e 100644 --- a/tests/Functional/Kernel/CustomDependencyInjectionHandler/Aspect.php +++ b/tests/Functional/Kernel/CustomDependencyInjectionHandler/Aspect.php @@ -8,10 +8,7 @@ #[AspectAttribute] class Aspect { - #[After( - class: Target::class, - method: 'answer', - )] + #[After(class: Target::class, method: 'answer')] public function higherAnswer(): int { return 420; diff --git a/tests/Functional/Kernel/CustomDependencyInjectionHandler/CustomDependencyInjectionHandlerTest.php b/tests/Functional/Kernel/CustomDependencyInjectionHandler/CustomDependencyInjectionHandlerTest.php index 41a3175..fb58fbc 100644 --- a/tests/Functional/Kernel/CustomDependencyInjectionHandler/CustomDependencyInjectionHandlerTest.php +++ b/tests/Functional/Kernel/CustomDependencyInjectionHandler/CustomDependencyInjectionHandlerTest.php @@ -18,17 +18,12 @@ public function testCustomDependencyInjectionHandler(): void Kernel::init(); $output = ob_get_clean(); + static::assertIsString($output); - $this->assertStringContainsString( - 'Generating aspect/transformer instance: ' . Aspect::class, - $output, - ); + static::assertStringContainsString('Generating aspect/transformer instance: ' . Aspect::class, $output); $class = new Target(); - $this->assertSame( - 420, - $class->answer(), - ); + static::assertSame(420, $class->answer()); } } diff --git a/tests/Functional/Kernel/CustomDependencyInjectionHandler/Kernel.php b/tests/Functional/Kernel/CustomDependencyInjectionHandler/Kernel.php index fa76324..a044e10 100644 --- a/tests/Functional/Kernel/CustomDependencyInjectionHandler/Kernel.php +++ b/tests/Functional/Kernel/CustomDependencyInjectionHandler/Kernel.php @@ -12,13 +12,14 @@ class Kernel extends AopKernel protected function dependencyInjectionHandler(): ?Closure { - return function (string $className) { + return /** @param class-string $className */ static function (string $className) { echo 'Generating aspect/transformer instance: ' . $className . PHP_EOL; - return new $className(); + return (new \ReflectionClass($className))->newInstance(); }; } + /** @var array */ protected array $aspects = [ Aspect::class, ]; diff --git a/tests/Integration/TransformerAndAspect/Aspect/FixWrongReturnValueAspect.php b/tests/Integration/TransformerAndAspect/Aspect/FixWrongReturnValueAspect.php index 9e1b861..0c80dc3 100644 --- a/tests/Integration/TransformerAndAspect/Aspect/FixWrongReturnValueAspect.php +++ b/tests/Integration/TransformerAndAspect/Aspect/FixWrongReturnValueAspect.php @@ -1,21 +1,20 @@ proceed(); $invocation->setResult(!$result); } diff --git a/tests/Integration/TransformerAndAspect/Kernel.php b/tests/Integration/TransformerAndAspect/Kernel.php index 3569abe..fe36e83 100644 --- a/tests/Integration/TransformerAndAspect/Kernel.php +++ b/tests/Integration/TransformerAndAspect/Kernel.php @@ -4,17 +4,19 @@ use Okapi\Aop\AopKernel; use Okapi\Aop\Tests\Integration\TransformerAndAspect\Aspect\FixWrongReturnValueAspect; -use Okapi\Aop\Tests\Integration\TransformerAndAspect\Transformer\FixDeprecatedFunctionTransformer; +use Okapi\Aop\Tests\Integration\TransformerAndAspect\Transformer\FixIncorrectFunctionTransformer; use Okapi\Aop\Tests\Util; class Kernel extends AopKernel { protected ?string $cacheDir = Util::CACHE_DIR; + /** @var array> */ protected array $transformers = [ - FixDeprecatedFunctionTransformer::class, + FixIncorrectFunctionTransformer::class, ]; + /** @var array */ protected array $aspects = [ FixWrongReturnValueAspect::class, ]; diff --git a/tests/Integration/TransformerAndAspect/Target/DeprecatedAndWrongClass.php b/tests/Integration/TransformerAndAspect/Target/IncorrectFunctionAndReturnClass.php similarity index 55% rename from tests/Integration/TransformerAndAspect/Target/DeprecatedAndWrongClass.php rename to tests/Integration/TransformerAndAspect/Target/IncorrectFunctionAndReturnClass.php index c705215..fb12791 100644 --- a/tests/Integration/TransformerAndAspect/Target/DeprecatedAndWrongClass.php +++ b/tests/Integration/TransformerAndAspect/Target/IncorrectFunctionAndReturnClass.php @@ -2,11 +2,10 @@ namespace Okapi\Aop\Tests\Integration\TransformerAndAspect\Target; -class DeprecatedAndWrongClass +class IncorrectFunctionAndReturnClass { public function checkIfFloat(mixed $value): bool { - /** @noinspection PhpDeprecationInspection */ - return !is_real($value); + return !is_int($value); } } diff --git a/tests/Integration/TransformerAndAspect/Transformer/FixDeprecatedFunctionTransformer.php b/tests/Integration/TransformerAndAspect/Transformer/FixIncorrectFunctionTransformer.php similarity index 53% rename from tests/Integration/TransformerAndAspect/Transformer/FixDeprecatedFunctionTransformer.php rename to tests/Integration/TransformerAndAspect/Transformer/FixIncorrectFunctionTransformer.php index 32205c9..10d0578 100644 --- a/tests/Integration/TransformerAndAspect/Transformer/FixDeprecatedFunctionTransformer.php +++ b/tests/Integration/TransformerAndAspect/Transformer/FixIncorrectFunctionTransformer.php @@ -3,28 +3,28 @@ namespace Okapi\Aop\Tests\Integration\TransformerAndAspect\Transformer; use Microsoft\PhpParser\Node\QualifiedName; -use Okapi\Aop\Tests\Integration\TransformerAndAspect\Target\DeprecatedAndWrongClass; +use Okapi\Aop\Tests\Integration\TransformerAndAspect\Target\IncorrectFunctionAndReturnClass; use Okapi\CodeTransformer\Transformer; use Okapi\CodeTransformer\Transformer\Code; -class FixDeprecatedFunctionTransformer extends Transformer +class FixIncorrectFunctionTransformer extends Transformer { public function getTargetClass(): string|array { - return DeprecatedAndWrongClass::class; + return IncorrectFunctionAndReturnClass::class; } public function transform(Code $code): void { $sourceFileNode = $code->getSourceFileNode(); + /** @var \Microsoft\PhpParser\Node $node */ foreach ($sourceFileNode->getDescendantNodes() as $node) { - if ($node instanceof QualifiedName && $node->getText() === 'is_real') { - $code->edit( - $node->nameParts[0], - 'is_float', - ); + if (!($node instanceof QualifiedName && $node->getText() === 'is_int')) { + continue; } + + $code->edit($node, 'is_float'); } } } diff --git a/tests/Integration/TransformerAndAspect/TransformerAndAspectTest.php b/tests/Integration/TransformerAndAspect/TransformerAndAspectTest.php index a824941..fdbe129 100644 --- a/tests/Integration/TransformerAndAspect/TransformerAndAspectTest.php +++ b/tests/Integration/TransformerAndAspect/TransformerAndAspectTest.php @@ -4,8 +4,8 @@ use Okapi\Aop\Tests\ClassLoaderMockTrait; use Okapi\Aop\Tests\Integration\TransformerAndAspect\Aspect\FixWrongReturnValueAspect; -use Okapi\Aop\Tests\Integration\TransformerAndAspect\Target\DeprecatedAndWrongClass; -use Okapi\Aop\Tests\Integration\TransformerAndAspect\Transformer\FixDeprecatedFunctionTransformer; +use Okapi\Aop\Tests\Integration\TransformerAndAspect\Target\IncorrectFunctionAndReturnClass; +use Okapi\Aop\Tests\Integration\TransformerAndAspect\Transformer\FixIncorrectFunctionTransformer; use Okapi\Aop\Tests\Util; use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses; use PHPUnit\Framework\TestCase; @@ -16,7 +16,7 @@ class TransformerAndAspectTest extends TestCase use ClassLoaderMockTrait; /** - * @see FixDeprecatedFunctionTransformer + * @see FixIncorrectFunctionTransformer * @see FixWrongReturnValueAspect::fixWrongReturnValue() */ public function testTransformerAndAspect(): void @@ -24,18 +24,18 @@ public function testTransformerAndAspect(): void Util::clearCache(); Kernel::init(); - $this->assertWillBeWoven(DeprecatedAndWrongClass::class); - $class = new DeprecatedAndWrongClass(); - $this->assertTrue($class->checkIfFloat(1.0)); + $this->assertWillBeWoven(IncorrectFunctionAndReturnClass::class); + $class = new IncorrectFunctionAndReturnClass(); + static::assertTrue($class->checkIfFloat(1.0)); } public function testCachedTransformerAndAspect(): void { Kernel::init(); - $this->assertAspectLoadedFromCache(DeprecatedAndWrongClass::class); - $class = new DeprecatedAndWrongClass(); - $this->assertTrue($class->checkIfFloat(42.0)); - $this->assertFalse($class->checkIfFloat("Hello World!")); + $this->assertAspectLoadedFromCache(IncorrectFunctionAndReturnClass::class); + $class = new IncorrectFunctionAndReturnClass(); + static::assertTrue($class->checkIfFloat(42.0)); + static::assertFalse($class->checkIfFloat('Hello World!')); } } diff --git a/tests/Integration/TransformerAndAspectDependencyInjectionHandler/Aspect.php b/tests/Integration/TransformerAndAspectDependencyInjectionHandler/Aspect.php index 133f115..5d1161d 100644 --- a/tests/Integration/TransformerAndAspectDependencyInjectionHandler/Aspect.php +++ b/tests/Integration/TransformerAndAspectDependencyInjectionHandler/Aspect.php @@ -1,4 +1,5 @@ proceed(); return $result + 378; diff --git a/tests/Integration/TransformerAndAspectDependencyInjectionHandler/Kernel.php b/tests/Integration/TransformerAndAspectDependencyInjectionHandler/Kernel.php index b88e764..65c0ec0 100644 --- a/tests/Integration/TransformerAndAspectDependencyInjectionHandler/Kernel.php +++ b/tests/Integration/TransformerAndAspectDependencyInjectionHandler/Kernel.php @@ -13,24 +13,25 @@ class Kernel extends AopKernel protected function dependencyInjectionHandler(): ?Closure { - return function (string $className, ComponentType $type) { + return /** @param class-string $className */ static function (string $className, ComponentType $type) { /** @noinspection PhpIfWithCommonPartsInspection */ if ($type === ComponentType::ASPECT) { echo 'Generating aspect instance: ' . $className . PHP_EOL; - return new $className(); - } else { - echo 'Generating transformer instance: ' . $className . PHP_EOL; - - return new $className(); + return (new \ReflectionClass($className))->newInstance(); } + echo 'Generating transformer instance: ' . $className . PHP_EOL; + + return (new \ReflectionClass($className))->newInstance(); }; } + /** @var array */ protected array $aspects = [ Aspect::class, ]; + /** @var array> */ protected array $transformers = [ Transformer::class, ]; diff --git a/tests/Integration/TransformerAndAspectDependencyInjectionHandler/Transformer.php b/tests/Integration/TransformerAndAspectDependencyInjectionHandler/Transformer.php index 8f72684..2f15b19 100644 --- a/tests/Integration/TransformerAndAspectDependencyInjectionHandler/Transformer.php +++ b/tests/Integration/TransformerAndAspectDependencyInjectionHandler/Transformer.php @@ -20,18 +20,24 @@ public function transform(Code $code): void { $sourceFileNode = $code->getSourceFileNode(); + /** @var \Microsoft\PhpParser\Node $node */ foreach ($sourceFileNode->getDescendantNodes() as $node) { - if ($node instanceof QualifiedNameList - && $node->getFirstAncestor(MethodDeclaration::class)?->getName() === 'answer' + $method = $node->getFirstAncestor(MethodDeclaration::class); + if ( + $node instanceof QualifiedNameList + && $method instanceof MethodDeclaration + && $method->getName() === 'answer' ) { $code->edit($node, 'int|float'); } - if ($node instanceof NumericLiteral - && $node->getFirstAncestor(MethodDeclaration::class)?->getName() === 'answer' + if ( + $node instanceof NumericLiteral + && $method instanceof MethodDeclaration + && $method->getName() === 'answer' ) { $text = $node->getText(); - $code->edit($node, "$text.69"); + $code->edit($node, "{$text}.69"); } } } diff --git a/tests/Integration/TransformerAndAspectDependencyInjectionHandler/TransformerAndAspectDependencyInjectionHandlerTest.php b/tests/Integration/TransformerAndAspectDependencyInjectionHandler/TransformerAndAspectDependencyInjectionHandlerTest.php index 8b960e7..678f57e 100644 --- a/tests/Integration/TransformerAndAspectDependencyInjectionHandler/TransformerAndAspectDependencyInjectionHandlerTest.php +++ b/tests/Integration/TransformerAndAspectDependencyInjectionHandler/TransformerAndAspectDependencyInjectionHandlerTest.php @@ -25,22 +25,14 @@ public function testTransformerAndAspectDependencyInjectionHandler(): void Kernel::init(); $output = ob_get_clean(); + static::assertIsString($output); - $this->assertStringContainsString( - 'Generating aspect instance: ' . Aspect::class, - $output, - ); - $this->assertStringContainsString( - 'Generating transformer instance: ' . Transformer::class, - $output, - ); + static::assertStringContainsString('Generating aspect instance: ' . Aspect::class, $output); + static::assertStringContainsString('Generating transformer instance: ' . Transformer::class, $output); $this->assertWillBeWoven(Target::class); $class = new Target(); - $this->assertSame( - 420.69, - $class->answer(), - ); + static::assertSame(420.69, $class->answer()); } } diff --git a/tests/Performance/Aspect/AddOneAspect.php b/tests/Performance/Aspect/AddOneAspect.php index fee703f..7d2bdaf 100644 --- a/tests/Performance/Aspect/AddOneAspect.php +++ b/tests/Performance/Aspect/AddOneAspect.php @@ -1,4 +1,5 @@ proceed(); $invocation->setResult($result + 1); diff --git a/tests/Performance/Kernel/MeasurePerformanceKernel.php b/tests/Performance/Kernel/MeasurePerformanceKernel.php index 7dff232..b50d625 100644 --- a/tests/Performance/Kernel/MeasurePerformanceKernel.php +++ b/tests/Performance/Kernel/MeasurePerformanceKernel.php @@ -13,6 +13,7 @@ class MeasurePerformanceKernel extends AopKernel protected Environment $environment = Environment::DEVELOPMENT; /** @noinspection PhpFullyQualifiedNameUsageInspection */ + /** @var array */ protected array $aspects = [ \Okapi\Aop\Tests\Performance\Aspect\AddOneAspect::class, ]; diff --git a/tests/Performance/MeasurePerformanceTest.php b/tests/Performance/MeasurePerformanceTest.php index 9f93e0a..5bde915 100644 --- a/tests/Performance/MeasurePerformanceTest.php +++ b/tests/Performance/MeasurePerformanceTest.php @@ -3,17 +3,21 @@ namespace Okapi\Aop\Tests\Performance; use Exception; -use Okapi\Aop\Tests\Performance\Kernel\MeasurePerformanceKernel; -use Okapi\Aop\Tests\Performance\Service\NumbersService; +use Okapi\Aop\AopKernel; +use Okapi\Aop\Tests\Performance\Service\NumbersServiceInterface; use Okapi\Aop\Tests\Performance\Target\Numbers; use Okapi\Aop\Tests\Util; use Okapi\Filesystem\Filesystem; -use PHPUnit\Framework\Attributes\{DataProvider, RunTestsInSeparateProcesses, Test}; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses; +use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; +use ReflectionClass; +use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Helper\TableStyle; use Symfony\Component\Console\Input\ArgvInput; -use Symfony\Component\Console\Style\SymfonyStyle; -use Symfony\Component\Console\Helper\{Table, TableStyle}; use Symfony\Component\Console\Output\ConsoleOutput; +use Symfony\Component\Console\Style\SymfonyStyle; #[RunTestsInSeparateProcesses] class MeasurePerformanceTest extends TestCase @@ -22,52 +26,55 @@ class MeasurePerformanceTest extends TestCase private bool $cached; private bool $production; - private CONST MEASURE_TYPE_NO_ASPECTS = 'No Aspects'; - private CONST MEASURE_TYPE_ASPECTS = 'Aspects'; - private CONST MEASURE_TYPE_CACHED_ASPECTS = 'Cached Aspects'; - private CONST MEASURE_TYPE_PRODUCTION = 'Production'; + private const MEASURE_TYPE_NO_ASPECTS = 'No Aspects'; + private const MEASURE_TYPE_ASPECTS = 'Aspects'; + private const MEASURE_TYPE_CACHED_ASPECTS = 'Cached Aspects'; + private const MEASURE_TYPE_PRODUCTION = 'Production'; + /** @var array>> */ private array $measures = [ - self::MEASURE_TYPE_NO_ASPECTS => [], - self::MEASURE_TYPE_ASPECTS => [], + self::MEASURE_TYPE_NO_ASPECTS => [], + self::MEASURE_TYPE_ASPECTS => [], self::MEASURE_TYPE_CACHED_ASPECTS => [], - self::MEASURE_TYPE_PRODUCTION => [], + self::MEASURE_TYPE_PRODUCTION => [], ]; private const MEASURE_TYPE_FROM_START_TO_END = 'From Start to End'; - private const MEASURE_TYPE_BOOT = 'Boot Time - Kernel::init()'; - private const MEASURE_TYPE_CLASS_LOADING = 'Class Loading Time'; - private const MEASURE_TYPE_EXECUTION = 'Execution Time'; + private const MEASURE_TYPE_BOOT = 'Boot Time - Kernel::init()'; + private const MEASURE_TYPE_CLASS_LOADING = 'Class Loading Time'; + private const MEASURE_TYPE_EXECUTION = 'Execution Time'; - private const START_TIME = 'Start Time'; - private const END_TIME = 'End Time'; + private const START_TIME = 'Start Time'; + private const END_TIME = 'End Time'; private const START_MEMORY = 'Start Memory'; - private const END_MEMORY = 'End Memory'; + private const END_MEMORY = 'End Memory'; - private const METRIC_TYPE_TIME = 'Time'; + private const METRIC_TYPE_TIME = 'Time'; private const METRIC_TYPE_MEMORY = 'Memory'; + /** @var non-empty-list */ public static array $aspectCountAndExecutionCount = [ - [1, 1], // Minimal - [5, 5], // Small - [20, 20], // Moderate - [50, 50], // Medium - [100, 100], // Common - [500, 100], // Common: Aspects++ - [100, 500], // Common: Executions++ - [500, 500], // High - [1000, 500], // High: Aspects++ - [500, 1000], // High: Executions++ + [1, 1], // Minimal + [5, 5], // Small + [20, 20], // Moderate + [50, 50], // Medium + [100, 100], // Common + [500, 100], // Common: Aspects++ + [100, 500], // Common: Executions++ + [500, 500], // High + [1000, 500], // High: Aspects++ + [500, 1000], // High: Executions++ [1000, 1000], // Very High ]; + /** @return array */ public static function dataProvider(): array { $flags = [ - self::MEASURE_TYPE_NO_ASPECTS => [], - self::MEASURE_TYPE_ASPECTS => ['useAspects' => true], + self::MEASURE_TYPE_NO_ASPECTS => [], + self::MEASURE_TYPE_ASPECTS => ['useAspects' => true], self::MEASURE_TYPE_CACHED_ASPECTS => ['useAspects' => true, 'cached' => true], - self::MEASURE_TYPE_PRODUCTION => ['useAspects' => true, 'cached' => true, 'production' => true], + self::MEASURE_TYPE_PRODUCTION => ['useAspects' => true, 'cached' => true, 'production' => true], ]; $data = []; @@ -79,30 +86,30 @@ public static function dataProvider(): array $aspectsLabel = $aspectCount === 1 ? 'aspect' : 'aspects'; $executionLabel = $executionCount === 1 ? 'execution' : 'executions'; - $dataProviderLabel = "$measureType: $aspectCount $aspectsLabel, $executionCount $executionLabel"; + $dataProviderLabel = "{$measureType}: {$aspectCount} {$aspectsLabel}, {$executionCount} {$executionLabel}"; $data[$dataProviderLabel] = [ - 'aspectCount' => $aspectCount, + 'aspectCount' => $aspectCount, 'executionCount' => $executionCount, - 'useAspects' => $flag['useAspects'] ?? false, - 'cached' => $flag['cached'] ?? false, - 'production' => $flag['production'] ?? false, + 'useAspects' => $flag['useAspects'] ?? false, + 'cached' => $flag['cached'] ?? false, + 'production' => $flag['production'] ?? false, ]; } } // Cleanup data $data['Cleanup'] = [ - 'aspectCount' => 0, + 'aspectCount' => 0, 'executionCount' => 0, ]; // Number of tests should equal the number of generated data sets $dataCount = count($data); - $expectedDataCount = count(self::$aspectCountAndExecutionCount) * count($flags) + 1; + $expectedDataCount = (count(self::$aspectCountAndExecutionCount) * count($flags)) + 1; if ($dataCount !== $expectedDataCount) { /** @noinspection PhpUnhandledExceptionInspection */ - throw new Exception("Expected $expectedDataCount data sets, got $dataCount"); + throw new Exception("Expected {$expectedDataCount} data sets, got {$dataCount}"); } if (extension_loaded('xdebug')) { @@ -129,15 +136,14 @@ public function measurePerformance( $noFlags = !$useAspects && !$cached && !$production; $lastMeasure = $useAspects && $cached && $production; - $firstRun = $aspectCount === self::$aspectCountAndExecutionCount[0][0] + $firstRun = + $aspectCount === self::$aspectCountAndExecutionCount[0][0] && $executionCount === self::$aspectCountAndExecutionCount[0][1] && $noFlags; - $shouldCleanCache = $firstRun || ($useAspects && !$cached); + $shouldCleanCache = $firstRun || $useAspects && !$cached; - $lastRun = $aspectCount === 0 - && $executionCount === 0 - && $noFlags; + $lastRun = $aspectCount === 0 && $executionCount === 0 && $noFlags; if ($firstRun) { $this->cleanup(); @@ -153,38 +159,43 @@ public function measurePerformance( return; } - /** @var class-string[] $services */ + /** @var class-string[] $services */ $services = []; if ($useAspects) { // Create $aspectCount aspects and a kernel that uses them $kernel = $this->createKernelAndAspects($aspectCount, $production); - } else { + } + if (!$useAspects) { // Emulate aspects by creating $aspectCount services $services = $this->createServices($aspectCount); } $this->useAspects = $useAspects; - $this->cached = $cached; + $this->cached = $cached; $this->production = $production; $this->startMeasure(self::MEASURE_TYPE_FROM_START_TO_END); $this->startMeasure(self::MEASURE_TYPE_BOOT); if ($useAspects) { - /** @var MeasurePerformanceKernel $kernel */ + /** @var class-string $kernel */ $kernel::init(); } - $this-> endMeasure(self::MEASURE_TYPE_BOOT); + $this->endMeasure(self::MEASURE_TYPE_BOOT); $this->startMeasure(self::MEASURE_TYPE_CLASS_LOADING); $numbersClass = new Numbers(); - /** @var NumbersService[] $serviceInstances */ + /** @var NumbersServiceInterface[] $serviceInstances */ $serviceInstances = []; if (!$useAspects) { foreach ($services as $service) { - $serviceInstances[] = new $service(); + $serviceClass = new ReflectionClass($service); + if (!$serviceClass->isInstantiable()) { + throw new Exception('Expected an instantiable benchmark service'); + } + $serviceInstances[] = $serviceClass->newInstance(); } } @@ -205,7 +216,8 @@ public function measurePerformance( // $expectedResults[] = $aspectCount; // $actualResults[] = $result; } - } else { + } + if (!$useAspects) { foreach (range(1, $aspectCount) as $i) { $numbersService = $serviceInstances[$i - 1]; @@ -231,50 +243,46 @@ public function measurePerformance( $this->saveMeasuresToFile(); if ($lastMeasure) { - $this->printMeasures($this->dataName()); + $this->printMeasures((string) $this->dataName()); } $this->assertTrue(true); } /** - * @return class-string + * @return class-string */ - private function createKernelAndAspects( - int $aspectCount, - bool $production - ): string { + private function createKernelAndAspects(int $aspectCount, bool $production): string + { $tempDirectory = __DIR__ . '/Temp'; if (!file_exists($tempDirectory)) { Filesystem::mkdir($tempDirectory); } - $newKernelFilePath = "$tempDirectory/MeasurePerformanceKernel$aspectCount.php"; - $newKernelFileNamespace = "\\Okapi\\Aop\\Tests\\Performance\\Temp\\MeasurePerformanceKernel$aspectCount"; + $newKernelFilePath = "{$tempDirectory}/MeasurePerformanceKernel{$aspectCount}.php"; + /** @var class-string $newKernelFileNamespace */ + $newKernelFileNamespace = "\\Okapi\\Aop\\Tests\\Performance\\Temp\\MeasurePerformanceKernel{$aspectCount}"; - static $kernelFile; + /** @var string|null $kernelFile */ + static $kernelFile = null; if (!$kernelFile) { $kernelFile = Filesystem::readFile(__DIR__ . '/Kernel/MeasurePerformanceKernel.php'); } $originalAspectLineNumber = 0; - $originalAspectLine = ''; - $lines = explode("\n", $kernelFile); + $originalAspectLine = ''; + $lines = explode("\n", $kernelFile); foreach ($lines as $lineNumber => &$line) { // Replace namespace if (str_contains($line, 'namespace Okapi\\Aop\\Tests\\Performance\\Kernel')) { - $line = str_replace( - search: 'Kernel', - replace: 'Temp', - subject: $line, - ); + $line = str_replace(search: 'Kernel', replace: 'Temp', subject: $line); } // Replace class name if (str_contains($line, 'class MeasurePerformanceKernel')) { $line = str_replace( search: 'MeasurePerformanceKernel', - replace: "MeasurePerformanceKernel$aspectCount", + replace: "MeasurePerformanceKernel{$aspectCount}", subject: $line, ); } @@ -282,7 +290,7 @@ private function createKernelAndAspects( // Find the line where the "AddOneAspect" is added to the kernel if (str_contains($line, 'AddOneAspect::class,')) { $originalAspectLineNumber = $lineNumber; - $originalAspectLine = $line; + $originalAspectLine = $line; break; } @@ -300,17 +308,18 @@ private function createKernelAndAspects( foreach (range(1, $aspectCount) as $aspectNumber) { $aspects[] = str_replace( search: 'Aspect\\AddOneAspect::class,', - replace: "Temp\\AddOneAspect$aspectNumber::class,", + replace: "Temp\\AddOneAspect{$aspectNumber}::class,", subject: $originalAspectLine, ); // Read aspect file - static $aspectFile; + /** @var string|null $aspectFile */ + static $aspectFile = null; if (!$aspectFile) { $aspectFile = Filesystem::readFile(__DIR__ . '/Aspect/AddOneAspect.php'); } - $newAspectFilePath = __DIR__ . "/Temp/AddOneAspect$aspectNumber.php"; + $newAspectFilePath = __DIR__ . "/Temp/AddOneAspect{$aspectNumber}.php"; if (file_exists($newAspectFilePath)) { continue; } @@ -327,15 +336,12 @@ private function createKernelAndAspects( // Replace class name $newAspectFile = str_replace( search: 'class AddOneAspect', - replace: "class AddOneAspect$aspectNumber", + replace: "class AddOneAspect{$aspectNumber}", subject: $newAspectFile, ); // Write aspect file - Filesystem::writeFile( - $newAspectFilePath, - $newAspectFile, - ); + Filesystem::writeFile($newAspectFilePath, $newAspectFile); $this->cacheFile($newAspectFilePath); } @@ -347,10 +353,7 @@ private function createKernelAndAspects( $kernelFile = implode("\n", $lines); // Write kernel file - Filesystem::writeFile( - $newKernelFilePath, - $kernelFile, - ); + Filesystem::writeFile($newKernelFilePath, $kernelFile); $this->cacheFile($newKernelFilePath); @@ -360,7 +363,7 @@ private function createKernelAndAspects( } /** - * @return class-string[] + * @return class-string[] */ private function createServices(int $serviceCount): array { @@ -372,15 +375,17 @@ private function createServices(int $serviceCount): array $services = []; foreach (range(1, $serviceCount) as $serviceNumber) { // Read service file - static $serviceFile; + /** @var string|null $serviceFile */ + static $serviceFile = null; if (!$serviceFile) { $serviceFile = Filesystem::readFile(__DIR__ . '/Service/NumbersService.php'); } - $serviceNamespace = "\\Okapi\\Aop\\Tests\\Performance\\Temp\\NumbersService$serviceNumber"; + /** @var class-string $serviceNamespace */ + $serviceNamespace = "\\Okapi\\Aop\\Tests\\Performance\\Temp\\NumbersService{$serviceNumber}"; $services[] = $serviceNamespace; - $newServiceFilePath = __DIR__ . "/Temp/NumbersService$serviceNumber.php"; + $newServiceFilePath = __DIR__ . "/Temp/NumbersService{$serviceNumber}.php"; if (file_exists($newServiceFilePath)) { continue; } @@ -397,15 +402,12 @@ private function createServices(int $serviceCount): array // Replace class name $newServiceFile = str_replace( search: 'class NumbersService', - replace: "class NumbersService$serviceNumber", + replace: "class NumbersService{$serviceNumber}", subject: $newServiceFile, ); // Write service file - Filesystem::writeFile( - $newServiceFilePath, - $newServiceFile, - ); + Filesystem::writeFile($newServiceFilePath, $newServiceFile); $this->cacheFile($newServiceFilePath); } @@ -425,12 +427,12 @@ private function cacheFile(string $filename): void private function dumpAutoload(): void { $workingDir = __DIR__ . '/../..'; - $workingDir = (DIRECTORY_SEPARATOR === '\\') + $workingDir = DIRECTORY_SEPARATOR === '\\' ? str_replace('/', '\\', $workingDir) : str_replace('\\', '/', $workingDir); ob_start(); - shell_exec("composer dump-autoload -d $workingDir -o -q"); + shell_exec("composer dump-autoload -d {$workingDir} -o -q"); ob_end_clean(); } @@ -456,20 +458,50 @@ private function saveMeasuresToFile(): void { $tempDirectory = __DIR__ . '/Temp'; - $measuresFile = "$tempDirectory/measures.json"; - if (!file_exists($measuresFile)) { - $measures = $this->measures; - } else { - $measures = json_decode(Filesystem::readFile($measuresFile), true); + $measuresFile = "{$tempDirectory}/measures.json"; + $measures = $this->measures; + if (file_exists($measuresFile)) { + $measures = $this->readMeasures($measuresFile); $measures[$this->getMeasureType()] = $this->measures[$this->getMeasureType()]; } // Save it every execution, because #[RunTestsInSeparateProcesses] // will not store $this->measures between executions - Filesystem::writeFile( - $measuresFile, - json_encode($measures, JSON_PRETTY_PRINT) - ); + Filesystem::writeFile($measuresFile, json_encode($measures, JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR)); + } + + /** @return array>> */ + private function readMeasures(string $path): array + { + /** @var mixed $decoded */ + $decoded = json_decode(Filesystem::readFile($path), true, flags: JSON_THROW_ON_ERROR); + if (!is_array($decoded)) { + throw new Exception('Expected measurement groups in ' . $path); + } + + $measures = []; + /** @var mixed $group */ + foreach ($decoded as $type => $group) { + if (!is_string($type) || !is_array($group)) { + throw new Exception('Invalid measurement group in ' . $path); + } + $measures[$type] = []; + /** @var mixed $metrics */ + foreach ($group as $name => $metrics) { + if (!is_string($name) || !is_array($metrics)) { + throw new Exception('Invalid measurement in ' . $path); + } + $measures[$type][$name] = []; + /** @var mixed $value */ + foreach ($metrics as $metric => $value) { + if (!is_string($metric) || !is_int($value) && !is_float($value)) { + throw new Exception('Invalid measurement metric in ' . $path); + } + $measures[$type][$name][$metric] = $value; + } + } + } + return $measures; } private function getMeasureType(): string @@ -493,14 +525,11 @@ private function getMeasureType(): string private function printMeasures(string $dataProviderLabel): void { - $this->measures = json_decode( - json: Filesystem::readFile(__DIR__ . '/Temp/measures.json'), - associative: true, - ); + $this->measures = $this->readMeasures(__DIR__ . '/Temp/measures.json'); // Remove the last measure, because it's the cleanup $dataProviderLabel = str_replace( - search: array_key_last($this->measures) . ': ', + search: (array_key_last($this->measures) ?? '') . ': ', replace: '', subject: $dataProviderLabel, ); @@ -514,22 +543,22 @@ private function printMeasures(string $dataProviderLabel): void $io->section($dataProviderLabel); // First Table - $output->writeln("Table 1: Without Aspects vs With Aspects ($dataProviderLabel)"); + $output->writeln("Table 1: Without Aspects vs With Aspects ({$dataProviderLabel})"); $this->printTable($output, self::MEASURE_TYPE_ASPECTS); // Second Table $output->writeln(''); - $output->writeln("Table 2: Without Aspects vs With Cached Aspects ($dataProviderLabel)"); + $output->writeln("Table 2: Without Aspects vs With Cached Aspects ({$dataProviderLabel})"); $this->printTable($output, self::MEASURE_TYPE_CACHED_ASPECTS); // Third Table $output->writeln(''); - $output->writeln("Table 3: Without Aspects vs Production ($dataProviderLabel)"); + $output->writeln("Table 3: Without Aspects vs Production ({$dataProviderLabel})"); $this->printTable($output, self::MEASURE_TYPE_PRODUCTION); // Fourth Table $output->writeln(''); - $output->writeln("Table 4: With Cached Aspects vs Production ($dataProviderLabel)"); + $output->writeln("Table 4: With Cached Aspects vs Production ({$dataProviderLabel})"); $this->printTable($output, self::MEASURE_TYPE_PRODUCTION, self::MEASURE_TYPE_CACHED_ASPECTS); $output->writeln(''); @@ -538,7 +567,7 @@ private function printMeasures(string $dataProviderLabel): void private function printTable( ConsoleOutput $output, string $comparisonAspect, - string $compareToType = self::MEASURE_TYPE_NO_ASPECTS + string $compareToType = self::MEASURE_TYPE_NO_ASPECTS, ): void { $table = new Table($output); @@ -580,33 +609,32 @@ private function printTable( $table->render(); } + /** @return list */ private function generateRowData( string $measureType, string $comparisonAspect, string $compareToType, - string $metricType + string $metricType, ): array { // Get start and end metrics - $startMetric = $metricType === self::METRIC_TYPE_TIME - ? self::START_TIME - : self::START_MEMORY; + $startMetric = $metricType === self::METRIC_TYPE_TIME ? self::START_TIME : self::START_MEMORY; - $endMetric = $metricType === self::METRIC_TYPE_TIME - ? self::END_TIME - : self::END_MEMORY; + $endMetric = $metricType === self::METRIC_TYPE_TIME ? self::END_TIME : self::END_MEMORY; // Calculate without aspects - $withoutAspectsValue = $this->measures[$compareToType][$measureType][$endMetric] + $withoutAspectsValue = + $this->measures[$compareToType][$measureType][$endMetric] - $this->measures[$compareToType][$measureType][$startMetric]; // Calculate with aspects - $comparisonValue = $this->measures[$comparisonAspect][$measureType][$endMetric] + $comparisonValue = + $this->measures[$comparisonAspect][$measureType][$endMetric] - $this->measures[$comparisonAspect][$measureType][$startMetric]; // For memory, convert to MB if ($metricType === self::METRIC_TYPE_MEMORY) { - $withoutAspectsValue /= (1024 * 1024); - $comparisonValue /= (1024 * 1024); + $withoutAspectsValue /= 1024 * 1024; + $comparisonValue /= 1024 * 1024; } // Calculate difference @@ -622,22 +650,19 @@ private function generateRowData( // Prefix difference with + or - $prefix = $difference > 0 ? '+' : ''; - $differenceText = "$prefix$difference"; + $differenceText = "{$prefix}{$difference}"; // Append unit - if ($metricType === self::METRIC_TYPE_TIME) { - $append = ' s'; - } else { - $append = ' MB'; - } + $append = $metricType === self::METRIC_TYPE_TIME ? ' s' : ' MB'; $withoutAspectsValue .= $append; $comparisonValue .= $append; $differenceText .= $append; if ($difference > 0) { - $differenceText = "$differenceText"; - } elseif ($difference < 0) { - $differenceText = "$differenceText"; + $differenceText = "{$differenceText}"; + } + if ($difference < 0) { + $differenceText = "{$differenceText}"; } return [ diff --git a/tests/Performance/Service/NumbersService.php b/tests/Performance/Service/NumbersService.php index 560a130..c8dc967 100644 --- a/tests/Performance/Service/NumbersService.php +++ b/tests/Performance/Service/NumbersService.php @@ -4,7 +4,7 @@ use Okapi\Aop\Tests\Performance\Target\Numbers; -class NumbersService +class NumbersService implements \Okapi\Aop\Tests\Performance\Service\NumbersServiceInterface { public function addToNumbers(int $number, Numbers $numbers): void { diff --git a/tests/Performance/Service/NumbersServiceInterface.php b/tests/Performance/Service/NumbersServiceInterface.php new file mode 100644 index 0000000..7de1725 --- /dev/null +++ b/tests/Performance/Service/NumbersServiceInterface.php @@ -0,0 +1,10 @@ + */ private array $log = []; public function log(string $message): void @@ -15,6 +16,7 @@ public function log(string $message): void $this->log[] = $message; } + /** @return list */ public function getLogs(): array { return $this->log; diff --git a/tests/Stubs/Etc/MailQueue.php b/tests/Stubs/Etc/MailQueue.php index f5fab04..3deece0 100644 --- a/tests/Stubs/Etc/MailQueue.php +++ b/tests/Stubs/Etc/MailQueue.php @@ -8,6 +8,7 @@ class MailQueue { use Singleton; + /** @var list */ private array $mails = []; public function addMail(string $mail): void @@ -15,6 +16,7 @@ public function addMail(string $mail): void $this->mails[] = $mail; } + /** @return list */ public function getMails(): array { return $this->mails; diff --git a/tests/Stubs/Etc/StackTrace.php b/tests/Stubs/Etc/StackTrace.php index aaa3ba3..6dfe61c 100644 --- a/tests/Stubs/Etc/StackTrace.php +++ b/tests/Stubs/Etc/StackTrace.php @@ -8,6 +8,7 @@ class StackTrace { use Singleton; + /** @var list */ private array $stackTrace = []; public function addTrace(string $trace): void @@ -15,6 +16,7 @@ public function addTrace(string $trace): void $this->stackTrace[] = $trace; } + /** @return list */ public function getStackTrace(): array { return $this->stackTrace; diff --git a/tests/Stubs/Kernel/EmptyKernel.php b/tests/Stubs/Kernel/EmptyKernel.php index 5f2356c..e4621df 100644 --- a/tests/Stubs/Kernel/EmptyKernel.php +++ b/tests/Stubs/Kernel/EmptyKernel.php @@ -9,5 +9,6 @@ class EmptyKernel extends AopKernel { protected ?string $cacheDir = Util::CACHE_DIR; + /** @var array */ protected array $aspects = []; } diff --git a/tests/Util.php b/tests/Util.php index 870ce3e..b2fb646 100644 --- a/tests/Util.php +++ b/tests/Util.php @@ -1,21 +1,17 @@ - $paths + * @return list}> + */ +function mago_type_issues(array $paths): array +{ + $stdout = mago_temp_file(); + $stderr = mago_temp_file(); + try { + $pipes = []; + $process = proc_open( + [ + PHP_BINARY, + 'vendor/bin/mago', + '--config', + 'tools/mago/tests.toml', + 'analyze', + '--reporting-format', + 'json', + '--minimum-fail-level', + 'note', + ...$paths, + ], + [0 => ['pipe', 'r'], 1 => ['file', $stdout, 'w'], 2 => ['file', $stderr, 'w']], + $pipes, + dirname(__DIR__, 2), + ); + if (!is_resource($process)) { + throw new RuntimeException('Cannot start Mago.'); + } + if (array_key_exists(0, $pipes)) { + fclose($pipes[0]); + } + $status = proc_close($process); + $errors = trim(mago_read_file($stderr)); + try { + /** @var array{issues: list}>} $report Mago JSON output schema. */ + $report = json_decode(mago_read_file($stdout), true, flags: JSON_THROW_ON_ERROR); + } catch (JsonException $exception) { + throw new RuntimeException('Invalid Mago output: ' . $errors, previous: $exception); + } + if ($status !== ($report['issues'] === [] ? 0 : 1)) { + throw new RuntimeException('Unexpected Mago exit status: ' . $status . ' ' . $errors); + } + return $report['issues']; + } finally { + unlink($stdout); + unlink($stderr); + } +} + +if (mago_type_issues(['tools/mago/tests/valid']) !== []) { + throw new RuntimeException('Valid dependency injection callbacks must pass analysis.'); +} + +$issues = mago_type_issues(['tools/mago/tests/valid', 'tools/mago/tests/invalid']); +$actual = []; +foreach ($issues as $issue) { + foreach ($issue['annotations'] as $annotation) { + if ($annotation['kind'] !== 'Primary') { + continue; + } + $actual[basename($annotation['span']['file_id']['name'])][] = $issue['code']; + break; + } +} +$expected = [ + 'ScalarCallback.php' => ['invalid-return-statement'], + 'WrongBaseCallback.php' => ['invalid-return-statement'], + 'WrongManager.php' => ['invalid-argument'], + 'WrongReturn.php' => ['invalid-return-statement'], +]; +ksort($actual); +ksort($expected); +if ($actual !== $expected || count($issues) !== 4) { + throw new RuntimeException( + 'Invalid callbacks must produce all four expected diagnostics: ' . json_encode($actual, JSON_THROW_ON_ERROR), + ); +} +echo 'Mago type patch: valid callbacks accepted; all four invalid callbacks rejected.' . PHP_EOL; diff --git a/tools/mago/patches/CodeTransformerKernel.php b/tools/mago/patches/CodeTransformerKernel.php new file mode 100644 index 0000000..773fa38 --- /dev/null +++ b/tools/mago/patches/CodeTransformerKernel.php @@ -0,0 +1,20 @@ +): Transformer + */ +abstract class CodeTransformerKernel +{ + /** @return THandler|null */ + protected function dependencyInjectionHandler(): ?Closure + { + return null; + } +} diff --git a/tools/mago/tests.toml b/tools/mago/tests.toml new file mode 100644 index 0000000..3160a64 --- /dev/null +++ b/tools/mago/tests.toml @@ -0,0 +1,9 @@ +php-version = "8.1" + +[source] +paths = ["tools/mago/tests/valid"] +includes = ["src", "vendor"] +patches = ["tools/mago/patches"] + +[analyzer] +check-missing-type-hints = true diff --git a/tools/mago/tests/invalid/ScalarCallback.php b/tools/mago/tests/invalid/ScalarCallback.php new file mode 100644 index 0000000..c2a4a1d --- /dev/null +++ b/tools/mago/tests/invalid/ScalarCallback.php @@ -0,0 +1,14 @@ +registerCustomDependencyInjectionHandler( + /** @param class-string $name */ static fn(string $name): string => 'invalid', + ); +} diff --git a/tools/mago/tests/invalid/WrongReturn.php b/tools/mago/tests/invalid/WrongReturn.php new file mode 100644 index 0000000..ce9d2d7 --- /dev/null +++ b/tools/mago/tests/invalid/WrongReturn.php @@ -0,0 +1,17 @@ + $type + === ComponentType::ASPECT + ? new ExampleAspect() + : new ExampleTransformer(); + } +} diff --git a/tools/mago/tests/valid/ValidManager.php b/tools/mago/tests/valid/ValidManager.php new file mode 100644 index 0000000..8802c2e --- /dev/null +++ b/tools/mago/tests/valid/ValidManager.php @@ -0,0 +1,14 @@ +registerCustomDependencyInjectionHandler( + /** @param class-string $name */ static fn(string $name): Transformer => new ExampleTransformer(), + ); +}