diff --git a/src/Attr.php b/src/Attr.php index c4e62ed..bc8ec80 100644 --- a/src/Attr.php +++ b/src/Attr.php @@ -68,7 +68,7 @@ public function __construct(mixed $target) } } else { throw new InvalidArgumentException( - 'The target must be a class, object, callable, or class-property array.' + 'The target must be a class, object, callable, or class-property array.', ); } } @@ -102,7 +102,7 @@ public function all(?string $attribute = null): Collection } /** - * Retrieves the first instanced attribute value from a class, method, or property. + * Retrieves the first instanced attribute value from a target. * * @template TAttribute of object * @@ -114,6 +114,62 @@ public function first(?string $attribute = null): ?object return $this->collect($attribute)->first()?->newInstance(); } + /** + * Retrieves all the arguments declared for the first given attribute. + * + * @param class-string $attribute + * @return scalar[]|null + */ + public function arguments(string $attribute): ?array + { + return $this->collect($attribute)->first()?->getArguments(); + } + + /** + * Retrieves a Collection of all the arguments for the all declarations of the given attribute. + * + * @param class-string $attribute + * @return array|null + */ + public function allArguments(string $attribute): ?array + { + return $this->collect($attribute)->map(static function (ReflectionAttribute $attribute): array { + return $attribute->getArguments(); + })->toArray(); + } + + /** + * Check if the target has no attributes set. + */ + public function isEmpty(): bool + { + return $this->collect(null)->isEmpty(); + } + + /** + * Check if the target has any attribute set. + */ + public function isNotEmpty(): bool + { + return ! $this->isEmpty(); + } + + /** + * Check if the target has the given attribute. + */ + public function has(string $attribute): bool + { + return $this->collect($attribute)->isNotEmpty(); + } + + /** + * Check if the target does not have the given attribute. + */ + public function missing(string $attribute): bool + { + return ! $this->has($attribute); + } + /** * Executes a method from the first instanced attribute. * diff --git a/tests/AttrTest.php b/tests/AttrTest.php index cbbc67e..81c1ecd 100644 --- a/tests/AttrTest.php +++ b/tests/AttrTest.php @@ -6,9 +6,17 @@ use Error; use InvalidArgumentException; use Laragear\Meta\Attr; +use ReflectionClass; class AttrTest extends TestCase { + public function test_resolves_from_reflection(): void + { + $attr = Attr::of(new ReflectionClass(StubClass::class)); + + static::assertNotEmpty($attr); + } + public function test_resolves_from_class_string(): void { $attr = Attr::of(StubClass::class); @@ -102,6 +110,52 @@ public function test_throws_error_if_attribute_class_does_not_exist(): void $attr->first('NonExistentAttribute'); } + public function test_retrieves_arguments(): void + { + $attr = Attr::of(StubClass::class); + + static::assertSame(['class'], $attr->arguments(TestAttribute::class)); + } + + public function test_retrieves_all_arguments(): void + { + $attr = Attr::of(StubClass::class); + + static::assertSame([['class']], $attr->allArguments(TestAttribute::class)); + } + + public function test_emptiness(): void + { + $attr = Attr::of(StubClass::class); + + static::assertFalse($attr->isEmpty()); + static::assertTrue($attr->isNotEmpty()); + } + + public function test_test_not_emptiness(): void + { + $attr = Attr::of(StubClassWithoutAttributes::class); + + static::assertTrue($attr->isEmpty()); + static::assertFalse($attr->isNotEmpty()); + } + + public function test_has_given_attribute(): void + { + $attr = Attr::of(StubClass::class); + + static::assertTrue($attr->has(TestAttribute::class)); + static::assertFalse($attr->has(StubClass::class)); + } + + public function test_missing_given_attribute(): void + { + $attr = Attr::of(StubClass::class); + + static::assertFalse($attr->missing(TestAttribute::class)); + static::assertTrue($attr->missing(StubClass::class)); + } + public function test_get_retrieves_property_from_attribute(): void { $attr = Attr::of(StubClass::class); @@ -147,6 +201,19 @@ public function getValue(): string } } +#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] +class TestRepeatableAttribute +{ + public function __construct(public string $value = 'default', public ?string $second = null) + { + } + + public function getValue(): string + { + return $this->value; + } +} + #[TestAttribute('class')] class StubClass { @@ -157,6 +224,25 @@ class StubClass public function stubMethod() { } + + public function stubMethodWithoutAttributes() + { + } + + #[TestRepeatableAttribute('first')] + #[TestRepeatableAttribute(value: 'second')] + #[TestRepeatableAttribute(second: 'test-argument', value: 'third')] + public function stubMethodWithMultipleAttributes() + { + } +} + +class StubClassWithoutAttributes +{ + #[TestAttribute('method')] + public function stubMethod() + { + } } #[TestAttribute('function')]