Skip to content

Commit

Permalink
Add AttributeReader helper and TaskQueue attribute
Browse files Browse the repository at this point in the history
  • Loading branch information
roxblnfk committed Jan 20, 2024
1 parent 62ffd45 commit 895734d
Show file tree
Hide file tree
Showing 11 changed files with 248 additions and 2 deletions.
17 changes: 17 additions & 0 deletions src/Attribute/TaskQueue.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

declare(strict_types=1);

namespace Temporal\Sugar\Attribute;

#[\Attribute(\Attribute::TARGET_CLASS)]
final class TaskQueue
{
/**
* @param non-empty-string $name Task queue name.
*/
public function __construct(
public readonly string $name,
) {
}
}
91 changes: 91 additions & 0 deletions src/Internal/AttributeReader.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<?php

declare(strict_types=1);

namespace Temporal\Sugar\Internal;

/**
* @internal
*/
final class AttributeReader
{
/**
* @param class-string $class
* @param list<class-string> $attributes
*
* @return array<class-string, list<object>>
*/
public static function fromClass(
string $class,
array $attributes,
bool $merge = true,
bool $inheritance = true,
bool $interfaces = true,
): array {
$reflection = new \ReflectionClass($class);
if ($reflection->isInternal()) {
return [];
}

/** @var array<class-string, array<class-string, list<object>>> $cache */
static $cache = [];

$result = $cache[$class] ?? self::initAttributes($reflection, $attributes);

if (!$inheritance) {
return $result;
}

if ($parent = $reflection->getParentClass()) {
$attrs = self::fromClass($parent->getName(), $attributes, $merge, true, false);
$result = $merge
? \array_merge_recursive($result, $attrs)
: $result + $attrs;
}

if (!$interfaces) {
return $result;
}

foreach (self::sortInterfaces($reflection) as $interface) {
$attrs = self::fromClass($interface, $attributes, $merge, false, false);
$result = $merge
? \array_merge_recursive($result, $attrs)
: $result + $attrs;
}

return $result;
}

/**
* @param \ReflectionClass $reflection
* @param array<class-string> $filter
* @return array<class-string, list<object>>
*/
private static function initAttributes(\ReflectionClass $reflection, array $filter): array
{
$result = [];
foreach ($reflection->getAttributes() as $attribute) {
$instance = $attribute->newInstance();
foreach ($filter as $attributeClass) {
if ($instance instanceof $attributeClass) {
$result[$attributeClass][] = $instance;
}
}
}

return $result;
}

private static function sortInterfaces(\ReflectionClass $class): array
{
$result = [];
foreach ($class->getInterfaces() as $reflection) {
$result[$reflection->getName()] = \count($reflection->getInterfaces());
}

\arsort($result);

return \array_keys($result);
}
}
2 changes: 1 addition & 1 deletion src/Stub/ClientFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ public static function workflow(
// Timeouts
$executionTimeout === 0 or $options = $options->withWorkflowExecutionTimeout($executionTimeout);
$runTimeout === 0 or $options = $options->withWorkflowRunTimeout($executionTimeout);
$taskTimeout > 10 and $options = $options->withWorkflowTaskTimeout(\max(60, $taskTimeout));
$taskTimeout !== 10 and $options = $options->withWorkflowTaskTimeout(\max(60, $taskTimeout));
// Workflow ID
$workflowId === null or $options = $options->withWorkflowId((string)$workflowId);
$workflowIdReusePolicy === IdReusePolicy::Unspecified or $options = $options
Expand Down
2 changes: 1 addition & 1 deletion src/Stub/WorkflowFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ public static function childWorkflow(
// Timeouts
$executionTimeout === 0 or $options = $options->withWorkflowExecutionTimeout($executionTimeout);
$runTimeout === 0 or $options = $options->withWorkflowRunTimeout($executionTimeout);
$taskTimeout > 10 and $options = $options->withWorkflowTaskTimeout(\max(60, $taskTimeout));
$taskTimeout !== 10 and $options = $options->withWorkflowTaskTimeout(\max(60, $taskTimeout));
// Workflow ID
$workflowId === null or $options = $options->withWorkflowId((string)$workflowId);
$workflowIdReusePolicy === IdReusePolicy::Unspecified or $options = $options
Expand Down
12 changes: 12 additions & 0 deletions tests/Stub/Attributed/AbstractAttributed.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace Temporal\Sugar\Tests\Stub\Attributed;

use Temporal\Sugar\Attribute\TaskQueue;

#[TaskQueue(name: 'test-queue-abstract')]
abstract class AbstractAttributed implements InterfaceAttributed
{
}
12 changes: 12 additions & 0 deletions tests/Stub/Attributed/ExtendedAttributed.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace Temporal\Sugar\Tests\Stub\Attributed;

use Temporal\Sugar\Attribute\TaskQueue;

#[TaskQueue(name: 'test-queue-extended')]
final class ExtendedAttributed extends AbstractAttributed
{
}
12 changes: 12 additions & 0 deletions tests/Stub/Attributed/InterfaceAttributed.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace Temporal\Sugar\Tests\Stub\Attributed;

use Temporal\Sugar\Attribute\TaskQueue;

#[TaskQueue(name: 'test-queue-interface')]
interface InterfaceAttributed extends ParentInterfaceAttributed
{
}
12 changes: 12 additions & 0 deletions tests/Stub/Attributed/ParentInterfaceAttributed.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace Temporal\Sugar\Tests\Stub\Attributed;

use Temporal\Sugar\Attribute\TaskQueue;

#[TaskQueue(name: 'test-queue-parent-interface')]
interface ParentInterfaceAttributed extends ParentParentInterfaceAttributed
{
}
12 changes: 12 additions & 0 deletions tests/Stub/Attributed/ParentParentInterfaceAttributed.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace Temporal\Sugar\Tests\Stub\Attributed;

use Temporal\Sugar\Attribute\TaskQueue;

#[TaskQueue(name: 'test-queue-parent-parent-interface')]
interface ParentParentInterfaceAttributed
{
}
12 changes: 12 additions & 0 deletions tests/Stub/Attributed/SimpleClass.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace Temporal\Sugar\Tests\Stub\Attributed;

use Temporal\Sugar\Attribute\TaskQueue;

#[TaskQueue(name: 'test-queue')]
final class SimpleClass
{
}
66 changes: 66 additions & 0 deletions tests/Unit/Internal/AttributeReaderTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php

declare(strict_types=1);

namespace Temporal\Sugar\Tests\Unit\Internal;

use PHPUnit\Framework\TestCase;
use Temporal\Sugar\Attribute\TaskQueue;
use Temporal\Sugar\Internal\AttributeReader;

class AttributeReaderTest extends TestCase
{
public function testFromClass(): void
{
$result = AttributeReader::fromClass(
\Temporal\Sugar\Tests\Stub\Attributed\SimpleClass::class,
[TaskQueue::class]
);

$this->assertArrayHasKey(TaskQueue::class, $result);
$this->assertIsArray($result[TaskQueue::class]);
$this->assertCount(1, $result[TaskQueue::class]);
$this->assertInstanceOf(TaskQueue::class, $result[TaskQueue::class][0]);
$this->assertEquals('test-queue', $result[TaskQueue::class][0]->name);
}

public function testFromExtendedClassWithInheritanceWithMerge(): void
{
$result = AttributeReader::fromClass(
\Temporal\Sugar\Tests\Stub\Attributed\ExtendedAttributed::class,
[TaskQueue::class],
merge: true,
);

$this->assertArrayHasKey(TaskQueue::class, $result);
$this->assertIsArray($result[TaskQueue::class]);
$this->assertCount(5, $result[TaskQueue::class]);
$this->assertInstanceOf(TaskQueue::class, $result[TaskQueue::class][0]);
$this->assertInstanceOf(TaskQueue::class, $result[TaskQueue::class][1]);
$this->assertInstanceOf(TaskQueue::class, $result[TaskQueue::class][3]);
$this->assertEquals('test-queue-extended', $result[TaskQueue::class][0]->name);
$this->assertEquals('test-queue-abstract', $result[TaskQueue::class][1]->name);
$this->assertEquals('test-queue-interface', $result[TaskQueue::class][2]->name);
$this->assertEquals('test-queue-parent-interface', $result[TaskQueue::class][3]->name);
$this->assertEquals('test-queue-parent-parent-interface', $result[TaskQueue::class][4]->name);
}

public function testFromInterfaceWithInheritance(): void
{
$result = AttributeReader::fromClass(
\Temporal\Sugar\Tests\Stub\Attributed\InterfaceAttributed::class,
[TaskQueue::class],
merge: true,
);

$this->assertArrayHasKey(TaskQueue::class, $result);
$this->assertIsArray($result[TaskQueue::class]);
$this->assertCount(3, $result[TaskQueue::class]);
$this->assertInstanceOf(TaskQueue::class, $result[TaskQueue::class][0]);
$this->assertInstanceOf(TaskQueue::class, $result[TaskQueue::class][1]);
$this->assertInstanceOf(TaskQueue::class, $result[TaskQueue::class][2]);
$this->assertEquals('test-queue-interface', $result[TaskQueue::class][0]->name);
$this->assertEquals('test-queue-parent-interface', $result[TaskQueue::class][1]->name);
$this->assertEquals('test-queue-parent-parent-interface', $result[TaskQueue::class][2]->name);
}
}

0 comments on commit 895734d

Please sign in to comment.