An open API service indexing awesome lists of open source software.

https://github.com/bedus-creation/automata


https://github.com/bedus-creation/automata

Last synced: about 1 month ago
JSON representation

Awesome Lists containing this project

README

          

# Finite State Machine (FSM)

A generic, reusable, and fluent object-oriented Finite State Machine library implementation in PHP.

This library makes it simple to map mathematical Final Automata logic (with states, inputs, and transitions) directly to robust software.

## Setup and Execution

### Requirements
- PHP 8.3 or higher
- Composer

### Installation
1. Clone the repository to your local machine:
```bash
git clone git@github.com:bedus-creation/automata.git
```
2. Navigate to the project directory and install dependencies via Composer:
```bash
composer install
```
3. Run the `index.php` file to execute the Finite State Machine example for modulo three Example:
```bash
php index.php
```

### Running Tests
The project includes a comprehensive suite of Unit and Integration (as well as Benchmark) tests using PHPUnit.
To execute the tests:
```bash
composer test
```
To run tests with code coverage (requires Xdebug or PCOV):
```bash
composer coverage
```

### Code Quality (Static Analysis & Formatting)
The project is configured with PHPStan (Level 8) and Pint to ensure high code quality.
To analyze the codebase:
```bash
composer analyze
```
To automatically format code to adhere to PSR-12 and formatting rules:
```bash
composer format
```

## Features
- Fully fluent API.
- Closures for flexible state declaration.
- Stores custom data on States for easy result extraction.
- Multibyte sequence (unicode) support (e.g. emojis).
- Validation and Transition Error checking.

## Usage Example: Modulo Three

Here is how you would configure the FSM to evaluate a binary string and find the integer remainder when divided by 3:

```php
use App\FSM;
use App\State;

// 1. Initialize the FSM and define allowed inputs
$fsm = (new FSM())->setInputs(['0', '1']);

// 2. Add states and map out their inputs -> transition targets
$s0 = new State($fsm, 'S0');
$s1 = new State($fsm, 'S1');
$s2 = new State($fsm, 'S2');

// Add transitions
$s0->addTransition('0', $s0)
->addTransition('1', $s1);

$s1->addTransition('0', $s2)
->addTransition('1', $s0);

$s2->addTransition('0', $s1)
->addTransition('1', $s2);

// Set initial state, final states, and add states
$fsm->addState($s0)
->addState($s1)
->addState($s2);

$fsm->setInitialState($s0);
$fsm->addAcceptingStates($s0);

// 3. Process the entire sequence with `run()`
$fsm->run('1101'); // 13 in decimal

// 4. Retrieve the answer current states from the FSM
echo $fsm->currentState; // Outputs S1
```

## State's data
We can attach data of any type to a stage.
```php
$s0->setData(0);
$s1->setData(0);
$s2->setData(1);

// 3. Process the entire sequence with `run()`
$fsm->run('1101'); // 13 in decimal

// 4. Retrieve the data from current state
echo $fsm->currentState->data; // Outputs 1
```

## Dynamic State Creation

By default, the FSM requires states to be explicitly registered via instantiated `State` objects. `addState()` and `findState()` and throws exceptions if the state doesn't exist.

You can enable dynamic state creation by using the `enableDynamicStates()` flag. This allows the FSM to automatically create any target states that don't exist yet, which is incredibly useful when using closures to map out transitions:

```php
$fsm = new FSM();
$fsm->enableDynamicStates();
$fsm->setInputs(['0', '1']);

$fsm->addState('A', function (State $state) {
$state->setIsInitialState();
$state->addTransition('0', 'B'); // 'B' will be dynamically created!
});

$fsm->addState('B', function (State $state) {
$state->addTransition('1', 'A');
});

$fsm->run('010');
echo $fsm->currentState->name; // Outputs 'B'
```

## Unicode and Multibyte Support
This system naturally supports any multibyte Unicode characters, making it quite flexible for text tokenizing or complex emoji state handling:

```php
$fsm = new FSM();
$fsm->enableDynamicStates();
$fsm->setInputs(['😎', '🔥']);

$fsm->addState('Cool State', function(State $state) {
$state->setIsInitialState();
$state->addTransition('😎', 'Fire State');
});

$fsm->addState('Fire State', function(State $state) {
$state->addTransition('🔥', 'Cool State');
});

$fsm->run('😎🔥😎');

echo $fsm->currentState->name; // Outputs 'Fire State'
```

## Re-using without Rebuilding
You don't need to rebuild the object every time you evaluate a sequence! You can just call `reset()`:
```php
$fsm->run('1101');
echo $fsm->currentState->data; // 1

$fsm->reset();

$fsm->run('1111');
echo $fsm->currentState->data; // 0
```

## Performance / Benchmarks

The library is designed to be lightweight and fast. As proven by the included benchmark test suite (`tests/Benchmark/FSMPerformanceTest.php`), an FSM instance can process an input sequence of **500,000 characters** (half a million transitions) in roughly **~0.4 seconds** depending on the hardware.

This makes it extremely scalable for evaluating huge text blocks, bitwise sequences, or high-volume streams perfectly matching the mathematical logic without overhead.