# Home

Sons of PHP has many libraries and projects for developers.

## 🏠 Home

### Welcome to the Sons of PHP

#### Reporting Issues

Please use the main [Sons of PHP Repository](https://github.com/SonsOfPHP/sonsofphp) to report any [issues](https://github.com/SonsOfPHP/sonsofphp/issues) that you find.

#### Contributing

Please submit [Pull Requests](https://github.com/SonsOfPHP/sonsofphp/pulls) to the main [Sons of PHP Repository](https://github.com/SonsOfPHP/sonsofphp).

#### Getting Help

Please visit the [Sons of PHP Organization Discussions](https://github.com/orgs/SonsOfPHP/discussions) section to ask questions and get more help.

## About Sons of PHP

Sons of PHP is a collection of various PHP projects that are split into "Contracts" and "Components". After that, functionality is extended by using "Bridges". Beyond that are plugins, bundles, and packages that are specifically for various frameworks.

The main focus of Sons of PHP is small re-usable components.

### Contracts

Everything starts with contracts. The contracts are NOT meant to replace PSR standards. Contracts are where all the interfaces are stored. There is no executable code here. Contracts are a way to provide other developers the ability to create components using a standard interface.

Contracts may also extend PSR standards as well as core PHP interfaces.

Contracts are also meant to be standalone. They do not require any other contracts other than PSRs.

### Components

All components will use the contracts to implement the code. These are the main packages that you will use in your projects.

Components are also meant to be standalone. Generally, there will be no additional requirements. Some components may require other additional Sons of PHP components, but for the most part, additional functionality is added by Bridges.

### Bridges

A bridge will connect a Sons of PHP component to another library. For example, the Pager component has additional packages that use Doctrine. The Filesystem component also has an AWS S3 bridge that allows you to use AWS S3 buckets to store files.

### Bundles, Plugins, etc.

Some frameworks out there (Symfony, Laravel, etc.) allow developers to install bundles, plugins, etc. Sons of PHP has a few of these and the leverage the various components and bridges.

### Additional Standalone Projects

Sons of PHP also has standalone projects such as Bard. These projects are include many different Sons of PHP components and bridges, but also include other libraries.


# Contributor Covenant Code of Conduct

<div align="center"><img src="https://raw.githubusercontent.com/SonsOfPHP/.github/main/assets/top-rocker.png" alt=""></div>

***

## Contributor Covenant Code of Conduct

### Our Pledge

We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.

We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.

### Our Standards

Examples of behavior that contributes to a positive environment for our community include:

* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
* Focusing on what is best not just for us as individuals, but for the overall community

Examples of unacceptable behavior include:

* The use of sexualized language or imagery, and sexual attention or advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting

### Enforcement Responsibilities

Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.

Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.

### Scope

This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.

### Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at <joshua@sonsofphp.com>. All complaints will be reviewed and investigated promptly and fairly.

All community leaders are obligated to respect the privacy and security of the reporter of any incident.

### Enforcement Guidelines

Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct:

#### 1. Correction

**Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.

**Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.

#### 2. Warning

**Community Impact**: A violation through a single incident or series of actions.

**Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.

#### 3. Temporary Ban

**Community Impact**: A serious violation of community standards, including sustained inappropriate behavior.

**Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.

#### 4. Permanent Ban

**Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.

**Consequence**: A permanent ban from any sort of public interaction within the community.

### Attribution

This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 2.0, available at <https://www.contributor-covenant.org/version/2/0/code\\_of\\_conduct.html>.

Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity).

For answers to common questions about this code of conduct, see the FAQ at <https://www.contributor-covenant.org/faq>. Translations are available at <https://www.contributor-covenant.org/translations>.


# Getting Help

<div align="center"><img src="https://raw.githubusercontent.com/SonsOfPHP/.github/main/assets/top-rocker.png" alt=""></div>

***

## Ways to get help and Ways to help yourself

Need more help? The [Sons of PHP](https://github.com/SonsOfPHP) have many different ways you can get more help.

### Documentation

This website is basically the *RTFM*.

### Source Code

The ultimate "RTFM". If the documentation on the site is lacking, there are gems in the source code.

### GitHub Discussions

If the documentation and source code have failed you, you can get help by going to the [Sons Of PHP Organization Discussions](https://github.com/orgs/SonsOfPHP/discussions) and searching for answers. If you cannot find any, create a new topic.


# Reporting Issues

<div align="center"><img src="https://raw.githubusercontent.com/SonsOfPHP/.github/main/assets/top-rocker.png" alt=""></div>

***

## Reporting Issues

### Components, Libraries, Projects, etc.

Please use the [Mother Repo](https://github.com/SonsOfPHP/sonsofphp) to report issues with any of the components. Be sure to include as much information as you can so it can get resolved as fast as possible.


# Security Policy

## Supported Versions

| Version | Supported            |
| ------- | -------------------- |
| 0.4.x   | :white\_check\_mark: |
| 0.3.x   | :white\_check\_mark: |
| 0.2.x   | :x:                  |
| 0.1.x   | :x:                  |

## Reporting a Vulnerability

Please review [Tidelift Security Policy](https://tidelift.com/security).


# Overview

Bard is used to manage monorepos.

### Installing

Bard is meant to be installed into a "tools" folder for use with your project.

```shell
mkdir tools/bard
composer require --working-dir=tools/bard sonsofphp/bard
echo "composer.lock\nvendor/" > tools/bard/.gitignore
php tools/bard/vendor/bin/bard
```

### Usage

Initialize a new bard.json file for new monorepos.

```shell
bard init
```

#### Adding Repositories

```shell
bard add path/to/code repoUrl
```

#### Push changes to read-only repos

```shell
bard push
```

#### Create a release

```shell
bard release major
bard release minor
bard release patch
```

Bard will track the versions so you can just use the keywords: major, minor, patch.

#### Copy files

Copy the LICENSE file from the root to all packages

```shell
bard copy LICENSE
```

#### Merging `composer.json` files

When you have to maintain the composer.json files, this command will take the packages and merge those into the main composer.json file. It will also update the package's composer.json file with the correct values as well.

```shell
bard merge
```


# Commands

All commands have usage examples along with help documentation in the commands. To view more details, run `bard <command> -h`.

### init

Initialize a new mono repository.

```shell
bard init
```

### add

Add a new package to bard.

```sh
bard add <path> <repository>
```

### merge

This command will merge all packages into the root `composer.json` file.

```sh
bard merge [<package>]
```

### push

Pushes code to their respective repositories.

```sh
bard push [<package>]
```

### release

Creates a new release. This command will update all the files that need to be updated, tag, and push the release out.

```sh
bard release <release>
```

{% hint style="success" %}
You can use `major`, `minor`, or `patch` as the `<release>` argument instead of specifying the exact version you want to release.
{% endhint %}


# Chorale

Chorale is a CLI tool for managing PHP monorepos. It uses a plan/apply workflow to keep package metadata and the root package in sync.

## Installation

```bash
cd tools/chorale
composer install
```

## Usage

Run the commands from the project root:

```bash
# create chorale.yaml by scanning packages
php bin/chorale setup

# preview changes without modifying files
php bin/chorale plan --json > plan.json

# apply an exported plan
php bin/chorale apply --file plan.json

# build and apply a plan in one go
php bin/chorale run
```

Chorale automatically merges all package `composer.json` files into the root `composer.json` so the monorepo can be installed as a single package. Any dependency conflicts are recorded under the `extra.chorale.dependency-conflicts` section for review.

## Commands

* `setup` – generate configuration and validate required files.
* `plan` – build a plan for splitting packages and root updates.
* `run` – build and immediately apply a plan.
* `apply` – execute steps from a JSON plan file.


# Feature Toggle

## Installation

Make sure Composer is installed globally, as explained in the [installation chapter](https://getcomposer.org/doc/00-intro.md) of the Composer documentation.

### Applications that use Symfony Flex

Open a command console, enter your project directory and execute:

```sh
composer require sonsofphp/feature-toggle-bundle
```

### Applications that don't use Symfony Flex

#### Step 1: Download the Bundle

Open a command console, enter your project directory and execute the following command to download the latest stable version of this bundle:

```sh
composer require sonsofphp/feature-toggle-bundle
```

#### Step 2: Enable the Bundle

Then, enable the bundle by adding it to the list of registered bundles in the `config/bundles.php` file of your project:

```php
// config/bundles.php

return [
    // ...
    SonsOfPHP\Bundle\FeatureToggleBundle\SonsOfPHPFeatureToggleBundle::class => ['all' => true],
];
```

## Configuration

```yaml
# config/packages/sons_of_php_feature_toggle.yaml
sons_of_php_feature_toggle:
  features:
    # You can create as many features as you want
    enabled_key:
      # Features can be enabled, disabled, or use a custom toggle
      toggle: enabled
    disabled_key:
      toggle: disabled
    custom_toggle_key:
      toggle: app.toggle.admin_users
```

## Debug Command

You can debug your features by running the `debug:features` command.

```sh
php bin/console debug:features
```

This will give you a list of features and the toggles they are using.

## Twig Templates

You can check to see if the feature is enabled in twig templates by using the `is_feature_enabled` function.

```twig
{% if is_feature_enabled('enabled_key') %}
    Feature "enabled_key" is enabled
{% else %}
    Feature "enabled_key" is disabled
{% endif %}
```

## Services

```php
<?php

use SonsOfPHP\Contract\FeatureToggle\FeatureToggleProviderInterface;

class ExampleService
{
    public function __construct(
        private FeatureToggleProviderInterface $featureToggleProvider,
    ) {}
    
    public function doSomething()
    {
        if ($featureToggleProvider->get('enabled_key')->isEnabled()) {
            // "enabled_key" is enabled
        }
    }
}
```

## Learn More

{% content-ref url="/pages/0pIf16g6dSN4CIRzJFjR" %}
[Feature Toggle](/components/feature-toggle)
{% endcontent-ref %}


# Contracts Overview

Contracts are interfaces that can be reusable across multiple different libraries and projects. The actual implementation of the interfaces is left up to you.

The interfaces also enhance existing PSRs.

Whenever possible, the components and projects created by Sons of PHP will implement these interfaces.

## Implementation

If you want to provide a concrete library for others to use, add this to your `composer.json` file.

```json
    "provide": {
        "sonsofphp/common-implementation": "^1.0"
    },
```

## Method Naming

Functionality around method names are kept as similar as possible. They SHOULD follow this convention.

### with\*

`with*` methods are meant to be used on value objects. They will return a new object with the value(s) modified.

Examples:

* `withFirstName`

### has\*

Will always return a `boolean` value. Used to check if a property or value exists on the object. Doesn't matter the type of the value.

Examples

* `hasFirstName`

### is\*

Will always return a `boolean` value.

Examples:

* `isNew`
* `isDeleted`

### get\*

Used to return property values.

Examples:

* `getFirstName`
* `getLastName`

### set\*

Used to set property values. SHOULD always return the same instance of the object to allow chaining.

Examples:

* `setFirstName`
* `setLastName`

### to\*

Used to convert an object to a specific format.

Examples:

* `toJson`
* `toString`
* `toArray`
* `toInteger`
* `toFloat`
* `toBoolean`


# Common

PHP Common Contracts

Common Contracts are generic contracts that are not specific to any one package or project.

## Installation

```shell
composer require sonsofphp/common-contract
```

## Includes Interfaces

* `ArrayableInterface`
* `ComparableInterface`
* `EquatableInterface`
* `JsonableInterface`
* `TryableInterface`


# Cookie

```shell
composer require sonsofphp/cookie-contract
```


# CQRS

In a nut shell, with CQRS you have Commands that will change the state of your application and Queries that will return information about the state of your application.

### Installation

```shell
composer require sonsofphp/cqrs-contract
```

### Messages

Both Commands and Queries are considered Messages and are handled by Message Handlers.

### Message Handlers

When a message is dispatched/handled, it is handled by a message handler.

### Message Bus

The Message Bus is given a Message and will use a Message Handler to handle the message. If it's a Query Message, the bus will return a result and for Command Messages, it will not return anything.

### Message Handler Provider

Returns the Handler that will handle the Message. They do not actually handle or process the message.


# Filesystem

```shell
composer require sonsofphp/filesystem-contract
```


# Mailer

### Installation

```shell
composer require sonsofphp/mailer-contract
```

### Definitions

* **Message** - The email message that will be sent.
* **Mailer** - Primary API used to send *messages*.
* **Transport** - Transports are what will actually send the message. This could be SMTP, Null, SendGrid, Amazon SES, or anything like that.
* **Middleware** - Before the message is sent to the transport to be sent, various middlewares have the ability to modify the message.


# Pager

The PHP Pager Contract is to support libraries that implement Pagination related components.

### Installation

```shell
composer require sonsofphp/pager-contract
```

### AdapterInterface

```php
<?php

namespace SonsOfPHP\Contract\Pager;

interface AdapterInterface extends \Countable
{
    /**
     * This will return part of the total results
     *
     * Offset is where the results to be returned will start, for example, if
     * offset is 0, it will start with the first record and return $length
     *
     * Offset should always be 0 or greater
     *
     * Length is how many results to return. For example, if length is 10, a
     * MAXIMUM of 10 results will be returned
     *
     * Length MUST always be a positive number that is 1 or greater OR be null
     *
     * If null is passed in as length, this should return ALL the results.
     *
     * If the total number of results is less than length, an exception must
     * not be thrown.
     *
     * @throws \InvalidArgumentException
     *   If offset or length is invalid, this expection will be thrown
     */
    public function getSlice(int $offset, ?int $length): iterable;
}
```


# Registry

```shell
composer require sonsofphp/registry-contract
```


# State Machine

```shell
composer require sonsofphp/state-machine-contract
```


# Assert

## Installation

```shell
composer require sonsofphp/assert
```

## Usage

```php
<?php

use SonsOfPHP\Component\Assert\Assert;
use SonsOfPHP\Component\Assert\InvalidArgumentException;

$data = 'test';
Assert::string($data);

// @throws InvalidArgumentException
Assert::int($data);

// Disable exceptions
Assert::disable();

// @return bool
Assert::int($data);

// Re-enable exceptions
Assert::enable();
```

## Assertions

```php
Assert::array($value);
Assert::bool($value);
Assert::callable($value);
Assert::empty($value);
Assert::eq($value, $value2);
Assert::false($value);
Assert::float($value);
Assert::int($value);
Assert::null($value);
Assert::numeric($value);
Assert::object($value);
Assert::resource($value);
Assert::same($value, $value2);
Assert::scalar($value);
Assert::string($value);
Assert::true($value);
```

## Magic Assertions

```php
// "all"
// Throws exception if any value in $values is not the correct type
Assert::allString($values);
Assert::allString(['one', 'two', 3]); // Throws Exception

// "not"
// Throws exception if $value is the type
Assert::notString($value);
Assert::notString('opps'); // Throws Exception

// "nullOr"
// Throws exception if $value is not null or not type
Assert::nullOrString($value);
Assert::nullOrString(42); // Throws Exception
```


# Cache

### Installation

```shell
composer require sonsofphp/cache
```

#### PSR-16 - Simple Cache

To add support for PSR-16 Simple Cache, require the PSR.

```shell
composer require psr/simple-cache
```

### Usage

```php
<?php

use SonsOfPHP\Component\Cache\Adapter\ApcuAdapter;

$pool = new ApcuAdapter();
```

All Adapters implement `Psr\Cache\CacheItemPoolInterface`

#### PSR-16 Simple Cache Wrapper

```php
<?php

use SonsOfPHP\Component\Cache\Adapter\ApcuAdapter;
use SonsOfPHP\Component\Cache\SimpleCache;

$pool = new ApcuAdapter();
$cache = new SimpleCache($pool);
```

`SimpleCache` implements `Psr\SimpleCache\CacheInterface`.

#### Setting up a multi layer cache

```php
<?php

use SonsOfPHP\Component\Cache\Adapter\ArrayAdapter;
use SonsOfPHP\Component\Cache\Adapter\ApcuAdapter;
use SonsOfPHP\Component\Cache\Adapter\ChainAdapter;

$pool = new ChainAdapter([
    new ArrayAdapter(),
    new ApcuAdapter(),
]);
```

The `ChainAdapter` will read from all pools and return the first result it finds. So in the above example, if the cache item is not found in `ArrayAdapter`, it will look for it in the `ApcuAdapter` pool.

The `ChainAdapter` will also write to and delete from all pools.


# Adapters

All adapters implement the PSR-6 `\Psr\Cache\CacheItemPoolInterface` and can be used as standalone cache pools.

## ApcuAdapter

Requires the APCu extension is loaded and enabled.

```php
<?php

use SonsOfPHP\Componenet\Cache\Adapter\ApcuAdapter;

$cache = new ApcuAdapter();

// You can set a default TTL in seconds and Marshaller as well.
$cache = new ApcuAdapter(60, new CustomMarshaller());
```

## ArrayAdapter

Stores cache items in an internal PHP array.

```php
<?php

use SonsOfPHP\Componenet\Cache\Adapter\ArrayAdapter;

$cache = new ArrayAdapter();
```

## ChainAdapter

The Chain Adapter will take one or more adapters. It will WRITE to all adapters and will READ from each adapter until it finds a hit and return that cache item.

```php
<?php

use SonsOfPHP\Componenet\Cache\Adapter\ApcuAdapter;
use SonsOfPHP\Componenet\Cache\Adapter\ArrayAdapter;
use SonsOfPHP\Componenet\Cache\Adapter\ChainAdapter;

$cache = new ChainAdapter([
    new ArrayAdapter(),
    new ApcuAdapter(),
]);
```

## FilesystemAdapter

Stores cache files on disk

```php
<?php

use SonsOfPHP\Componenet\Cache\Adapter\FilesystemAdapter;

$cache = new FilesystemAdapter();

// You can configure the directory, default permissions, default ttl, and
// marshaller
$cache = new FilesystemAdapter('/path/to/cache', 0777, 60, new CustomMarshaller());
```

## NullAdapter

Mainly used for testing, however you could have some checks in place and if those checks fail, you could fallback to this adapter.

```php
<?php

use SonsOfPHP\Componenet\Cache\Adapter\NullAdapter;

$cache = new NullAdapter();
```


# Marshallers

## Marshallers

Using marshallers will allow you to serialize the cache values differently.

### SerializableMarshaller

This marshaller will use php's native `serialize` and `unserialize` functions on the values.

```php
<?php

use SonsOfPHP\Componenet\Cache\Marshaller\SerializableMarshaller;

$marshaller = new SerializableMarshaller();
```

### JsonMarshaller

The json marshaller will use php's native `json_encode` and `json_decode` functions to serialize data.

```php
<?php

use SonsOfPHP\Componenet\Cache\Marshaller\JsonMarshaller;

$marshaller = new JsonMarshaller();
```

## Custom Marshaller

You can create a custom marshaller. For example, you could create one the encrypts/decrypts the values.


# Clock

The Clock Component is a wrapper around PHP's native DateTime objects and functions. Using the Clock Component helps you test and keep everything a standard timezone

### Installation

```shell
composer require sonsofphp/clock
```

### Usage

#### SystemClock

The `SystemClock` is generally used in production. If you need check timestamps or manage dates and times, this is the Clock you want to use. Use Dependency Injection to make it easy to test with.

```php
<?php

use SonsOfPHP\Component\Clock\SystemClock;

$clock = new SystemClock();

$now = $clock->now(); // Returns a DateTimeImmutable object
```

#### FixedClock

The `FixedClock` is used for testing. Just set the time and inject into your class.

```php
<?php
use SonsOfPHP\Component\Clock\FixedClock;

$clock = new FixedClock();
$firstNow = $clock->now();
sleep(10);
$secondNow = $clock->now();

var_dump($firstNow === $secondNow); // true

// You can update the internal time at any time.
$clock->tick(); // Updates the internal clock to the curent time
$thridNow = $clock->now();

var_dump($firstNow === $secondNow); // still true
var_dump($firstNow === $thirdNow); // false
var_dump($thirdNow === $secondNow); // false

// You can also set the internal clock to whatever time you need it to be
$clock->tickTo('2022-04-20 04:20:00'); // format is "Y-m-d H:i:s"
```


# Container

PSR-11 Container

### Installation

```shell
composer require sonsofphp/container
```

### Usage

```php
<?php

use SonsOfPHP\Component\Container\Container;
use Psr\Container\ContainerInterface;

$container = new Container();
$container->set('service.id.one', function (ContainerInterface $container) {
    return new Service();
});
$container->set('service.id.two', function (ContainerInterface $container) {
    return new Service($container->get('service.id.one'));
});

// Services will not be created until they are called, once called, they will
// always return the same instance of the service. That means that in the
// following code, the "service.id.two" is only constructed once.
$service  = $container->get('service.id.two');
$service2 = $container->get('service.id.two');
```


# Cookie

### Installation

```shell
composer require sonsofphp/cookie
```

### Usage

A Cookie is treated as a value object. This means that if two cookie objects have the same name and value, they will be considered equal. They are also considered to be immutable.

```php
<?php

use SonsOfPHP\Component\Cookie\Cookie;

$cookie = new Cookie('name', 'value');

header('Set-Cookie: ' . $cookie->getHeaderValue());
// OR
// header('Set-Cookie: ' . (string) $cookie);

// Set various attributes
$cookie = $cookie
    ->withPath('/')
    ->withDomain('docs.sonsofphp.com')
;
```


# CQRS

Command Query Responsibility Segregation is a simple concept. When you want something to happen, you will use a Command. If you need results returned, you will use a query.

An over simplified example of this would be if you need to write to a database, you would use a Command. If you need results from a database, you would use a Query.

### Installation

```shell
composer require sonsofphp/cqrs
```

### Basic Usage

#### Command Bus

```php
<?php

use SonsOfPHP\Component\Cqrs\Command\CommandBus;

$commandBus = new CommandBus();
$commandBus->addHandler(CreateUser::class, $handler);

$command = new CreateUser();
$commandBus->dispatch($command);
```

!!! success "Symfony CQRS Bridge" Once the CQRS Symfony Bridge is installed, you can use the Command Bus that comes with that to gain access to additional features and functionality.

#### Query Bus

```php
<?php

use SonsOfPHP\Component\Cqrs\Query\QueryBus;

$queryBus = new QueryBus();
$queryBus->addHandler(GetUser::class, $handler);

$query = (new GetUser())->with('id', 123);
$user  = $queryBus->handle($query);
```

!!! success "Symfony CQRS Bridge" Once the CQRS Symfony Bridge is installed, you can use the Query Bus that comes with that to gain addition features and functionality.

### Messages

Both Commands and Queries are considered to be messages. It is HIGHLY recommended to use the `AbstractMessage` class for both Commands and Queries. For all examples I will assume you have extended this class.

```php
<?php

use SonsOfPHP\Component\Cqrs\AbstractMessage;

class CreateUser extends AbstractMessage {}

$command = new CreateUser();
$command = $command->with('id', 123);
$command = $command->with('email', 'joshua@sonsofphp.com');

$userId  = $command->get('id'); // $userId = 123
```

```php
<?php

use SonsOfPHP\Component\Cqrs\AbstractMessage;

class GetUser extends AbstractMessage {}

$query = new GetUser();
$query = $query->with('id', 123);

$userId = $query->get('id'); // $userId = 123
```

!!! note `AbstractMessage` treats the message as a value object. So using `with` will return a new instance of the class.

#### Additional `AbstractMessage` API

```php
<?php

use SonsOfPHP\Component\Cqrs\AbstractMessage;

class Message extends AbstractMessage {}

// Create a new instance with multiple paramters
$message = (new Message())->with([
    'key'     => 'value',
    'another' => 'value',
    // ...
]);

// Getting all the paramters of the message
$parameters = $message->get();

// Get a single paramter value
// WARNING: If the parameter is not found, an exception will be thrown
$userId = $message->get('user.id');
```

### Message Handlers

Both Command and Query Handers are assumed to just be message handlers.

#### Command Handlers

```php
<?php

use SonsOfPHP\Contract\Cqrs\Command\CommandBusInterface;

class CreateUserHandler
{
    // First argument will be the Command (ie Message)
    // Second argument is the command bus
    public function __invoke(CreateUser $command, CommandBusInterface $bus): void
    {
        // ...
    }
}
```

#### Query Handlers

```php
<?php

use SonsOfPHP\Contract\Cqrs\Query\QueryBusInterface;

class GetUserHandler
{
    // First argument will be the Query (ie Message)
    // Second argument is the query bus
    public function __invoke(GetUser $query, QueryBusInterface $bus): ?UserInterface
    {
        // ...
    }
}
```

### Symfony Bridge

The Symfony Bridge uses Symfony Components to add additional functionality to the CQRS component.

#### Installation

```shell
composer require sonsofphp/cqrs-symfony
```


# Event Dispatcher

Event Dispatcher will allow you to attach listeners and notify those listeners so they can handle the event that you dispatch. The Event Dispatcher is PSR-14 Compatible with extra features.

## Features

* Event Names
* Event Subscribers
* Listener Priorities

## Installation

```shell
composer require sonsofphp/event-dispatcher
```

## Usage

```php
<?php

use SonsOfPHP\Component\EventDispatcher\EventDispatcher;

$dispatcher = new EventDispatcher();

// If you have a custom ListenerProviderInterface you can inject it into the
// EventDispatcher
//$dispatcher = new EventDispatcher($provider);

$dispatcher->addListener($event::class, function ($event, $eventName, $dispatcher) {});
$dispatcher->addListener('event.name', function ($event, $eventName, $dispatcher) {});
$dispatcher->addSubscriber($subscriber);

$dispatcher->dispatch($event); // PSR-14
$dispatcher->dispatch($event, 'event.name'); // Custom Event Name
```

### Event Subscribers

Must implement `EventSubscriberInterface`.

### Listener Priorities

```php
<?php

$dispatcher->addListener('event.name', function () {}, $priority);
```

The priority will default to `0`. Lower numbers are higher priority. Higher numbers will be handled later. For example, a listener with a priority of `-1` will be handled before a listener of priority `1`.

### Stoppable Events

If your code extends the `AbstractStoppableEvent` and within your listener or subscriber code, you execute `$this->stopPropagation();` and it will return the event and no more listeners or subscribers will handle that event.

```php
<?php

use SonsOfPHP\Component\EventDispatcher\AbstractStoppableEvent;

class OrderCreated extends AbstractStoppableEvent
{
    // ...
}

class OrderListener
{
    public function __invoke(OrderCreated $event): void
    {
        // ...
        $event->stopPropagation();
        // ...
    }
}
```

You can also create Stoppable Events by using the `StoppableEventInterface` and `StoppableEventTrait`.

```php
<?php

use SonsOfPHP\Component\EventDispatcher\StoppableEventTrait;
use Psr\EventDispatcher\StoppableEventInterface;

class OrderCreated implements StoppableEventInterface
{
    use StoppableEventTrait;

    // ...
}
```

## Creating an Event Listener

```php
<?php

use Psr\EventDispatcher\EventDispatcherInterface;

class OrderListener
{
    public function __invoke(OrderCreated $event, string $eventName, EventDispatcherInterface $dispatcher): void
    {
        // ...
    }
}
```

The dispatcher will always invoke the Listener with those three arguments in that order. If you do not need to know the event name or if you do not need the event dispatcher, you can ignore those two arguments.

## Creating an Event Subscriber

Subscribers allow you to "subscribe" to multiple events.

```php
<?php

use OrderCreated;
use OrderUpdated;
use OrderDeleted;
use SonsOfPHP\Component\EventDispatcher\EventSubscriberInterface;

class OrderSubscriber implements EventSubscriberInterface
{
    // ...

    public static function getSubscribedEvents()
    {
        // Can return like this:
        yield OrderCreated::class => 'onCreated';
        yield OrderUpdated::class => ['onUpdated', 100];
        yield OrderDeleted::class => [['onDeleted', 100], ['doFirstOnDeleted', -100]];

        // OR like this
        return [
            OrderCreated::class => 'onCreated',
            OrderUpdated::class => ['onUpdated', 100],
            OrderDeleted::class => [['onDeleted', 100], ['doFirstOnDeleted', -100]],
        ];
    }
}
```


# Event Sourcing

Using the Event Sourcing Component allows you to manage an Event Sourced application.

## Installation

```shell
composer require sonsofphp/event-sourcing
```

### Additional Features

Additional features and functionality can be enabled by installing additional packages.

#### Doctrine

Installing the Doctrine Bridge will allow the storage of event messages in a database.

```shell
composer require sonsofphp/event-sourcing-doctrine
```

#### Symfony

Installing the Symfony Bridge will add addition functionality such as allowing events to be sent asynchronously using the Messenger Component. It allows has options to generate UUID/ULID for Aggregates.

```shell
composer require sonsofphp/event-sourcing-symfony
```


# Aggregates

Aggregates are the primary objects that you will be working with.

## Aggregate ID

Each Aggregate will need to have it's own unique ID. You can use the same AggregateId class for all your aggregates or you can make you own for each one to ensure data consistency.

### Working with an Aggregate ID

```php
<?php
use SonsOfPHP\Component\EventSourcing\Aggregate\AggregateId;

// Create a new Aggregate ID
$aggregateId = new AggregateId('my-unique-id');

// You can get the value of the ID
$id = $aggregateId->toString();
$id = (string) $aggregateId;

// To compare two Aggregate IDs
if ($aggregateId->equals($anotherAggregateId)) {
    // they are the same
}
```

If you need to create an Aggregate ID Class in order to use type-hinting, just extend the `AbstractAggregateId` class.

```php
<?php
use SonsOfPHP\Component\EventSourcing\Aggregate\AbstractAggregateId;

final class UserId extends AbstractAggregateId
{
}
```

#### Auto Generate Aggregate IDs

!!! attention This requires the installation of the `sonsofphp/event-sourcing-symfony` package.

Creating your own implementation to generate UUIDs or other type of IDs can be a pain in the ass. Once the `sonsofphp/event-sourcing-symfony` package is installed, you can easily use Symfony's Uid Component to generate UUIDs for you.

```php
<?php
use SonsOfPHP\Bridge\Symfony\EventSourcing\AggregateId;
use Symfony\Component\Uid\Uuid;
use Symfony\Component\Uid\Ulid;

$id = new AggregateId();

Uuid::isValid($id->toString()); // true

// It also still supports setting the ID via the constructor
$ulid = new AggregateId((string) new Ulid());
```

## Aggregate Version

An Aggregate also has a version. Each event that is raised will increase the version. You will generally not have to work much with versions as they are mostly handled internally.

### Working with an Aggregate Version

```php
<?php
use SonsOfPHP\Component\EventSourcing\Aggregate\AggregateVersion;

$aggregateVersion = new AggregateVersion();

// Get the version
$version = $aggregateVersion->toInt();

// Comparing Versions
if ($aggregateVersion->equals($anotherAggregateVersion)) {
    // they are the same
}
```

## Creating an Aggregate

Pretty simple to create an aggregate.

```php
<?php
use SonsOfPHP\Component\EventSourcing\Aggregate\AbstractAggregate;

final class UserAggregate extends AbstractAggregate
{
}
```

Using the `AbstractAggregate` class takes care of all the heavy lifting. This allows you to focus on the different methods.

```php
<?php
use SonsOfPHP\Component\EventSourcing\Aggregate\AggregateId;

// Creating a new aggregate can be done by passing either a string
$userAggregate = new UserAggregate('unique-id');

// Or an object that implements `AggregateIdInterface`
$userAggregate = new UserAggregate(new AggregateId('unique-id'));
```

## Working with Event Messages

When you create an [Event Message](/components/event-sourcing/event-messages) you will need to raise that event within your aggregate and if need be, apply that event to the aggregate. Let me show you what I mean.

```php
<?php
use SonsOfPHP\Component\EventSourcing\Aggregate\AbstractAggregate;
use SonsOfPHP\Component\EventSourcing\Message\AbstractSerializableMessage;

final class EmailUpdated extends AbstractSerializableMessage
{
    public function getEmail(): string
    {
        return $this->getPayload()['email'];
    }
}

final class UserAggregate extends AbstractAggregate
{
    private $email;

    public function setEmail(string $email)
    {
        $this->raiseEvent(EmailUpdated::new()->withPayload([
            'email' => $email,
        ]);
    }

    protected function applyEmailUpdated(EmailUpdated $message): void
    {
        $this->email = $message->getEmail();
    }
}
```

This is a very simplistic example. We should be doing a few checks on the email before the event is raised. What happens if we try to set an email and it's the same email as the user currently has? It would continue to raise an event.

You will also notice the `applyEmailUpdated` method on the class. This is optional, but when you raise an event, the `AbstractAggregate` will look for a method in the format of `apply{EventMessageClassname}`. So if the event classname was `DrankABeer` it would look for the method `applyDrankABeer` and if there is one, it will pass in the `DrankABeer` event message.


# Aggregate Repository

The `AggregateRepository` is the main interface used to persist and find aggregates.

## Usage

```php
<?php
use SonsOfPHP\Component\EventSourcing\Aggregate\Repository\AggregateRepository;
use SonsOfPHP\Component\EventSourcing\Aggregate\AggregateIdInterface;
use SonsOfPHP\Component\EventSourcing\Aggregate\AggregateInterface;
use SonsOfPHP\Component\EventSourcing\Message\Repository\MessageRepositoryInterface;
use Psr\EventDispatcher\EventDispatcherInterface;

$repository = new AggregateRepository(
    $aggregateClass, // @var string
    $eventDispatcher, // @var EventDispatcherInterface
    $messageRepository // @var MessageRepositoryInterface
);

// @var AggregateIdInterface    $aggregateId
// @var AggregateInterface|null $aggregate
$aggregate = $repository->find($aggregateId);

// You can also pass in a string as the $aggregateId
$aggregate = $repository->find('unique-id');

// To save an aggregate
$repository->persist($aggregate);
```


# Event Messages

Event Messages are what Aggregates will raise and what the Event Message Handlers will handle. Event Messages will also be stored in whatever storage backend you decide to use. They are used to rebuild the Aggregates as well. So as you can see, Event Messages are a big part of Event Sourcing!

Event Messages consist of 1) Metadata and 2) Payload. The Metadata contains information about the event and the aggregate. The payload is the data that is passed in and used to rebuild the aggregates.


# Using the Serializable Event Message

The `AbstractSerializableMessage` allows you to quickly create Event Messages that are dispatched.

## Usage

### Event Message Class

First you will need to create your Event Message Class.

```php
<?php
use SonsOfPHP\Component\EventSourcing\Message\AbstractSerializableMessage;

class AggregateFieldChangedEventMessage extends AbstractSerializableMessage
{
}
```

### Initializing Event Message Class

Once you create you Event Message Class, using it is very easy.

```php
<?php
$event = AggregateFieldChangedEventMessage::new()->withPayload([
    'previous_value' => $previousValue,
    'new_value'      => $newValue,
]);
```


# Message Enrichers

Message Enrichers will add Metadata to the Event Messages. This metadata could be information about the user, the http request, etc. Internally, the library will use the metadata to insert data about the aggregate and the event.


# Message Serializers

Message Serializers will `serialize` an Event Message into Event Data and `deserialize` Event Data into an Event Message. These are generally used by the Message Repository classes and `serialize` is done before the event message is persisted to storage and `deserialize` is done on the data that is pulled out of the database.


# Message Repository

The message repository will store the event messages where and how you want them to be stored. This is usually a database but could be anywhere. The [Aggregate Repository](/components/event-sourcing/aggregates/aggregate-repository) uses the Message Repository to save the event messages that were raised.

## Usage

```php
<?php

use SonsOfPHP\Component\EventSourcing\Message\Repository\MessageRepositoryInterface;
use SonsOfPHP\Component\EventSourcing\Message\MessageInterface;
use SonsOfPHP\Component\EventSourcing\Aggregate\AggregateIdInterface;
use SonsOfPHP\Component\EventSourcing\Aggregate\AggregateVersionInterface;

// @var MessageRepositoryInterface $messageRepository
// @var MessageInterface $message
$messageRepository->persist($message);

// @var AggregateIdInterface|string $aggregateId
// @var MessageInterface[] $eventMessages
$eventMessages = $messageRepository->find($aggregateId);

// or to grab event messages after a specific version
// @var AggregateVersionInterface|int $aggregateVersion
$eventMessages = $messageRepository->find($aggregateId, $aggregateVersion);
```

## InMemoryMessageRepository

This repository is mainly used for testing, however to keep the examples easy we will start with this message repository.

```php
<?php

use SonsOfPHP\Component\EventSourcing\Message\Repository\InMemoryRepository;

$messageRepository = new InMemoryRepository();
```

## DoctrineDbalMessageRepository

!!! attention The `DoctrineDbalMessageRepository` requires the `sonsofphp/event-sourcing-doctrine` package.

The `DoctrineDbalMessageRepository` allows you to persist event messages in the database.

```php
<?php

use SonsOfPHP\Bridge\Doctrine\EventSourcing\DoctrineDbalMessageRepository;
use SonsOfPHP\Bridge\Doctrine\EventSourcing\TableSchemaInterface;
use Doctrine\DBAL\Connection;
use SonsOfPHP\Component\EventSourcing\Message\Serializer\MessageSerializerInterface;

$messageRepository = new DoctrineDbalMessageRepository(
    $connection, // @var Connection
    $messageSerializer, // @var MessageSerializerInterface
    $tableSchema // @var TableSchemaInterface
);
```

The `TableSchemaInterface` defines what table to use along with the columns and mapping information. They are easy to work with and create custom schemas.


# Message Upcasters

Message Upcasters will modify raw event data before it is rebuilt back into a message. This is useful in case you want to change the format of event data if you screwed up or if you learned something new.


# Feature Toggle

## Installation

```shell
composer require sonsofphp/feature-toggle
```

## Usage

```php
<?php

use SonsOfPHP\Component\FeatureToggle\Feature;
use SonsOfPHP\Component\FeatureToggle\Provider\InMemoryFeatureToggleProvider;
use SonsOfPHP\Component\FeatureToggle\Toggle\AlwaysEnabledToggle;

// Using a feature toggle provider
$provider = new InMemoryFeatureToggleProvider();
$provider->add(new Feature('feature.example', new AlwaysEnabledToggle()));

$feature = $provider->get('feature.example');

// Checking if the feature is enabled
$isEnabled = $feature->isEnabled();
```

### Advanced Usage

```php
<?php

// ...
use SonsOfPHP\Component\FeatureToggle\Context;

// Different ways to build the context, the context
// is used by the Toggle
$context = new Context([
    'user' => $user,
]);
$context->set('user', $user);
$context['user'] = $user;

$isEnabled = $feature->isEnabled($context);
```

When you create your own toggles, you may need to introduce additional context to the toggle to check if everything should be enabled or disabled. This is where this comes into play at.

#### Chain Toggle

The chain toggle allows you to use many toggles together. If ANY toggle returns `true`, the feature is considered enabled.

```php
<?php

use SonsOfPHP\Component\FeatureToggle\Toggle\AlwaysDisabledToggle;
use SonsOfPHP\Component\FeatureToggle\Toggle\AlwaysEnabledToggle;
use SonsOfPHP\Component\FeatureToggle\Toggle\ChainToggle;

$toggle = new ChainToggle([
    new AlwaysEnabledToggle(),
    new AlwaysDisabledToggle(),
]);

// true because at least ONE is enabled
$isEnabled = $toggle->isEnabled();
```

#### Affirmative Toggle

Similar to the chain toggle, this will only return `true` when ALL toggles are `true`.

```php
<?php

use SonsOfPHP\Component\FeatureToggle\Toggle\AlwaysDisabledToggle;
use SonsOfPHP\Component\FeatureToggle\Toggle\AlwaysEnabledToggle;
use SonsOfPHP\Component\FeatureToggle\Toggle\ChainToggle;

$toggle = new ChainToggle([
    new AlwaysDisabledToggle(),
    new AlwaysEnabledToggle(),
]);

// false because at least ONE is disabled
$isEnabled = $toggle->isEnabled();
```

#### Date Range Toggle

The date range toggle will return `true` if it's within a given time range.

```php
<?php

use SonsOfPHP\Component\FeatureToggle\Toggle\DateRangeToggle;

$toggle = new DateRangeToggle(
    start: new \DateTimeImmutable('2024-01-01'),
    end: new \DateTimeImmutable('2024-12-31'),
);

// ...
```

### Create your own Toggle

Take a look at how some of the other toggles are implemented. Creating your own toggles are very easy. You just need to make sure they implement the interface `ToggleInterface`.

```php
<?php

use SonsOfPHP\Contract\FeatureToggle\ContextInterface;
use SonsOfPHP\Contract\FeatureToggle\ToggleInterface;

class MyCustomToggle implements ToggleInterface
{
    public function isEnabled(?ContextInterface $context = null): bool
    {
        // ...
    }
}
```

Once you make your custom toggle, you can use it just like all the rest of the toggles.


# Filesystem

This filesystem is more of a virtual filesystem that allows you to keep one interface but is able to manage files on multiple backends. For example, you could use the `NativeAdapter` to write files to disk and during testing, you can use the `NullAdapter` so no files are actually written to disk.

## Installation

```shell
composer require sonsofphp/filesystem
```

## Usage

```php
<?php

use SonsOfPHP\Component\Filesystem\Adapter\NativeAdapter;
use SonsOfPHP\Component\Filesystem\Filesystem;

$filesystem = new Filesystem(new NativeAdapter('/tmp'));

// Write File
$filesystem->write('example.txt', 'Contents to write');

// Read File contents
$content = $filesystem->read('example.txt');

// Delete File
$filesystem->delete('example.txt');

// Check if file exists
$doesExist = $filesystem->exists('example.txt');

// Get MIME Type of file
$mimeType = $filesystem->mimeType('example.txt');

// Copy file
$filesystem->copy('source.txt', 'destination.txt');

// Move file
$filesystem->move('source.txt', 'destination.txt');
```

## Support for LiipImagineBundle

```shell
composer require sonsofphp/filesystem-liip-imagine
```

```yaml
# config/services.yaml
services:
    SonsOfPHP\Contract\Filesystem\Adapter\AdapterInterface:
        class: SonsOfPHP\Component\Filesystem\Adapter\NativeAdapter
        arguments: ['%kernel.project_dir%/var/data/%kernel.id%']
    SonsOfPHP\Contract\Filesystem\FilesystemInterface:
        class: SonsOfPHP\Component\Filesystem\Filesystem
        arguments: ['@SonsOfPHP\Contract\Filesystem\Adapter\AdapterInterface']
    imagine.cache.resolver.sonsofphp:
      class: SonsOfPHP\Bridge\LiipImagine\Filesystem\Imagine\Cache\Resolver\SonsOfPHPFilesystemResolver
      arguments:
        - '@SonsOfPHP\Contract\Filesystem\FilesystemInterface'
        - 'https://images.example.com'
      tags:
        - { name: "liip_imagine.cache.resolver", resolver: sonsofphp }
```

```yaml
# config/packages/liip_imagine.yaml
liip_imagine:
    data_loader: SonsOfPHP\Bridge\LiipImagine\Filesystem\Binary\Loader\SonsOfPHPFilesystemLoader
    cache: sonsofphp
```

## Need Help?

Check out [Sons of PHP's Organization Discussions](https://github.com/orgs/SonsOfPHP/discussions).


# Adapters

Adapters allow you to connector various services such as Amazon S3 for storage.

## Standard Adapters

### InMemoryAdapter

In Memory Adapter does not write anything to disk but keeps them in memory.

```php
<?php

use SonsOfPHP\Component\Filesystem\Adapter\InMemoryAdapter;

$adapter = new InMemoryAdapter();
```

### NativeAdapter

Read/write files to disk.

```php
<?php

use SonsOfPHP\Component\Filesystem\Adapter\NativeAdapter;

$adapter = new NativeAdapter(
    prefix: '/tmp',
);
```

### NullAdapter

This adapter does not read or write anything. Mainly used for testing.

```php
<?php

use SonsOfPHP\Component\Filesystem\Adapter\NullAdapter;

$adapter = new NullAdapter();
```

## Special Adapters

Special adapters are used in various use cases.

### ChainAdapter

This allows you to use multiple adapters together.

When writing a file, it will write to ALL adapters.

When getting a file, it will return the file on the first adapter that has that file.

When deleting a file, it will delete them from ALL adapters.

```php
<?php

use SonsOfPHP\Component\Filesystem\Adapter\ChainAdapter;
use SonsOfPHP\Component\Filesystem\Adapter\InMemoryAdapter;
use SonsOfPHP\Component\Filesystem\Adapter\NativeAdapter;

$adapter = new ChainAdapter([
    new InMemoryAdapter(),
    new NativeAdater('/tmp'),
]);
```

### ReadOnlyAdapter

This ONLY allows you to read files from a filesystem. You cannot write anything.

```php
<?php

use SonsOfPHP\Component\Filesystem\Adapter\ReadOnlyAdapter;
use SonsOfPHP\Component\Filesystem\Adapter\NativeAdapter;

$adapter = new ReadOnlyAdapter(new NativeAdater('/tmp'));
```

### WormAdapter

This adapters allows you to Write Once, Read Many (WORM). Once a file has been written, it CANNOT be modified.

```php
<?php

use SonsOfPHP\Component\Filesystem\Adapter\WormAdapter;
use SonsOfPHP\Component\Filesystem\Adapter\NativeAdapter;

$adapter = new WormAdapter(new NativeAdater('/tmp'));
```

## Additional Adapters

### AWS S3

```shell
composer require sonsofphp/filesystem-aws
```

```php
<?php

use SonsOfPHP\Bridge\Aws\Filesystem\Adapter\S3Adapter;

$adapter = new S3Adapter($s3Client, 'bucket-name');
```


# Http Factory

Simple PSR-17 Http Factory Component

## Installation

```shell
composer require sonsofphp/http-factory
```

## Usage

```php
use SonsOfPHP\Component\HttpFactory\HttpFactory;

$factory = new HttpFactory();

$request       = $factory->createRequest($method, $uri);
$response      = $factory->createResponse();
$serverRequest = $factory->createServerRequest($method, $uri);
$stream        = $factory->createStream();
$stream        = $factory->createStreamFromFile('/path/to/file.ext');
$stream        = $factory->createStreamFromResource($resource);
$uploadedFile  = $factory->createUploadedFile($stream);
$uri           = $factory->createUri('https://docs.sonsofphp.com');
```

For more details, please see the source code or review the PSR-17 documentation.


# Http Handler

Simple PSR-15 Http Handler

### Installation

```shell
composer require sonsofphp/http-handler
```

### Usage

Usage is pretty simple.

```php
<?php

use SonsOfPHP\Component\HttpHandler\HttpHandler;
use SonsOfPHP\Component\HttpHandler\MiddlewareStack;

$stack = new MiddlewareStack();
$stack->add(new RouterMiddleware());
$stack->add(new CookieMiddleware());
$stack->add(function ($request, $handler) {
    // ...
});
// ...

$app = new HttpHandler($stack);
$response = $app->handle($request);
```

The `MiddlewareStack` accepts objects that implement `Psr\Http\Server\MiddlewareInterface` and anonymous functions.

#### Middleware Priorities

An optional second argument may be passed to the `MiddlewareStack` which is for the priority of the middleware. Priorities are ordered in ascending order.

```php
<?php

use SonsOfPHP\Component\HttpHandler\MiddlewareStack;

$stack = new MiddlewareStack();
$stack->add(new NotFoundMiddleware(), 1025);
$stack->add(new RouterMiddleware(), 255);
$stack->add(new CookieMiddleware(), -255);
$stack->add(new DefaultMiddleware());
```

In the above example, the `CookieMiddleware` will be processed first and `NotFoundMiddleware` will be processed last.


# Http Message

PHP PSR-7 implementation

Simple PSR-7 Compatible Http Message Component

## Installation

```shell
composer require sonsofphp/http-message
```

## Usage

### Uri

```php
<?php

use SonsOfPHP\Component\HttpMessage\Uri;

// Passing in the url is optional.
$uri = new Uri('https://docs.sonsofphp.com');

// You can also pass in a more detailed url
$uri = new Uri('https://docs.sonsofphp.com/search/results?q=test');

// You can also easily add additional query parameters
$uri = $uri->withQueryParams([
    'page'    => 1,
    'limit'   => 25,
    'filters' => [
        'isActive' => 1,
    ]
]);

// To remove all the query parameters, pass in `null`
$uri = $uri->withQueryParams(null);
```


# JSON

The JSON Component is a wrapper around php's `json_encode` and `json_decode`.

### Installation

```shell
composer require sonsofphp/json
```

### Usage

#### Json

```php
<?php
use SonsOfPHP\Component\Json\Json;

// You use this as a drop-in replacement for json_encode and json_decode
$json = Json::encode($value);
$object = Json::decode($json);
$array = Json::decode($json, true);

// It comes with a JsonEncoder and JsonDecoder, see below for usage
$json = new Json();
$encoder = $json->getEncoder();
$decoder = $json->getDecoder();
```

#### JsonEncoder

```php
<?php
use SonsOfPHP\Component\Json\JsonEncoder;

$encoder = new JsonEncoder();

// You can add various flags using the various JSON constants PHP provides. You
// can enter them one at a time or combine them
$encoder = $encoder
    ->withFlags(JSON_HEX_QUOT)
    ->withFlags(JSON_HEX_TAG | JSON_HEX_AMP);

// Removing flags is just as easy
$encoder = $encoder->withoutFlags(JSON_HEX_TAG);

// Changing the depth is just as easy
$encoder = $encoder->withDepth(256);

// Once you are ready, you just encode the value to output json
$json = $encoder->encode($value);

// You can chain everything together as well
$json = (new JsonEncoder())
    ->withDepth(256)
    ->withFlags(JSON_HEX_QUOT)
    ->encode($value);
```

#### JsonDecoder

```php
<?php
use SonsOfPHP\Component\Json\JsonDecoder;

$decoder = new JsonDecoder();

// Add & Remove flags with the constants provided by PHP
$decoder = $decoder
    ->withFlags(...)
    ->withoutFlags(...);

// Change the depth
$decoder = $decoder->withDepth(256);

// Easy to return array
$decoder = $decoder->asArray();

// Decode the json
$array = $decoder->decode($json);
```

### Need Help?

Check out [Sons of PHP's Organization Discussions](https://github.com/orgs/SonsOfPHP/discussions).


# Link

PSR-13: Link

## Installation

```shell
composer require sonsofphp/link
```

## Usage

```php
<?php

use SonsOfPHP\Component\Link\Link;

$link = new Link('https://docs.sonsofphp.com');
```


# Logger

PSR-3 Logger

Simple yet powerful PSR-3 Logger.

## Installation

```shell
composer require sonsofphp/logger
```

## Usage

Simple Usage Example

```php
<?php

use SonsOfPHP\Component\Logger\Logger;

// Logger is PSR-3 Logger
$logger = new Logger();
$logger->debug('Debug Log Message');
```

Full Usage Example

```php
<?php

use SonsOfPHP\Component\Logger\Logger;
use SonsOfPHP\Component\Logger\Level;

// "api" will be the channel
$logger = new Logger('api');

// Add as many handlers as you want
$logger->addHandler(new FileHandler('/var/logs/api.log'));

// Add as many filters as you want
$logger->addEnricher(new MaskContextValueEnricher('password'));

// You can add a filter
$logger->setFilter(new LogLevelFilter(Level::Info)); // ONLY log info and above messages

// Filters can also be added to handlers
$handler = new FileHandler('/var/logs/api.alert.log');

// This handler will now ONLY handle records that are 'alert' and higher
$handler->setFilter(new LogLevelFilter(Level::Alert));
$logger->addHandler($handler);
```

## Handlers

Handlers are responsible for "handling" the log message. The handler will send the log message where it's been configured to. Out of the box, it supports a few different handlers.

## Enrichers

Enrichers will add extra context to the log message. This could be the git hash or memory usage, or anything else you want.

Enrichers are also used to modify context values in case someone adds sensitive values in there.

## Filters

Filters can be used to determine it the log message should be handled. Custom filters could be specific to an HTTP Request so that ONLY requests matching a specific path would be handled. You could also make a custom filter to log only authenticated users

## Formatters

Formatters are used by Handlers to format the message that needs to be logged.


# Handlers

Handlers are responsible for "handling" the log message. The handler will send the log message where it's been configured to. Out of the box, it supports a few different handlers.

## FileHandler

## NullHandler

## StreamHandler


# Enrichers

Enrichers will add extra context to the log message. This could be the git hash or memory usage, or anything else you want.

Enrichers are also used to modify context values in case someone adds sensitive values in there.

## GroupIdEnricher

## HostnameEnricher

## InodeEnricher

## MaskContextValueEnricher

## NullEnricher

## ProcessIdEnricher

## ScriptOwnerEnricher

## UserIdEnricher


# Filters

Filters can be used to determine it the log message should be handled. Custom filters could be specific to an HTTP Request so that ONLY requests matching a specific path would be handled. You could also make a custom filter to log only authenticated users

## ChannelFilter

## LogLevelFilter

## MockFilter


# Formatters

Formatters are used by Handlers to format the message that needs to be logged.

## SimpleFormatter


# Mailer

Simple PHP Mailer

### Installation

```shell
composer require sonsofphp/mailer
```

### Usage

```php
<?php

use SonsOfPHP\Component\Mailer\Message;
use SonsOfPHP\Component\Mailer\Mailer;
use SonsOfPHP\Component\Mailer\Transport\NullTransport;

$message = new Message();
$message
    ->setTo('joshua@sonsofphp.com')
    ->setFrom('joshua@sonsofphp.com')
    ->setSubject('Test Subject')
    ->setBody($body)
;

$mailer = new Mailer(new NullTransport());
$mailer->send($message);
```

#### Middleware

The `Mailer` class supports various middleware as well.

```php
<?php

use SonsOfPHP\Component\Mailer\Mailer;
use SonsOfPHP\Component\Mailer\Transport\NullTransport;

$mailer = new Mailer(new NullTransport());
$mailer->addMiddleware($middleware);
```


# Transports

## NativeMailTransport

## NullTransport


# Money

The Money component was inspired by JSR 354 along with a few other ideas. It is mainly used for services and sites dealing with money.

## Installation

```shell
composer require sonsofphp/money
```

## Usage

### Money

```php
<?php
use SonsOfPHP\Component\Money\Money;
use SonsOfPHP\Component\Money\Currency;

// Can use the Money Class like this
$money = new Money(100, new Currency('USD'));
$amount = $money->getAmount(); // AmountInterface
$value = $amount->toString(); // `toInt` and `toFloat` are also supported

// Or like this
$money = Money::USD(100);
```

You can preform different [operations](/components/money/operators) to create new money.

```php
<?php
use SonsOfPHP\Component\Money\Money;

$money = Money::USD(100);

$newMoney1 = $money->add(Money::USD(100)); // Amount is now 200
$newMoney2 = $money->subtract(Money::USD(100)); // Amount is now 0

// The amount of the orginal Money does not change
$amount = $money->getAmount(); // Amount is 100
```

You can multiple and divide too.

```php
<?php
use SonsOfPHP\Component\Money\Money;

$money = Money::USD(100);

$newMoney1 = $money->multiply(5); // value of Amount is now 500
$newMoney2 = $money->divide(5); // value of Amount is now 20

// The amount of the orginal Money does not change
$amount = $money->getAmount(); // value of Amount is 100
```

### Currency

```php
<?php
use SonsOfPHP\Component\Money\Currency;

$currency = new Currency('USD');
// OR
$currency = Currency::USD();
```

### Formatters

```php
<?php

use SonsOfPHP\Component\Money\Formatter\IntlMoneyFormatter;
use SonsOfPHP\Component\Money\Money;

$formatter = new IntlMoneyFormatter(new \NumberFormatter('en_US', \NumberFormatter::CURRENCY));
$output = $formatter->format(Money::USD(4.20));
echo $output; // $4.20
```

## Twig Bridge

### Installation

```shell
composer require sonsofphp/money-twig
```

### Usage

#### Add Extension to Twig Environment

```php
<?php

use SonsOfPHP\Bridge\Twig\Money\MoneyExtension;
use SonsOfPHP\Component\Money\Formatter\IntlMoneyFormatter;

$formatter = new IntlMoneyFormatter(new \NumberFormatter('en_US', \NumberFormatter::CURRENCY));
$extension = new MoneyExtension($formatter);

$twig = new \Twig\Environment($loader);
$twig->addExtension($extension);
```

#### Usage in Twig Templates

```twig
Your total is {{ money|format_money }}.
```

## Need Help?

Check out [Sons of PHP's Organization Discussions](https://github.com/orgs/SonsOfPHP/discussions).


# Currency Providers

The Currency Provider allows you to grab all the known Currencies. It also has a `query` method to pass in `CurrencyProviderQuery`s to return various results.

## Basic Usage

### Loop over Currencies

```php
<?php
// @var CurrencyInterface $currency
foreach ($provider->getCurrencies() as $currency) {
    // Do something with each currency
}
```

### Check if Provider has a Currency

```php
// Check to see if the provider has the currency you are looking for
// @var bool $hasCurrency
$hasCurrency = $provider->hasCurrency('USD');
$hasCurrency = $provider->hasCurrency(Currency::USD());
```

### Getting Currency from the Provider

```php
// You can get a Currency like this. If the Currency does not exist
// it will throw an exception.
// @var CurrencyInterface $currency
$currency = $provider->getCurrency('USD');

$code      = $currency->getCurrencyCode(); // "USD"
$numCode   = $currency->getNumericCode(); // 840
$minorUnit = $currency->getMinorUnit(); // 2
```

## Currency Providers

### CurrencyProvider

The `CurrencyProvider` is usually the one that you will be using most of the time. It provides most of the [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) currencies.

```php
<?php
use SonsOfPHP\Component\Money\CurrencyProvider\CurrencyProvider;

$provider = new CurrencyProvider();
```

### XCurrencyProvider

The `XCurrencyProvider` gives you access to all of the "X" Currencies. For example, this provider is where you will find the Currency "XTS" which is reserved for use in testing.

```php
<?php
use SonsOfPHP\Component\Money\CurrencyProvider\XCurrencyProvider;

$provider = new XCurrencyProvider();
```

### ChainCurrencyProvider

The `ChainCurrencyProvider` lets you use multiple Currency Providers together at once. It does not provide any currencies itself.

```php
<?php
use SonsOfPHP\Component\Money\CurrencyProvider\ChainCurrencyProvider;
use SonsOfPHP\Component\Money\CurrencyProvider\CurrencyProvider;
use SonsOfPHP\Component\Money\CurrencyProvider\XCurrencyProvider;

$provider = new ChainCurrencyProvider([
    new CurrencyProvider(),
    new XCurrencyProvider(),
]);

// You can add additional providers as well
$provider->addProvier(new MyCustomCurrencyProvider());
```


# Operators

Operators allow you to change something about the Money and will return a new instance of the Money. For example, the methods: add, subtract, multiply, and divide on the `Money` class are all MoneyOperators.

You can create your own Money Operators and use them with the Money class out of the box.

```php
<?php
$operator = new MyOwnMoneyOperator();
$newMoney = $money->with($operator);
```

Here's how it works in slightly more detail.

```php
<?php
use SonsOfPHP\Component\Money\Operator\Money\AddMoneyOperator;
use SonsOfPHP\Component\Money\Money;

$accountBalance = Money::USD(1000);
$depositAmount  = Money::USD(2000);

// The construct takes the deposit amount we will apply to the balance
$operator = new AddMoneyOperator($depositAmount);

// The operator will apply (the deposit amount) to the account balance
// and the returned MoneyInterface will be a new object
$newBalance = $operator->apply($accountBalance);

$newBalance->isEqualTo($accountBalance); // will return false

// Another way to use the Operator is
$anotherNewBalance = $accountBalance->with($operator);

$newBalance->isEqualTo($anotherNewBalance); // will return true

// Even though $newBalance and $anotherNewBalance are different objects, they
// are both USD and both have the same amount (3000)
```

As you can see, using Operators provides you some power. You could create your Operator to calculate a payment amount for a loan. Another great idea is to use an Operator that can calculate tax.


# Queries

Queries are used to answer questions about the Money. They can return different values based on what you want.

You can also create and use your own money queries.

```php
<?php
$query = new DummyQuery();
$result = $money->query($query);
```

Let's take a look a `MoneyQuery` in a little more detail.

```php
<?php
use SonsOfPHP\Component\Money\Query\Currency\IsEqualToCurrencyQuery;
use SonsOfPHP\Component\Money\Currency;

$currency    = Currency::USD();
$currencyUSD = Currency::USD();
$currencyJPY = Currency::JPY();

// We create the new query and inject the dependencies
$query = new IsEqualToCurrencyQuery($currency);

// Both currencies have the same Currency Code, so we can say
// they are equal
$isEqual = $query->queryFrom($currencyUSD); // returns true

// Another way to do the same thing is
$isEqual = $currencyUSD->query($query); // returns true
$isEqual = $currencyJPY->query($query); // returns false
```

Queries aren't just limited to `bool` type of results. Running a query can return any type of result. This means you could return an `array` or even a `Generator`.


# Pager

Simple yet powerful pagination

## Installation

```shell
composer require sonsofphp/pager
```

## Usage

Basic Usage

```php
<?php

use SonsOfPHP\Component\Pager\Adapter\ArrayAdapter;
use SonsOfPHP\Component\Pager\Pager;

$pager = new Pager(new ArrayAdapter($results));

foreach ($pager as $result) {
    // ...
}
```

Advance Usage

```php
<?php

use SonsOfPHP\Component\Pager\Adapter\ArrayAdapter;
use SonsOfPHP\Component\Pager\Pager;

// These are the default option values
$pager = new Pager(new ArrayAdapter($results), [
    'current_page' => 1,
    'max_per_page' => 10,
]);

// You can also set current page and max per page
$pager->setCurrentPage(1);
$pager->setMaxPerPage(10);


$totalPages   = $pager->getTotalPages();
$totalResults = $pager->getTotalResults();
$currentPage  = $pager->getCurrentPage();

if ($pager->haveToPaginate()) {
    // ...
}

if ($pager->hasPreviousPage()) {
    $prevPage = $pager->getPreviousPage();
    // ...
}

if ($pager->hasNextPage()) {
    $nextPage = $pager->getNextPage();
    // ...
}
```


# Adapters

## Custom Adapters

Creating Custom Adapters is easy. You can take a look at the available adapters to see how easy it is.

Please see the [Pager Contract](/contracts/pager) to learn more.

## Available Adapters

### ArrayAdapter

```php
<?php

use SonsOfPHP\Component\Pager\Adapter\ArrayAdapter;

$adapter = new ArrayAdapter($results);
```

### CallableAdabter

Will take any `callable` arguments.

```php
<?php

use SonsOfPHP\Component\Pager\Adapter\CallableAdapter;

$adapter = new CallableAdapter(
    count: function (): int {
        // ...
    },
    slice: function (int $offset, ?int $length): iterable {
        // ...
    },
);
```

### ArrayCollectionAdapter (doctrine/collections)

{% hint style="danger" %}
Requires `sonsofphp/pager-doctrine-collections`
{% endhint %}

```php
<?php

use Doctrine\Common\Collections\ArrayCollection;
use SonsOfPHP\Bridge\Doctrine\Collections\Pager\ArrayCollectionAdapter;

$collection = new ArrayCollection();

$adapter = new ArrayCollectionAdapter($collection);
```

### QueryBuilderAdapter (doctrine/dbal)

{% hint style="danger" %}
Requires `sonsofphp/pager-doctrine-dbal`
{% endhint %}

```php
<?php

use Doctrine\DBAL\Query\QueryBuilder;
use SonsOfPHP\Bridge\Doctrine\DBAL\Pager\QueryBuilderAdapter;

// ...

$adapter = new QueryBuilderAdapter($builder, function (QueryBuilder $builder): void {
    $builder->select('COUNT(e.id) as total');
});
```

### QueryBuilderAdapter (doctrine/orm)

{% hint style="danger" %}
Requires `sonsofphp/pager-doctrine-orm`
{% endhint %}

```php
<?php

use Doctrine\ORM\QueryBuilder;
use SonsOfPHP\Bridge\Doctrine\ORM\Pager\QueryBuilderAdapter;

$builder = $repository->createQueryBuilder('e');

$adapter = new QueryBuilderAdapter($builder);
```


# Registry

### Installation

```shell
composer require sonsofphp/registry
```

### Usage

```php
<?php

use SonsOfPHP\Component\Registry\ServiceRegistry;

$registry = new ServiceRegistry($interfaceClassName);

$registry->register('service.id', $service);
$service = $registry->get('service.id');

if ($registry->has('service.id')) {
    // ...
}

$registry->unregister('service.id');

foreach ($registry->all() as $identifier => $service) {
    // ...
}
```


# State Machine

### Installation

```shell
composer require sonsofphp/state-machine
```

### Usage

Basic Usage

```php
<?php

use SonsOfPHP\Component\StateMachine\StateMachine;

$sm = new StateMachine([
    'graph'         => 'order',
    'state_getter'  => 'getState',
    'state_setter'  => 'setState',
    'supports' => [
        OrderInterface::class,
    ],
    'transitions' => [
        'create' => [
            'from' => 'draft',
            'to' => 'new',
            'callbacks' => [
                'guard' => [
                    'guard-create' => [
                        'do' => function () { return true; },
                    ],
                ],
                'pre' => [
                    'pre-create' => [
                        'do' => function () { },
                    ],
                    'another-pre-create' => [
                        'do' => function () {},
                    ],
                ],
                'post' => [
                    'post-create' => [
                        'do' => function () {},
                    ],
                    'another-post-create' => [
                        'do' => function () {},
                    ],
                ],
            ],
        ],
        'fulfill' => [
            'from' => 'new',
            'to' => 'fulfilled',
        ],
        'cancel' => [
            'from' => ['draft', 'new', 'fulfilled'],
            'to' => 'fulfilled',
        ],
    ],
]);

// Check if state can change
$sm->can($order, 'create');

// Apply transition
$sm->apply($order, 'fulfil');

// Get Current State
$sm->getState($order);
```


# Version

The Version component allows you an easy way to parse and compare versions. It supports both Semversion and package manager syntax.

## Installation

```shell
composer require sonsofphp/version
```

## Usage

The base `Version` supports semver standards.

```php
<?php
use SonsOfPHP\Component\Version\Version;

// You can create versions two different ways
$version = new Version('1.2.3');
$version = Version::from('1.2.3');

echo $version; // prints "1.2.3"
$versionString = $version->toString(); // $versionString === "1.2.3"

// Easy API
$major = $version->getMajor(); // $major === 1
$minor = $version->getMinor(); // $minor === 2
$patch = $version->getPatch(); // $patch === 3

// Comparing Versions is easy too
$currentVersion = new Version('1.1.1');
$latestVersion  = new Version('1.1.2');

// if $latestVersion > $currentVersion
if ($latestVersion->isGreaterThan($currentVersion)) {
    // run upgrade
}
// Also supports "isLessThan" and "isEqualTo"
```

### Advanced Usage

You can also use Pre-release and Build Metadata with your versions.

```php
<?php
use SonsOfPHP\Component\Version\Version;

// Create a new Version that includes Pre-release and/or Build Metadata
$version = new Version('1.2.3-RC1+buildMetaData');

// You can grab the info as well
$preRelease = $version->getPreRelease(); // $preRelease === "RC1"
$build = $version->getBuild(); // $build === "RC1"
```


# Contributing Overview

<div align="center"><img src="https://raw.githubusercontent.com/SonsOfPHP/.github/main/assets/top-rocker.png" alt=""></div>

***

## How to Contribute

The Sons of PHP is always looking for people who can contribute to the project. Popular ways people get started contributing is by updating our Documentation.

### GitHub Discussions

[Sons of PHP Discussions](https://github.com/orgs/SonsOfPHP/discussions) are centralized so all questions and help can be done in one location. Finding and helping answer questions is an easy way to become more involved.

### Code

All of our code is kept in the [Mother Repository](https://github.com/SonsOfPHP/sonsofphp) and manged from there. You can browse the [issues](https://github.com/SonsOfPHP/sonsofphp/issues) and [PRs](https://github.com/SonsOfPHP/sonsofphp/pulls) for all the different Sons of PHP packages.


# Contributing Code

Always looking for new code contributions. Just fork the `sonsofphp/sonsofphp` repo, cut a branch and add your code. Once you push the feature branch up, create a PR it.

You can take a look at the existing issues, or add new code yourself.

## Git Branch Workflow

* Mainline = This is the `main` branch.
* Version branches = branch `4.x` is for the Sons of PHP latest 4.x code

## New Features

New Features are only accepted for the mainline.

Cut off of the mainline (main) and submit a PR to have it merged back into the mainline (main).

## Bug Fixes

Bug fixes are accepted mainline and previous version only. If the current version is 5, only bug fixes for `main` and `4.x` branches are accepted.

Cut branch off the version and submit a PR to have it merged into that version branch.

## Security Fixes

Security fixes are accepted on the mainline, previous 2 version branches. If the current version is 5, security fixes can be submitted for `main`, `4.x`, and `3.x` branches.

Cut branch off the version and submit a PR to have it merged into that version branch.


# Discussions

We use [GitHub Discussions](https://github.com/orgs/SonsOfPHP/discussions) for various things and are always looking for new people to help out and answer questions or post what they use some of the libraries for.


# Documentation

Documentation is an important part of Sons of PHP and we are always looking for people who can help make the documentation better. Fixing typos and adding more examples to the documentation to help other devs out.

If this interests you, please fork the repository and add/update the existing documentation.


