Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support mermaid; modernize codebase #7

Merged
merged 3 commits into from
Sep 7, 2024
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
10 changes: 10 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
root = true

[*]
end_of_line = lf
charset = utf-8
indent_size = 4
indent_style = space
max_line_length = 120
insert_final_newline = true
trim_trailing_whitespace = true
14 changes: 9 additions & 5 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
/.gitattributes export-ignore
/.github export-ignore
/.gitignore export-ignore
/phpunit.xml.dist export-ignore
/tests export-ignore
.github export-ignore
tests export-ignore
.editorconfig export-ignore
.gitattributes export-ignore
.gitignore export-ignore
.php-cs-fixer.dist.ph export-ignore
phpstan.dist.neon export-ignore
phpunit.xml.dist export-ignore
README.md export-ignore
2 changes: 2 additions & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
github: [uuf6429]
custom: ['https://paypal.me/uuf6429']
49 changes: 26 additions & 23 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,34 +8,37 @@ on:

jobs:

build:
name: Test
Lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
coverage: none
- run: composer update --ansi --no-progress --prefer-dist --no-interaction
- run: composer run lint

Test:
strategy:
fail-fast: false
matrix:
php: [ '7.4', '8.0' ]

os: [ 'ubuntu-latest' ]
php: [ '7.4', '8.0', '8.1', '8.2', '8.3' ]
include:
- php: '8.3'
os: 'macos-latest'
- php: '8.3'
os: 'windows-latest'
runs-on: ${{ matrix.os }}
steps:
- name: Set up PHP
uses: shivammathur/setup-php@v2
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
coverage: xdebug2

- name: Checkout code
uses: actions/checkout@v2
with:
fetch-depth: 2

- name: Download dependencies
uses: ramsey/composer-install@v1
with:
composer-options: --no-interaction --prefer-dist --optimize-autoloader

- name: Run tests
run: ./vendor/bin/phpunit --coverage-clover coverage.xml

- name: Upload to Codecov
coverage: xdebug
- run: composer update --ansi --no-progress --prefer-dist --no-interaction
- run: composer run test:cover
- uses: codecov/codecov-action@v4
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
run: bash <(curl -s https://codecov.io/bash)
11 changes: 7 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
.idea/
vendor/
*.cache
*.iml
/vendor/
composer.lock
phpstan.neon
.php-cs-fixer.php
.php-cs-fixer.cache
coverage.xml
phpunit.xml
.phpunit.result.cache
14 changes: 14 additions & 0 deletions .php-cs-fixer.dist.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

return (new PhpCsFixer\Config())
->setRiskyAllowed(true)
->setParallelConfig(PhpCsFixer\Runner\Parallel\ParallelConfigFactory::detect())
->setRules([
'@PER-CS2.0' => true,
'@PER-CS2.0:risky' => true,
'cast_spaces' => ['space' => 'none'],
])
->setFinder(
(new PhpCsFixer\Finder())
->in(__DIR__),
);
68 changes: 53 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,23 @@
This library provides some interfaces and a basic implementation of a State Engine or State Machine.

✨ **Highlights:**

- Dual functionality:
1. Either as a basic state engine; switching to a desired state as long the transition is defined)
([see "JiraIssueTest"](#jiraissuetest-state-engine))
2. Or a more sophisticated state machine; same as above but matching data for any state
([see "TurnstileTest"](#turnstiletest-state-machine))
1. Either as a basic state engine; switching to a desired state as long the transition is
defined ([see "JiraIssueTest"](#jiraissuetest-state-engine))
2. Or a more sophisticated state machine; same as above but matching data for any
state ([see "TurnstileTest"](#turnstiletest-state-machine))
- Highly composable - everything can be replaced as desired
- [PSR-14](http://www.php-fig.org/psr/psr-14/) (Event Dispatcher) compatible
- Fluent builder interface ([see "From Scratch"](#from-scratch))
- Generates PlantUML markup ([see "Examples & Testing"](#examples--testing))
- Generates Mermaid or PlantUML markup ([see "Examples & Testing"](#-examples--testing))

## 🔌 Installation

The recommended and easiest way to install this library is through [Composer](https://getcomposer.org/):

```bash
composer require "uuf6429/state-engine-php" "^2.0"
```shell
composer require "uuf6429/state-engine-php"
```

## 🧐 Why?
Expand Down Expand Up @@ -54,12 +55,15 @@ There are a few key parts to how this works:
## 🚀 Usage

You have the possibility to use it from scratch or plug it into your existing. There are basically three parts to it:

1. configuring the engine (creating states and transitions)
2. using the engine (eg, in a web controller or service)
3. (optionally) handling events (with the same event dispatcher provided to the engine)

A slightly different situation would be when you need to provide a list of valid transitions, for example to the user.
In this case, having the [`StateTraversion`](https://github.com/uuf6429/state-engine-php/blob/main/src/Implementation/Traits/StateTraversion.php) trait on the repository would be useful.
In this case, having the [
`StateTraversion`](https://github.com/uuf6429/state-engine-php/blob/main/src/Implementation/Traits/StateTraversion.php)
trait on the repository would be useful.

### From Scratch

Expand Down Expand Up @@ -135,24 +139,58 @@ $doorStateManager->changeState($doorStateMutator, new State('closed'));

## 😎 Examples & Testing

You can find some examples in this readme as well as [the tests](https://github.com/uuf6429/state-engine-php/tree/main/tests), some of which explained below.
You can find some examples in this readme as well
as [the tests](https://github.com/uuf6429/state-engine-php/tree/main/tests), some of which are explained below.

### [`JiraIssueTest`](https://github.com/uuf6429/state-engine-php/blob/main/tests/JiraIssueTest.php) State Engine

This test provides a realistic example of how Jira Issue states could be set up.

The test also generates the PlantUML diagram below (embedded as an image due to GFM limitations), thanks to the [Plantable trait](https://github.com/uuf6429/state-engine-php/blob/main/src/Implementation/Traits/Plantable.php):

![jira issue example](https://www.planttext.com/api/plantuml/svg/TPBDRiCW48JlFCKUauDV88SgZgfAlLIrymGqJ2rK31PiBENjYurfux_hpZVB370EB3tVMoF4uI9lFyOrHogA5pgKLff7qE589xgWqPRaD5cIxvPUqG_ScmnSi8ygVJjF2ZsCwrfO5a_xHbCDgHuZDNcpJZVNTWQCbUNlr1FLuBktn8w-qb0i5wuwV02AMkSHOx7K9cnR_ikaqhCEMLmqgCg1lyAg8L5Lxe8r36J0nbNvfEmwfqnNTjqyqZn5hf0IfGQCmDes8i-tDrTbZAGDr1xtb3sodpA4WTtG9rzmfeTAZpKg8vsdwmTr7QmGvtY9yJV-0W00)
The test also generates the Mermaid diagram below, thanks to
the [Mermaidable trait](https://github.com/uuf6429/state-engine-php/blob/main/src/Implementation/Traits/Mermaidable.php):

```mermaid
stateDiagram
s1_backlog: Backlog
s2_analysis: Analysis
s3_in_dev: In Dev
s4_ready_for_dev: Ready for Dev
s5_ready_for_qa: Ready for QA
s6_ready_for_release: Ready for Release
s7_in_qa: In QA
s8_resolved: Resolved
s1_backlog --> s2_analysis: Begin analysis
s1_backlog --> s3_in_dev: Fast-track for development
s2_analysis --> s4_ready_for_dev: Analysis complete
s2_analysis --> s1_backlog: Return to backlog
s4_ready_for_dev --> s2_analysis: Need more details
s4_ready_for_dev --> s3_in_dev: Begin development
s3_in_dev --> s5_ready_for_qa: Send to QA
s3_in_dev --> s6_ready_for_release: Fast-track for release
s3_in_dev --> s4_ready_for_dev: Stop development
s5_ready_for_qa --> s7_in_qa: Begin testing
s7_in_qa --> s4_ready_for_dev: QA Failed
s7_in_qa --> s6_ready_for_release: QA Passed
s6_ready_for_release --> s8_resolved: Released
s8_resolved --> s1_backlog: Reopen
```

### [`TurnstileTest`](https://github.com/uuf6429/state-engine-php/blob/main/tests/JiraIssueTest.php) State Machine

This test illustrates how a [state machine](https://en.wikipedia.org/wiki/Finite-state_machine) can be used to model a [turnstile gate](https://en.wikipedia.org/wiki/Turnstile).
This test illustrates how a [state machine](https://en.wikipedia.org/wiki/Finite-state_machine) can be used to model
a [turnstile gate](https://en.wikipedia.org/wiki/Turnstile).
As before, here's the generated diagram:

![turnstile example](https://www.planttext.com/api/plantuml/svg/SoWkIImgAStDuUBIyCmjI2mkJapAITLKqDMrKz08W7Ej59ppC_CK2d8IarDJk90amEgGDLef1AGM5UVdAPGdvcGNAvHa5EMNfcTmSJcavgM0h040)
```mermaid
stateDiagram
s1_locked: Impassable
s2_open: Passable
s1_locked --> s2_open: Coin placed
s2_open --> s1_locked: Person walks through
```

Here's how the state machine definition looks like and how it could be used:

Here's how the state machine definition looks like and is used:
```php
use App\Models\Turnstile; // example model that implements StateAwareInterface

Expand Down
33 changes: 28 additions & 5 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
{
"name": "uuf6429/state-engine",
"type": "library",
"homepage": "https://github.com/uuf6429/state-engine-php",
"readme": "README.md",
"license": "MIT",
"description": "A library providing interfaces and basic implementation of a State Engine or Machine",
"license": "MIT",
"type": "library",
"keywords": [
"state",
"engine",
Expand All @@ -14,18 +12,24 @@
"workflow",
"uuf6429"
],
"readme": "README.md",
"authors": [
{
"name": "Christian Sciberras",
"email": "[email protected]"
}
],
"homepage": "https://github.com/uuf6429/state-engine-php",
"require": {
"php": "^7.4 || ^8.0",
"psr/event-dispatcher": "^1.0"
},
"require-dev": {
"phpunit/phpunit": "^9.5"
"ergebnis/composer-normalize": "^2.42",
"friendsofphp/php-cs-fixer": "^3.53",
"phpstan/phpstan": "^1.11",
"phpunit/phpunit": "^9.5 || ^10",
"roave/security-advisories": "dev-latest"
},
"autoload": {
"psr-4": {
Expand All @@ -36,5 +40,24 @@
"psr-4": {
"uuf6429\\StateEngine\\": "tests/"
}
},
"config": {
"allow-plugins": {
"ergebnis/composer-normalize": true
},
"process-timeout": 0
},
"scripts": {
"lint": [
"composer normalize --dry-run",
"composer exec phpstan -- analyse --no-progress",
"composer exec php-cs-fixer -- fix --dry-run --show-progress=none --diff"
],
"lint:fix": [
"composer normalize",
"composer exec php-cs-fixer -- fix --show-progress=dots --diff"
],
"test": "phpunit ./tests/",
"test:cover": "@php -dzend_extension=php_xdebug -dxdebug.mode=coverage vendor/bin/phpunit --coverage-clover coverage.xml ./tests/"
}
}
6 changes: 6 additions & 0 deletions phpstan.dist.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
parameters:
level: 9
treatPhpDocTypesAsCertain: true
paths:
- src
- tests
5 changes: 1 addition & 4 deletions src/Exceptions/InvalidArgumentException.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,4 @@

namespace uuf6429\StateEngine\Exceptions;

class InvalidArgumentException extends \InvalidArgumentException
{

}
class InvalidArgumentException extends \InvalidArgumentException {}
5 changes: 1 addition & 4 deletions src/Exceptions/RuntimeException.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,4 @@

namespace uuf6429\StateEngine\Exceptions;

class RuntimeException extends \RuntimeException
{

}
class RuntimeException extends \RuntimeException {}
26 changes: 18 additions & 8 deletions src/Implementation/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,7 @@ class Builder
*/
protected array $transitions = [];

private function __construct()
{

}
final private function __construct() {}

public static function create(): self
{
Expand All @@ -41,9 +38,19 @@ public static function create(): self
public static function makeStateMutator(callable $getter, callable $setter): StateAwareInterface
{
return new class ($getter, $setter) implements StateAwareInterface {
/**
* @var callable(): StateInterface
*/
private $getter;
/**
* @var callable(StateInterface): void
*/
private $setter;

/**
* @param callable(): StateInterface $getter
* @param callable(StateInterface): void $setter
*/
public function __construct(callable $getter, callable $setter)
{
$this->getter = $getter;
Expand Down Expand Up @@ -105,20 +112,23 @@ public function defTransition(string $oldStateName, string $newStateName, ?strin
new Transition(
$this->states[$oldStateName] ?? new State($oldStateName),
$this->states[$newStateName] ?? new State($newStateName),
$description
)
$description,
),
);
}

/**
* @param array<int|string, mixed> $data
*/
public function defDataTransition(string $oldStateName, array $data, string $newStateName, ?string $description = null): self
{
return $this->addTransition(
new TransitionWithData(
$this->states[$oldStateName] ?? new State($oldStateName),
$data,
$this->states[$newStateName] ?? new State($newStateName),
$description
)
$description,
),
);
}

Expand Down
Loading