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
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,16 @@ enum ExampleEnum
{
return self::findAttribute(CustomStuff::class, $this);
}

public function getCustomWithTrue(): CustomStuff|null
{
return self::findAttribute(CustomStuff::class, $this, static fn (CustomStuff $s) => $s->thing);
}
}
```

See the trait for more methods.
You can use the `$filter` parameter to better revise results, so that you don't have to create multiple classes to gain the same functionality.

Attributes are preloaded once per enum and stored in AttributeCache.

Expand Down
58 changes: 47 additions & 11 deletions src/AttributeControlTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,29 @@ trait AttributeControlTrait
* @template T of CacheableAttributeInterface
*
* @param class-string<T> $class
* @param null|callable(T): bool $filter whether to return attribute
*
* @return list<T>
*/
protected static function findAttributes(
string $class,
\UnitEnum $case
\UnitEnum $case,
callable|null $filter = null
): array {
return AttributeCache::instance()->get(static::class)[$class][$case->name] ?? []; // @phpstan-ignore-line
/** @var list<T> $attributes */
$attributes = AttributeCache::instance()->get(static::class)[$class][$case->name] ?? []; // @phpstan-ignore-line

if (null !== $filter) {
foreach ($attributes as $index => $attribute) {
if (!$filter($attribute)) {
unset($attributes[$index]);
}
}

$attributes = array_values($attributes);
}

return $attributes;
}

/**
Expand All @@ -26,14 +41,16 @@ protected static function findAttributes(
* @template T of CacheableAttributeInterface
*
* @param class-string<T> $class
* @param null|callable(T): bool $filter whether to return attribute
*
* @return T|null
*/
protected static function findAttribute(
string $class,
\UnitEnum $case
\UnitEnum $case,
callable|null $filter = null
): object|null {
return self::findAttributes($class, $case)[0] ?? null;
return self::findAttributes($class, $case, $filter)[0] ?? null;
}

/**
Expand All @@ -42,12 +59,14 @@ protected static function findAttribute(
* @template T of CacheableAttributeInterface
*
* @param class-string<T> $class
* @param null|callable(T): bool $filter whether to return attribute
*/
protected static function attributeExists(
string $class,
\UnitEnum $case
\UnitEnum $case,
callable|null $filter = null
): bool {
return null !== self::findAttribute($class, $case);
return null !== self::findAttribute($class, $case, $filter);
}

/**
Expand All @@ -56,15 +75,32 @@ protected static function attributeExists(
* @template T of CacheableAttributeInterface
*
* @param class-string<T> $class
* @param null|callable(T): bool $filter whether to return case
*
* @return list<self>
*/
protected static function findCases(
string $class
string $class,
callable|null $filter = null
): array {
return array_map(
static fn (string $case) => self::{$case},
array_keys(AttributeCache::instance()->get(static::class)[$class])
);
$results = AttributeCache::instance()->get(static::class)[$class];

if (null !== $filter) {
$keys = [];

foreach ($results as $case => $attributes) {
/** @var T $attribute */
foreach ($attributes as $attribute) {
if ($filter($attribute)) {
$keys[] = $case;
break;
}
}
}
} else {
$keys = array_keys($results);
}

return array_map(static fn (string $case) => self::{$case}, $keys);
}
}
24 changes: 16 additions & 8 deletions tests/Enum/ExposedEnum.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,52 +24,60 @@ enum ExposedEnum
* @template T of CacheableAttributeInterface
*
* @param class-string<T> $class
* @param null|callable(T): bool $filter
*
* @return list<T>
*/
public static function tFindAttributes(
string $class,
self $case
self $case,
callable|null $filter = null
): array {
return self::findAttributes($class, $case);
return self::findAttributes($class, $case, $filter);
}

/**
* @template T of CacheableAttributeInterface
*
* @param class-string<T> $class
* @param null|callable(T): bool $filter
*
* @return T|null
*/
public static function tFindAttribute(
string $class,
self $case
self $case,
callable|null $filter = null
): object|null {
return self::findAttribute($class, $case);
return self::findAttribute($class, $case, $filter);
}

/**
* @template T of CacheableAttributeInterface
*
* @param class-string<T> $class
* @param null|callable(T): bool $filter
*/
public static function tAttributeExists(
string $class,
self $case
self $case,
callable|null $filter = null
): bool {
return self::attributeExists($class, $case);
return self::attributeExists($class, $case, $filter);
}

/**
* @template T of CacheableAttributeInterface
*
* @param class-string<T> $class
* @param null|callable(T): bool $filter
*
* @return list<self>
*/
public static function tFindCases(
string $class
string $class,
callable|null $filter = null
): array {
return self::findCases($class);
return self::findCases($class, $filter);
}
}
59 changes: 59 additions & 0 deletions tests/Unit/ExposedEnumTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,26 @@ public function testFindAttributes(): void
);
}

public function testFindAttributesWithFilter(): void
{
$callback = static fn (CustomStuff $s) => $s->thing;

static::assertEmpty(
ExposedEnum::tFindAttributes(CustomStuff::class, ExposedEnum::Bar, $callback)
);
static::assertCount(
1,
$found = ExposedEnum::tFindAttributes(CustomStuff::class, ExposedEnum::Foo, $callback)
);
static::assertInstanceOf(
CustomStuff::class,
$found[0]
);
static::assertTrue(
$found[0]->thing
);
}

public function testFindAttribute(): void
{
static::assertNull(
Expand All @@ -46,6 +66,22 @@ public function testFindAttribute(): void
);
}

public function testFindAttributeWithFilter(): void
{
$callback = static fn (CustomStuff $s) => !$s->thing;

static::assertNull(
ExposedEnum::tFindAttribute(CustomStuff::class, ExposedEnum::Bar, $callback)
);
static::assertInstanceOf(
CustomStuff::class,
$output = ExposedEnum::tFindAttribute(CustomStuff::class, ExposedEnum::Foo, $callback)
);
static::assertFalse(
$output->thing
);
}

public function testAttributeExists(): void
{
static::assertFalse(
Expand All @@ -63,6 +99,16 @@ public function testAttributeExists(): void
);
}

public function testAttributeExistsWithFilter(): void
{
static::assertFalse(
ExposedEnum::tAttributeExists(NotAllowed::class, ExposedEnum::Bar, static fn () => false)
);
static::assertFalse(
ExposedEnum::tAttributeExists(CustomStuff::class, ExposedEnum::Foo, static fn () => false)
);
}

public function testFindCases(): void
{
static::assertEquals(
Expand All @@ -72,4 +118,17 @@ public function testFindCases(): void
ExposedEnum::tFindCases(CustomStuff::class)
);
}

public function testFindCasesWithFilter(): void
{
static::assertEquals(
[
ExposedEnum::Foo,
],
ExposedEnum::tFindCases(CustomStuff::class, static fn (CustomStuff $s) => $s->thing),
);
static::assertEmpty(
ExposedEnum::tFindCases(CustomStuff::class, static fn () => false)
);
}
}