Skip to content

Commit

Permalink
Merge pull request #166 from yohang/feature/symfony-bundle
Browse files Browse the repository at this point in the history
feature: Adds Symfony Bundle to 2.0
  • Loading branch information
yohang authored Jan 13, 2025
2 parents af75644 + 254cb6f commit 57d58fd
Show file tree
Hide file tree
Showing 23 changed files with 294 additions and 56 deletions.
10 changes: 3 additions & 7 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
vendor
build
bin
composer.phar
tests/Extension/Symfony/Fixtures/app/var/
vendor/
.phpunit.result.cache
composer.lock
cache.properties
coverage.clover
docs/_build
10 changes: 2 additions & 8 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
tests/Extension/Symfony/Fixtures/app/var/
vendor/
.phpunit.result.cache
vendor
build
bin
composer.phar
composer.lock
cache.properties
coverage.clover
docs/_build
.phpunit.result.cache
20 changes: 9 additions & 11 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,12 @@ ARG DEPENDENCIES=highest

RUN set -eux; \
apk add --no-cache acl libzip; \
apk add --no-cache --virtual .build-deps ${PHPIZE_DEPS} zlib-dev libzip-dev linux-headers; \
docker-php-ext-install zip; \
pecl install xdebug;\
docker-php-ext-enable xdebug; \
apk add --no-cache --virtual .build-deps ${PHPIZE_DEPS} zlib-dev libzip-dev; \
docker-php-ext-install zip opcache; \
pecl install pcov;\
docker-php-ext-enable pcov; \
apk del .build-deps;

RUN echo "xdebug.mode=coverage" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini

COPY --from=composer:2 /usr/bin/composer /usr/bin/composer

WORKDIR /app
Expand All @@ -25,8 +23,8 @@ RUN set -eux; \
if [ "${DEPENDENCIES}" = "lowest" ]; then COMPOSER_MEMORY_LIMIT=-1 composer update --prefer-lowest --no-interaction; fi; \
if [ "${DEPENDENCIES}" = "highest" ]; then COMPOSER_MEMORY_LIMIT=-1 composer update --no-interaction; fi

COPY ./examples /app/examples
COPY ./src /app/src
COPY ./tests /app/tests
COPY ./phpunit.xml.dist /app/
COPY ./psalm.xml /app/
COPY --link ./examples /app/examples
COPY --link ./src /app/src
COPY --link ./tests /app/tests
COPY --link ./phpunit.xml.dist /app/
COPY --link ./psalm.xml /app/
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ cli:
docker run -it --rm -v${PWD}:/app -w/app yohang/finite ash

test:
docker run -it --rm -v${PWD}:/app -w/app yohang/finite php ./vendor/bin/phpunit
docker run -it --rm -v${PWD}:/app -w/app yohang/finite php ./vendor/bin/phpunit --coverage-text

test_all_targets:
docker build -t yohang/finite:php-8.1 --build-arg PHP_VERSION=8.1 .
Expand All @@ -27,4 +27,4 @@ test_all_targets:

psalm:
docker build -t yohang/finite:php-8.4 --build-arg PHP_VERSION=8.4 .
docker run -it --rm yohang/finite:php-8.4 php ./vendor/bin/psalm --show-info=true
docker run -it --rm yohang/finite:php-8.4 php ./vendor/bin/psalm --show-info=true --no-diff
11 changes: 8 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,18 @@
],
"require": {
"php": ">=8.1",
"symfony/property-access": "^6.1|^7.0"
"symfony/property-access": ">=5.4,<8",
"psr/event-dispatcher": "^1.0"
},
"require-dev": {
"phpunit/phpunit": "^10.5.40",
"symfony/var-dumper": "^6.1|^7.0",
"symfony/var-dumper": ">=5.4,<8",
"twig/twig": "^3.4",
"vimeo/psalm": "dev-master@dev"
"vimeo/psalm": "dev-master@dev",
"symfony/http-kernel": ">=5.4,<8",
"symfony/framework-bundle": ">=5.4,<8",
"symfony/event-dispatcher": ">=5.4,<8",
"symfony/stopwatch": ">=5.4,<8"
},
"autoload": {
"psr-4": {
Expand Down
2 changes: 1 addition & 1 deletion phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<!-- http://www.phpunit.de/manual/current/en/appendixes.configuration.html -->
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
colors="true"
bootstrap="tests/bootstrap.php"
bootstrap="tests/Extension/Symfony/Fixtures/app/bootstrap.php"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd">
<source>
<include>
Expand Down
2 changes: 1 addition & 1 deletion psalm.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0"?>
<psalm
errorLevel="7"
errorLevel="1"
resolveFromConfigFile="true"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
Expand Down
4 changes: 3 additions & 1 deletion src/Event/Event.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

namespace Finite\Event;

abstract class Event
use Psr\EventDispatcher\StoppableEventInterface;

abstract class Event implements StoppableEventInterface
{
private bool $propagationStopped = false;

Expand Down
9 changes: 6 additions & 3 deletions src/Event/EventDispatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

namespace Finite\Event;

class EventDispatcher
use Psr\EventDispatcher\EventDispatcherInterface;
use Psr\EventDispatcher\StoppableEventInterface;

class EventDispatcher implements EventDispatcherInterface
{
/**
* @var array<string,array<callable>>
Expand All @@ -18,7 +21,7 @@ public function addEventListener(string $eventClass, callable $listener): void
$this->listeners[$eventClass][] = $listener;
}

public function dispatch(Event $event): void
public function dispatch(object $event): void
{
if (!isset($this->listeners[get_class($event)])) {
return;
Expand All @@ -27,7 +30,7 @@ public function dispatch(Event $event): void
foreach ($this->listeners[get_class($event)] as $listener) {
$listener($event);

if ($event->isPropagationStopped()) {
if ($event instanceof StoppableEventInterface && $event->isPropagationStopped()) {
return;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php
declare(strict_types=1);

namespace Finite\Extension\Symfony\Bundle\DependencyInjection;

use Finite\Extension\Twig\FiniteExtension as TwigExtension;
use Finite\StateMachine;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Extension\Extension;
use Symfony\Component\DependencyInjection\Reference;

final class FiniteExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container): void
{
$container->addDefinitions(
[
StateMachine::class => (new Definition(StateMachine::class))
->setArgument('$dispatcher', new Reference('event_dispatcher'))
->setPublic(true),
TwigExtension::class => new Definition(TwigExtension::class),
]
);
}
}
10 changes: 10 additions & 0 deletions src/Extension/Symfony/Bundle/FiniteBundle.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php
declare(strict_types=1);

namespace Finite\Extension\Symfony\Bundle;

use Symfony\Component\HttpKernel\Bundle\Bundle;

final class FiniteBundle extends Bundle
{
}
19 changes: 9 additions & 10 deletions src/StateMachine.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@
use Finite\Event\PostTransitionEvent;
use Finite\Event\PreTransitionEvent;
use Finite\Transition\TransitionInterface;
use Psr\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\PropertyAccess\PropertyAccess;

class StateMachine
{
public function __construct(
private readonly EventDispatcher $dispatcher = new EventDispatcher,
private readonly EventDispatcherInterface $dispatcher = new EventDispatcher,
)
{

}

/**
Expand Down Expand Up @@ -85,13 +85,19 @@ public function getReachablesTransitions(object $object, ?string $stateClass = n
);
}

public function getDispatcher(): EventDispatcherInterface
{
return $this->dispatcher;
}

/**
* @param class-string|null $stateClass
*/
private function extractState(object $object, ?string $stateClass = null): State
{
$property = $this->extractStateProperty($object, $stateClass);

/** @psalm-suppress MixedReturnStatement */
return PropertyAccess::createPropertyAccessor()->getValue($object, $property->getName());
}

Expand All @@ -107,11 +113,8 @@ private function extractStateProperty(object $object, ?string $stateClass = null
$reflectionClass = new \ReflectionClass($object);
/** @psalm-suppress DocblockTypeContradiction */
do {
if (!$reflectionClass) {
throw new \InvalidArgumentException('Found no state on object ' . get_class($object));
}

foreach ($reflectionClass->getProperties() as $property) {
/** @var \ReflectionUnionType|\ReflectionIntersectionType|\ReflectionNamedType $reflectionType */
$reflectionType = $property->getType();
if (null === $reflectionType) {
continue;
Expand All @@ -125,10 +128,6 @@ private function extractStateProperty(object $object, ?string $stateClass = null
continue;
}

if (!$reflectionType instanceof \ReflectionNamedType) {
continue;
}

/** @var class-string $name */
$name = $reflectionType->getName();
if (!enum_exists($name)) {
Expand Down
2 changes: 2 additions & 0 deletions src/Transition/Transition.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ class Transition implements TransitionInterface
{
public function __construct(
public readonly string $name,
/** @var State[] */
public readonly array $sourceStates,
public readonly State $targetState,
/** @var array<string, string> */
public readonly array $properties = []
)
{
Expand Down
8 changes: 8 additions & 0 deletions tests/E2E/BasicGraphTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@

class Article
{
public $noTypeHere = null;

public int|float $unionType = 0;

public \Traversable&\Countable $intersectionType;

public string $namedType = 'named';

private SimpleArticleState $state = SimpleArticleState::DRAFT;

private readonly \DateTimeInterface $createdAt;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php
declare(strict_types=1);

namespace Finite\Tests\Extension\Symfony\Bundle\DependencyInjection;

use Finite\Extension\Symfony\Bundle\DependencyInjection\FiniteExtension;
use Finite\Extension\Twig\FiniteExtension as TwigExtension;
use Finite\StateMachine;
use PHPUnit\Framework\TestCase;
use Symfony\Component\DependencyInjection\ContainerBuilder;

class FiniteExtensionTest extends TestCase
{
public function test_it_loads_services(): void
{
$container = $this->getMockBuilder(ContainerBuilder::class)
->disableOriginalConstructor()
->getMock();

$container->expects($this->once())->method('addDefinitions')->with(
$this->logicalAnd(
$this->countOf(2),
$this->arrayHasKey(StateMachine::class),
$this->arrayHasKey(TwigExtension::class),
),
);

$extension = new FiniteExtension;
$extension->load([], $container);
}
}
11 changes: 11 additions & 0 deletions tests/Extension/Symfony/Fixtures/Model/Document.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php
declare(strict_types=1);

namespace Finite\Tests\Extension\Symfony\Fixtures\Model;

use Finite\Tests\Extension\Symfony\Fixtures\State\DocumentState;

class Document
{
public DocumentState $state = DocumentState::DRAFT;
}
25 changes: 25 additions & 0 deletions tests/Extension/Symfony/Fixtures/State/DocumentState.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);

namespace Finite\Tests\Extension\Symfony\Fixtures\State;

use Finite\State;
use Finite\Transition\Transition;

enum DocumentState: string implements State
{
case DRAFT = 'draft';
case PUBLISHED = 'published';
case REPORTED = 'reported';
case DISABLED = 'disabled';

public static function getTransitions(): array
{
return [
new Transition('publish', [self::DRAFT], self::PUBLISHED),
new Transition('clear', [self::REPORTED, self::DISABLED], self::PUBLISHED),
new Transition('report', [self::PUBLISHED], self::REPORTED),
new Transition('disable', [self::REPORTED, self::PUBLISHED], self::DISABLED),
];
}
}
32 changes: 32 additions & 0 deletions tests/Extension/Symfony/Fixtures/app/AppKernel.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php
declare(strict_types=1);

use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Kernel;

class AppKernel extends Kernel
{
use MicroKernelTrait;

public function registerBundles(): iterable
{
return [
new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
new Finite\Extension\Symfony\Bundle\FiniteBundle(),
];
}

protected function configureContainer(ContainerBuilder $c, LoaderInterface $loader): void
{
$c->setParameter('kernel.debug', true);
$c->prependExtensionConfig('framework', ['test' => true, 'profiler' => true]);
}

public function getProjectDir(): string
{
return __DIR__;
}

}
7 changes: 7 additions & 0 deletions tests/Extension/Symfony/Fixtures/app/bootstrap.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php
declare(strict_types=1);

$loader = require __DIR__ . '/../../../../../vendor/autoload.php';
require __DIR__.'/AppKernel.php';

return $loader;
Loading

0 comments on commit 57d58fd

Please sign in to comment.