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
9 changes: 3 additions & 6 deletions .github/workflows/php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
strategy:
fail-fast: false
matrix:
php-versions: ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4', '8.5']
php-versions: ['8.2', '8.3', '8.4', '8.5']
steps:
- uses: actions/checkout@v2

Expand Down Expand Up @@ -43,8 +43,5 @@ jobs:
- name: PHPStan
run: bin/phpstan

- name: Composer validate
run: composer validate

# - name: Infection
# run: bin/infection --formatter=progress
- name: Infection
run: bin/infection --formatter=progress
15 changes: 9 additions & 6 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,23 @@
}
},
"require": {
"php": ">=7.4|^8.0",
"php": ">=8.2",
"webmozart/assert": "^1.0",
"symfony/event-dispatcher": "^4.0|^5.0|^6.0|^7.0"
"symfony/event-dispatcher": "^7.0||^8.0"
},
"require-dev": {
"ext-pdo": "*",
"doctrine/annotations": "^1.13",
"doctrine/orm": "^2.5",
"doctrine/orm": ">=2.5",
"infection/infection": "~0.13",
"phpstan/phpstan": "^2.0",
"phpstan/phpstan-phpunit": "^2.0",
"phpunit/phpunit": "^10.0|^11.0|^12.0",
"phpunit/phpunit": "^12.0",
"squizlabs/php_codesniffer": "^3.2",
"symfony/cache": "^4.0|^5.0|^6.0"
"symfony/cache": ">=7.0",
"symfony/var-exporter": "^7.0"
},
"scripts": {
"test": "bin/phpunit; bin/phpcs; bin/phpstan; bin/infection"
},
"config": {
"bin-dir": "bin",
Expand Down
5 changes: 0 additions & 5 deletions examples/ContextUsingBuilderTest.php
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
<?php declare(strict_types=1);
/**
* This file is part of the php-state project.
*
* (c) Yannick Voyer (http://github.com/yvoyer)
*/

namespace Star\Component\State\Example;

Expand Down
46 changes: 15 additions & 31 deletions examples/DoctrineMappedContextTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,12 @@

namespace Star\Component\State\Example;

use Doctrine\DBAL\DriverManager;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\Column;
use Doctrine\ORM\Mapping\Embedded;
use Doctrine\ORM\Mapping\Embeddable;
use Doctrine\ORM\Mapping\Entity;
use Doctrine\ORM\Mapping\GeneratedValue;
use Doctrine\ORM\Mapping\Id;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\ORM\ORMSetup;
use Doctrine\ORM\Tools\SchemaTool;
use Doctrine\ORM\Tools\Setup;
use PHPUnit\Framework\AssertionFailedError;
use PHPUnit\Framework\TestCase;
use Star\Component\State\Builder\StateBuilder;
Expand All @@ -27,14 +23,15 @@ public function setUp(): void
$this->markTestSkipped('Sqlite extension is needed');
}

$configuration = Setup::createAnnotationMetadataConfiguration([__DIR__], true);
$this->em = EntityManager::create(
$configuration = ORMSetup::createAttributeMetadataConfiguration([__DIR__], true);
$connection = DriverManager::getConnection(
[
'driver' => 'pdo_sqlite',
'in_memory' => true,
],
$configuration
$configuration,
);
$this->em = new EntityManager($connection, $configuration);
$tool = new SchemaTool($this->em);
$tool->createSchema([$this->em->getClassMetadata(MyEntity::class)]);
}
Expand Down Expand Up @@ -82,24 +79,16 @@ private function save(MyEntity $entity): MyEntity
}
}

/**
* @Entity()
*/
#[ORM\Entity]
class MyEntity
{
/**
* @var int
* @Id()
* @GeneratedValue(strategy="AUTO")
* @Column(name="id", type="integer")
*/
#[ORM\Id()]
#[ORM\GeneratedValue(strategy: "AUTO")]
#[ORM\Column(name: "id", type: "integer")]
public int $id;

/**
* @var MyState|StateMetadata
* @Embedded(class="MyState", columnPrefix="my_")
*/
private $state;
#[ORM\Embedded(class: "MyState", columnPrefix: "my_")]
private MyState $state;

public function __construct()
{
Expand All @@ -122,15 +111,10 @@ public function unlock(): void
}
}

/**
* @Embeddable()
*/
#[ORM\Embeddable]
final class MyState extends StateMetadata
{
/**
* @var string
* @Column(name="state", type="string")
*/
#[ORM\Column(name: "state", type: "string")]
protected string $current;

public function __construct()
Expand Down
1 change: 1 addition & 0 deletions phpcs.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<ruleset>
<rule ref="PSR2" />
<arg name="colors" />
<arg value="p" />

<file>src</file>
<file>examples</file>
Expand Down
36 changes: 25 additions & 11 deletions src/Builder/StateBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,30 @@
use Star\Component\State\TransitionRegistry;
use Star\Component\State\Transitions\ManyToOneTransition;
use Star\Component\State\Transitions\OneToOneTransition;
use function is_array;

/**
* Tool to build the StateMachine.
*/
final class StateBuilder
{
private TransitionRegistry $registry;
private EventRegistry $listeners;
private EventRegistry /* todo use StateRegistry */ $listeners;

public function __construct()
{
$this->registry = new TransitionRegistry();
$this->listeners = new EventDispatcherAdapter();
public function __construct(
?TransitionRegistry $registry = null,
?EventRegistry $listeners = null,
) {
/** @todo deprecated nullable construct, move to private */
if (!$registry) {
$registry = new TransitionRegistry();
}
$this->registry = $registry;

if (!$listeners) {
$listeners = new EventDispatcherAdapter();
}
$this->listeners = $listeners;
}

/**
Expand All @@ -31,9 +42,9 @@ public function __construct()
*
* @return StateBuilder
*/
public function allowTransition(string $name, $from, string $to): StateBuilder
public function allowTransition(string $name, string|array $from, string $to): StateBuilder
{
if (\is_array($from)) {
if (is_array($from)) {
$transition = new ManyToOneTransition($name, $to, ...$from);
} else {
$transition = new OneToOneTransition($name, $from, $to);
Expand All @@ -49,6 +60,7 @@ public function allowTransition(string $name, $from, string $to): StateBuilder
*/
public function allowCustomTransition(StateTransition $transition): void
{
// todo add interface for registry, and inject interface instead
$this->registry->addTransition($transition);
}

Expand All @@ -58,7 +70,7 @@ public function allowCustomTransition(StateTransition $transition): void
*
* @return StateBuilder
*/
public function addAttribute(string $attribute, $states): StateBuilder
public function addAttribute(string $attribute, string|array $states): StateBuilder
{
$states = (array) $states;
foreach ($states as $stateName) {
Expand All @@ -73,8 +85,10 @@ public function create(string $currentState): StateMachine
return new StateMachine($currentState, $this->registry, $this->listeners);
}

public static function build(): StateBuilder
{
return new static();
public static function build(
?TransitionRegistry $registry = null,
?EventRegistry $listeners = null,
): StateBuilder {
return new self($registry, $listeners);
}
}
51 changes: 51 additions & 0 deletions src/Callbacks/BufferStateChanges.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php declare(strict_types=1);

namespace Star\Component\State\Callbacks;

use RuntimeException;
use Star\Component\State\InvalidStateTransitionException;
use Star\Component\State\StateMachine;
use Webmozart\Assert\Assert;
use function get_class;
use function is_object;

final class BufferStateChanges implements TransitionCallback
{
/**
* @var array<string, list<string>>
*/
private array $buffer = [];

public function beforeStateChange($context, StateMachine $machine): void
{
if (is_object($context)) {
$context = get_class($context);
}
Assert::string($context, 'Context is expected to be a string. Got: %s');

$this->buffer[$context][] = __FUNCTION__;
}

public function afterStateChange($context, StateMachine $machine): void
{
if (is_object($context)) {
$context = get_class($context);
}
Assert::string($context, 'Context is expected to be a string. Got: %s');

$this->buffer[$context][] = __FUNCTION__;
}

public function onFailure(InvalidStateTransitionException $exception, $context, StateMachine $machine): string
{
throw new RuntimeException(__METHOD__ . ' is not implemented yet.');
}

/**
* @return array<string, list<string>>
*/
public function flushBuffer(): array
{
return $this->buffer;
}
}
25 changes: 20 additions & 5 deletions src/Event/StateEventStore.php
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
<?php declare(strict_types=1);
/**
* This file is part of the php-state project.
*
* (c) Yannick Voyer (http://github.com/yvoyer)
*/

namespace Star\Component\State\Event;

use InvalidArgumentException;
use function get_class;
use function sprintf;

final class StateEventStore
{
/**
Expand All @@ -29,4 +28,20 @@ final class StateEventStore
* @see TransitionWasFailed
*/
const FAILURE_TRANSITION = 'star_state.transition_failure';

public static function eventNameFromClass(StateEvent $event): string
{
// todo deprecate string event in favor of class name
return match (get_class($event)) {
TransitionWasRequested::class => self::BEFORE_TRANSITION,
TransitionWasSuccessful::class => self::AFTER_TRANSITION,
TransitionWasFailed::class => self::FAILURE_TRANSITION,
default => throw new InvalidArgumentException(
sprintf(
'Event "%s" is not mapped to a name.',
get_class($event),
)
),
};
}
}
5 changes: 0 additions & 5 deletions src/Event/TransitionWasRequested.php
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
<?php declare(strict_types=1);
/**
* This file is part of the php-state project.
*
* (c) Yannick Voyer (http://github.com/yvoyer)
*/

namespace Star\Component\State\Event;

Expand Down
5 changes: 0 additions & 5 deletions src/Event/TransitionWasSuccessful.php
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
<?php declare(strict_types=1);
/**
* This file is part of the php-state project.
*
* (c) Yannick Voyer (http://github.com/yvoyer)
*/

namespace Star\Component\State\Event;

Expand Down
5 changes: 0 additions & 5 deletions src/InvalidStateTransitionException.php
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
<?php declare(strict_types=1);
/**
* This file is part of the php-state project.
*
* (c) Yannick Voyer (http://github.com/yvoyer)
*/

namespace Star\Component\State;

Expand Down
5 changes: 0 additions & 5 deletions src/NotFoundException.php
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
<?php declare(strict_types=1);
/**
* This file is part of the php-state project.
*
* (c) Yannick Voyer (http://github.com/yvoyer)
*/

namespace Star\Component\State;

Expand Down
3 changes: 2 additions & 1 deletion src/Port/Symfony/EventDispatcherAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Star\Component\State\Port\Symfony;

use Star\Component\State\Event\StateEvent;
use Star\Component\State\Event\StateEventStore;
use Star\Component\State\EventRegistry;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
Expand All @@ -22,7 +23,7 @@ public function __construct(

public function dispatch(string $name, StateEvent $event): void
{
$this->dispatcher->dispatch($event);
$this->dispatcher->dispatch($event, StateEventStore::eventNameFromClass($event));
}

public function addListener(string $event, callable $listener): void
Expand Down
6 changes: 1 addition & 5 deletions src/StateMachine.php
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
<?php declare(strict_types=1);
/**
* This file is part of the php-state project.
*
* (c) Yannick Voyer (http://github.com/yvoyer)
*/

namespace Star\Component\State;

Expand Down Expand Up @@ -45,6 +40,7 @@ public function transit(
mixed $context,
?TransitionCallback $callback = null
): string {
// todo deprecate mixed to use StateContext
if (!$callback) {
$callback = new AlwaysThrowExceptionOnFailure();
}
Expand Down
Loading