Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 58 additions & 2 deletions src/Attr.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.',
);
}
}
Expand Down Expand Up @@ -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
*
Expand All @@ -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<scalar[]>|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.
*
Expand Down
86 changes: 86 additions & 0 deletions tests/AttrTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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
{
Expand All @@ -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')]
Expand Down
Loading