Skip to content

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Jeroen-G committed Jun 15, 2020
0 parents commit 7490167
Show file tree
Hide file tree
Showing 29 changed files with 8,017 additions and 0 deletions.
15 changes: 15 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 4
trim_trailing_whitespace = true

[*.md]
trim_trailing_whitespace = false

[*.{yml,yaml}]
indent_size = 2
49 changes: 49 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
name: CI

on:
push:

jobs:
php-tests:
runs-on: ubuntu-latest

strategy:
matrix:
php: [7.4]

name: PHP${{ matrix.php }}

steps:
- name: Checkout code
uses: actions/checkout@v1

- name: Setup PHP
uses: shivammathur/setup-php@v1
with:
php-version: ${{ matrix.php }}
extensions: json

- name: Install dependencies
run: composer update --prefer-dist --no-suggest --no-interaction --no-scripts

- name: Check codestyle
run: vendor/bin/ecs check --config=dev/easy-coding-standard.yml --no-progress-bar .

- name: Execute unit tests
run: vendor/bin/phpunit --testdox --colors=always --coverage-text=report/coverage.txt

- name: Archive code coverage results
if: ${{ github.event_name != 'pull_request' }}
uses: actions/upload-artifact@v1
with:
name: code-coverage-report
path: report/coverage.txt

- name: Report coverage in PR
if: ${{ github.event_name == 'pull_request' }}
uses: slavcodev/[email protected]
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
clover_file: "logs/clover.xml"
threshold_alert: 10
threshold_warning: 50
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/node_modules
/report
/vendor
.env
.env.backup
.phpunit.result.cache
npm-debug.log
yarn-error.log
81 changes: 81 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# Default to showing help section
info: intro help

intro:
@echo ""
@echo "Statinator"
@echo ""

# ===========================
# Main commands
# ===========================

# Dependencies
install: intro do-composer-install do-assets-install
update: intro do-composer-update

# Tests
tests: intro do-test-unit do-test-report
test-unit: intro do-test-unit

# Development
pre-commit: intro do-lint-staged-files do-commit-intro
codestyle: intro do-cs-ecs
codestyle-fix: intro do-cs-ecs-fix

# ===========================
# Overview of commands
# ===========================

help:
@echo "\n=== Make commands ===\n"
@echo "Dependencies"
@echo " make install Make the project ready for development."
@echo " make update Update backend and frontend dependencies."
@echo " make reset Reinstall backend and frontend dependencies."
@echo "\nTests"
@echo " make tests Run tests."
@echo " make test-unit Run unit tests."
@echo "\nDevelopment"
@echo " make codestyle Check if the codestyle is OK."
@echo " make codestyle-fix Check and fix your messy codestyle."

# ===========================
# Recipes
# ===========================

# Dependencies
do-composer-install:
@echo "\n=== Installing composer dependencies ===\n"\
COMPOSER_MEMORY_LIMIT=-1 composer install

do-composer-update:
@echo "\n=== Updating composer dependencies ===\n"\
COMPOSER_MEMORY_LIMIT=-1 composer update

# Development
do-commit-intro:
@echo "\n=== Let's ship it! ===\n"

do-lint-staged-files:
@node_modules/.bin/lint-staged

do-cs-ecs:
./vendor/bin/ecs check --config=dev/easy-coding-standard.yml

do-cs-ecs-fix:
./vendor/bin/ecs check --fix --config=dev/easy-coding-standard.yml

# Project
do-assets-install:
@echo "\n=== Installing npm dependencies ===\n"
npm install

# Tests
do-test-unit:
@echo "\n=== Running unit tests ===\n"
vendor/bin/phpunit

do-test-report:
@echo "\n=== Click the link below to see the test coverage report ===\n"
@echo "report/index.html"
168 changes: 168 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
# Statinator

[![CI/CD][ico-actions]][link-actions]

Next-gen PHP state machines and state charts.

## Installation

`composer require jeroen-g/statinator`

## Usage

Imagine you want to create a state machine for a light switch in which the light can be in the state ON or OFF.
A diagram would look as follows:

![diagram](docs/lights-diagram.png)

_Source: [statecharts.github.io](https://statecharts.github.io/)_

The Statinator configuration for this state machine would look like this:

```php
$config = [
'states' => [
'LIGHTS_ON',
'LIGHTS_OFF',
],
'transitions' => [
'FLICK_ON' => [
'from' => ['LIGHTS_OFF'],
'to' => ['LIGHTS_ON'],
],
'FLICK_OFF' => [
'from' => ['LIGHTS_ON'],
'to' => ['LIGHTS_OFF'],
],
],
];
```

Then in your application you should instantiate Statinator with this configuration.

```php
use JeroenG\Statinator\Statinator;
$statinator = new Statinator($config);
```

The Statinator accepts a second parameter as a repository implementing the `ActionRepositoryInterface`,
this (optionally) stores successful and failed actions.

Any object that you want to use with a state machine should implement the `StatableInterface` contract.
For the example of the light switch this might be:

```php
use JeroenG\Statinator\StatableInterface;

class LightSwitch implements StatableInterface {
private string $currentState;

public function __construct(string $initialState)
{
$this->currentState = $initialState;
}

public function getState(): string
{
return $this->currentState;
}

public function setState(string $to): void
{
$this->currentState = $to;
}
}
```

Anywhere in your code, you may use the Statinator instance to get a state machine and interact with its state and transitions.

```php
$lightSwitch = new LightSwitch('LIGHTS_OFF');
$sm = $statinator->get($lightSwitch);

$sm->getState(); // LIGHTS_OFF
$sm->can('FLICK_ON'); // true
$sm->can('FLICK_OFF'); // false

$sm->apply('FLICK_ON');

$sm->getState(); // LIGHTS_ON
$sm->can('FLICK_ON'); // false
$sm->can('FLICK_OFF'); // true
```

While seeing objects move from one state to another is quite cool, the real power lies with the actions that you may define
for transitions.

Before and after a state changes, the state machine executes any available actions. You can define actions as part of the
global configuration, or by using a setter on the Statinator.

```php
// Using the configuration
$config = [
// ...
'transitions' => [
'FLICK_ON' => [
'from' => ['LIGHTS_OFF'],
'to' => ['LIGHTS_ON'],
'on' => [
'entry' => NotifyLightsAreOn::class,
'exit' => NotifyLightsAreOff::class,
],
],
// ...
],
];

// Or using a setter
$statinator->onEntry('FLICK_ON', NotifyLightsAreOn::class);
$statinator->onExit('FLICK_ON', NotifyLightsAreOff::class);
```

The actions can be a `callable` or a class implementing `ActionableInterface`.

```php
$statinator->onEntry('FLICK_OFF', fn(StateMachineInterface $stateMachine, string $transition) => var_dump('Called it!'));
````

```php
use JeroenG\Statinator\ActionableInterface;
use JeroenG\Statinator\StateMachineInterface;

class NotifyLightsAreOn implements ActionableInterface {
private StateMachineInterface $stateMachine;
private string $transition;

public function getState(): string
{
return $this->stateMachine->getState();
}

public function execute(StateMachineInterface $stateMachine, string $transition): void
{
$this->stateMachine = $stateMachine;
$this->transition = $transition;

$notifier = new MyNotifier();
$notifier->send('Lights are turned on');
}

public function getTransition(): string
{
return $this->transition;
}
}
```

If your action implements the `ActionableInterface` it could be saved using a repository that is passed when you instantiated Statinator.
This package ships with an `ArrayActionRepository` that is used by default and does not persist the data. Another possibility
is the `LogActionRepository`, this one requires a PSR-compliant logger where the data will be persisted.
You can of course also make your own repository (maybe one that uses the database) as long as it implements the `ActionRepositoryInterface`.

## Contributing

The project includes a Makefile to run install it, run the tests and check the code style.


[link-actions]: https://github.com/Jeroen-G/alpine-artisan/actions?query=workflow%3ACI%2FCD
[ico-actions]: https://img.shields.io/github/workflow/status/Jeroen-G/alpine-artisan/CI?label=CI%2FCD&style=flat-square
30 changes: 30 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "jeroen-g/statinator",
"description": "Next-gen PHP state machines",
"type": "library",
"license": "MIT",
"authors": [
{
"name": "Jeroen",
"email": "[email protected]"
}
],
"require": {
"php": "^7.4"
},
"require-dev": {
"phpunit/phpunit": "^9.2",
"symplify/easy-coding-standard": "^8.0"
},
"autoload": {
"psr-4": {
"JeroenG\\Statinator\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"JeroenG\\Statinator\\Tests\\": "tests/"
},
"classmap": ["src/"]
}
}
Loading

0 comments on commit 7490167

Please sign in to comment.