# Siler

Siler is a set of general purpose high-level abstractions aiming an API for declarative programming in PHP.

> Simplicity is the ultimate sophistication. – Leonardo Da Vinci

You can use it within any framework or standalone, as a micro-framework:

```bash
composer require leocavalcante/siler
```

## A Hello World example

```php
use Siler\Functional as λ; // Just to be cool, don't use non-ASCII identifiers ;)
use Siler\Route;

Route\get('/', λ\puts('Hello World'));
```

This outputs "Hello World" when the file is reached via HTTP using the GET method and an URI path that matches "/". **Got the idea, right?**


# Routing

```php
use Siler\Route;

Route\get('/path', <handler>);
Route\post('/path', <handler>);
Route\put('/path', <handler>);
Route\delete('/path', <handler>);
Route\options('/path', <handler>);
```

Also a facade to catch **any HTTP method:**

```php
Route\any('/path', <handler>);
```

Additional and **custom HTTP methods** can be set using the `route` function:

```php
Route\route('custom', '/path', <handler>);
```

With an `array`, you can listen for **multiple HTTP methods** on the same handler:

```php
Route\route(['post', 'put'], '/path', <handler>);
```

### **Route parameters**

Route parameters can be defined using Regular Expressions:

```php
Route\get('/number/([0-9]+)', <handler>);
```

Or using a little syntax for creating named groups, that is just wrapping the parameter name around curly-brackets:

```php
Route\get('/number/{n}', <handler>);
```

{% hint style="info" %}
The above will match anything, not only numbers. For a fine-grained control, please use Regular Expressions.
{% endhint %}

#### Optional parameters

Optional named parameters can be defined using the question mark `?` as sufix:

```php
Route\get('/hello/{name}?', <handler>);
```

{% hint style="info" %}
To avoid the need of a trailing slash, add a question mark after it, like regex (because it is regex):

```php
Route\get('/hello/?{name}?', <handler>);
```

{% endhint %}

### Route handlers

The `<handler>` placeholder you saw is where you can put the route logic and it can be contained on the following:

#### Callables

As [Anonymous functions](http://php.net/manual/en/functions.anonymous.php):

```php
Route\get('/hello/{name}', function (array $routeParams) {
    echo 'Hello '.($routeParams['name'] ?? 'World');
});
```

As a [Closures](http://php.net/manual/en/class.closure.php):

```php
$handler = function (array $routeParams) {
    echo 'Hello World';  
};

function create_handler() {
    return function (array $routeParams) {
        echo 'Hello World';
    };
}

Route\get('/', $handler);
Route\get('/', create_handler());
```

As a method call on array-syntax:

```php
class Hello {
    public function world(array $routeParams) {
        echo 'Hello World';
    }
}

$hello = new Hello();
Route\get('/', [$hello, 'world']);
```

As a static method call string-syntax:

```php
class Hello {
    static public function world(array $routeParams) {
        echo 'Hello World';
    }
}

Route\get('/', 'Hello::world');
```

As any kind of [callable](http://php.net/manual/en/language.types.callable.php):

```php
class Hello {
    public function __invoke(array $routeParams) {
        echo 'Hello World';
    }
}

Route\get('/', new Hello());
```

#### Filenames

Handlers can be a String representing the filename of another PHP file, route parameters will be available at the global `$params` variable:

{% code title="index.php" %}

```php
Route\get('/hello/{name}', 'pages/home.php');
```

{% endcode %}

{% code title="pages/home.php" %}

```php
echo 'Hello '.$params['name'];
```

{% endcode %}

#### Resources

CRUD routes can be auto-magically be defined for convenience using the Rails and Laravel pattern.

Given this resource declaration:

```php
Route\resource('/users', 'api/users');
```

Siler will look for files at `path/to/files` matching the HTTP URI according to the table below:

| HTTP Verb | URI                | File                     |
| --------- | ------------------ | ------------------------ |
| GET       | `/users`           | `/api/users/index.php`   |
| GET       | `/users/create`    | `/api/users/create.php`  |
| POST      | `/users`           | `/api/users/store.php`   |
| GET       | `/users/{id}`      | `/api/users/show.php`    |
| GET       | `/users/{id}/edit` | `/api/users/edit.php`    |
| PUT       | `/users/{id}`      | `/api/users/update.php`  |
| DELETE    | `/users/{id}`      | `/api/users/destroy.php` |

The file structure should look like:

```
index.php
/api
└── /users
    ├─ index.php
    ├─ create.php
    ├─ store.php
    ├─ show.php
    ├─ edit.php
    ├─ update.php
    └─ destroy.php
```

#### Files

You can also let Siler create the routes recursively looking for files at a base path. Then the files names will be used to define the method and the path.

```php
Route\files('controllers');
```

Siler will interpret periods (.) as slashes and also maintain folder structure at HTTP path:

| Filename                | Method | Path       |
| ----------------------- | ------ | ---------- |
| `index.get.php`         | GET    | `/`        |
| `index.post.php`        | POST   | `/`        |
| `foo.get.php`           | GET    | `/foo`     |
| `bar/index.get.php`     | GET    | `/bar`     |
| `foo.bar.get.php`       | GET    | `/foo/bar` |
| `foo/bar.get.php`       | GET    | `/foo/bar` |
| `foo/bar/index.get.php` | GET    | `/foo/bar` |

Since `{` and `}` are valid chars in a filename, route parameters should work as well, but you can define required parameters prefixing with `$` and optional parameters using `@`:

| Filename             | Method | Path           |
| -------------------- | ------ | -------------- |
| `foo.{slug}.get.php` | GET    | `/foo/{slug}`  |
| `foo.$slug.get.php`  | GET    | `/foo/{slug}`  |
| `foo.@slug.get.php`  | GET    | `/foo/{slug?}` |

Any method is valid, it is guessed based on the penultimate "token":

| Filename           | Method   | Path   |
| ------------------ | -------- | ------ |
| `foo.options.php`  | OPTIONS  | `/foo` |
| `foo.x-custom.php` | X-CUSTOM | `/foo` |

{% hint style="info" %}
Note on handlers
{% endhint %}

When creating routes, be careful about **early** and **lazy** evaluations.

```php
Route\get('/foo', [new FooController(), 'index']);
```

The example above is **early**, which means it will call `FooController` constructor for each request even if it's not a request to `/foo`.

To make it **lazy** you can wrap inside a `Closure`:

```php
Route\get('/foo', function () {
  $controller = new FooController();
  return $controller->index();
});
```

Now `FooController` is called only when there is a match for route `/foo`.\
One downside is that now you have to explicitly manage path parameters, on the other hand is a best practice to do so.\
It is a good time to validate parameters, convert plain string parameters to meaningful types on your domain or resolve dependencies.

```php
Route\get('/users/{id}', function (array $params) use ($ioc) {  
  if (!preg_match('/[0-9]+/', $params['id']) {
    $controller = $ioc->resolve(ErrorController::class);
    return $controller->invalid('IDs must be numbers');
  }
  
  $controller = $ioc->resolve(UsersController::class);
  return $controller->show($params['id']);
});
```

### Request

#### Body

You can grab the raw request body using:

```php
use Siler\Http\Request;

$body = Request\raw();
```

Parse as URL-encoded data using:

```php
$params = Request\params();
```

Parse as JSON using:

```php
$resource = Request\json();
```

#### $\_GET and $\_POST superglobals

Or get data from a Form and from the Query String using:

```php
$input = Request\post('input');
$searchTerm = Request\get('q');
```

Calling them without arguments will tell Siler to return all the values as an `array`:

```php
$data = Request\post();
$queryString = Request\get();
```

You can also pass a default value if the key isn't present in the GET or POST super-globals:

```php
$input = Request\post('input', 'default-value');
```

#### Headers

Also conveniently get a header as easy as for the body:

```php
$contentType = Request\header('Content-Type');
```

*e.g. Serving both JSON and Form requests:*

```php
$data = Request\header('Content-Type') == 'json' ? Request\json() : Request\post();
```

### Response

Siler also have convenient functions to simplify HTTP responses.

You can output JSON encoded body with proper headers in just one line:

```php
use Siler\Http\Response;

Response\json(['error' => false, 'message' => 'It works']);
```

It will already output the given data, you don't need to call `echo` or `print` so use carefully, it's not a encoder, it's an output-er.

#### Headers

Same easy as for Request, you can set HTTP response headers with the `header` function at `Response` namespace:

```php
Response\header('Access-Control-Allow-Origin', 'https://know-domain.tld');
Response\header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
```


# PSRs & Middleware Pipeline

This interfaces are already (and amazingly) implemented by Laminas at projects: [Diactoros](https://github.com/laminas/laminas-diactoros) and [Stratigility](https://github.com/laminas/laminas-stratigility). Siler wraps them and exposes a function-friendly API handling state internally while achieving a fully-featured and declarative way for: **Middleware Pipelining**.

## PSR-7 HTTP Messages

{% hint style="info" %}
Siler doesn't have direct dependencies, to stay fit, it favors peer dependencies, which means you have to explicitly declare a `diactoros` dependency in your project in order to use it.
{% endhint %}

```bash
composer require laminas/laminas-diactoros
```

You can create a *superglobals* seeded `ServerRequest` with `Siler\Diactoros\request()`:

```php
use Siler\Diactoros;

$request = Diactoros\request();
```

And create Responses through helpers:

```php
$json = Diactoros\json(['some' => 'data']);
$html = Diactoros\html('<p>some markup</p>');
$text = Diactoros\text('plain text');
```

If none of them fits your needs, you can create a raw Response:

```php
$response = Diactoros\response();
$response->getBody()->write('something');
```

To emit a Response, there is no big deal, if you got Siler, you already imagined that is about one or two function calls, but this time we get the help from [HttpHandlerRunner](https://github.com/laminas/laminas-httphandlerrunner):

```bash
composer require laminas/laminas-httphandlerrunner
```

Then

```php
HttpHandlerRunner\sapi_emit($response);
```

As in `Siler\Http\Response` namespace functions, the `HttpHandlerRunner\sapi_emit` will output headers and text to the buffer, use it carefully.

Example:

```php
<?php declare(strict_types=1);

require_once 'vendor/autoload.php';

use Siler\Diactoros;
use Siler\HttpHandlerRunner;
use Siler\Route;
use function Siler\array_get;

$request = Diactoros\request();
$response = Route\matching([
    // /greet/Leo?salute=Hello
    Route\get('/greet/{name}', function ($params) use ($request) {
        $salute = array_get($request->getQueryParams(), 'salute', 'Olá');
        return Diactoros\text("{$salute} {$params['name']}");
    }, $request),

    Route\get('/', function () {
        return Diactoros\text('hello world');
    }, $request),

    Diactoros\text('not found', 404),
]);

HttpHandlerRunner\sapi_emit($response);
```

## PSR-15 Middleware Pipelining

```bash
composer require laminas/laminas-stratigility
```

{% hint style="info" %}
Siler doesn't have direct dependencies, to stay fit, it favors peer dependencies, which means you have to explicitly declare a `stratigility` dependency in your project in order to use it.
{% endhint %}

A very simple Hello World example:

```php
use function Siler\Diactoros\request;
use function Siler\Diactoros\text;
use function Siler\HttpHandlerRunner\sapi_emit;
use function Siler\Stratigility\handle;
use function Siler\Stratigility\pipe;

pipe(function ($request, $handler) {
    return text('hello world');
});

sapi_emit(handle(request()));
```

It's more `use`s than actual code because Siler is abstracting all the way down for you.

| API         | Description                                                                                                                                                                                                                  |
| ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `pipe`      | Creates a Stratigility `MiddlewarePipe` with a default name and pipes the given Clousure to it already wrapping it inside a `MiddlewareInterface` decorator, or you can pass any implementation `MiddlewareInterface` to it. |
| `text`      | Creates a Diactoros `TextResponse`. The Diactoros namespace in Siler is basically just helper functions for Responses.                                                                                                       |
| `sapi_emit` | Creates and immediately calls `emit` method on a HttpHandlerRunner `SapiEmitter`.                                                                                                                                            |
| `handle`    | Calls `handle` on a `MiddlewarePipe` marshaling the Request.                                                                                                                                                                 |
| `request`   | Creates a Diactoros `ServerRequest` using PHP's Globals.                                                                                                                                                                     |

### Siler's Routes

You can also run pipelines for specific routes:

```php
use Siler\Diactoros;
use Siler\Http\Request;
use Siler\HttpHandlerRunner;
use Siler\Route;
use Siler\Stratigility;

$userMiddleware = function ($request, $handler) {
    $token = Request\get('token');

    if (empty($token)) {
        return Diactoros\json('no user', 401);
    }

    $user = "get_user_by_token:$token";
    $request = $request->withAttribute('user', $user);

    return $handler->handle($request);
};

$homeHandler = function () {
    return Diactoros\json('welcome');
};

$adminHandler = function ($request) {
    return Diactoros\json(['user' => $request->getAttribute('user')]);
};

$secretHandler = function ($request) {
    return Diactoros\json(['user' => $request->getAttribute('user')]);
};

Stratigility\pipe($userMiddleware, 'auth');

$request = Diactoros\request();
$response = Route\matching([
    Route\get('/', $homeHandler, $request),
    Route\get('/admin', Stratigility\process($request, 'auth')($adminHandler), $request),
    Route\get('/secret', Stratigility\process($request, 'auth')($secretHandler), $request),
    Diactoros\json('not found', 404),
]);

HttpHandlerRunner\sapi_emit($response);
```

The second argument on `pipe` here is a Pipeline name, you can pipe middlewares to any number of pipelines, then in `Stratigility\process` we marshal it, from the given `$request` and returns a Closure to be called on a final handler.


# Twig Templating

```
composer require twig/twig
```

{% hint style="info" %}
Siler doesn't have direct dependencies, to stay fit, it favors peer dependencies, which means you have to explicitly declare a `twig` dependency in your project in order to use it.
{% endhint %}

Siler will internally handle the `Twig_Environment` instance.

```php
use Siler\Twig;

Twig\init('path/to/templates');
```

Actually it is also returned at `init` function call, so you call add Twig plugins, filters and functions, for example, adding `Siler\Http\url` into Twig's Environment to later reference static assets on the public folder:

```php
Twig\init('path/to/templates')
    ->addFunction(new Twig_SimpleFunction('url', 'Siler\Http\url'));
```

At initialization, you can also provide a path to templates cache as second argument and if you want to let Twig debug as third argument (defaults to `false`):

```php
$shouldTwigDebug = true;
Twig\init('path/to/templates', 'path/to/templates/cache', $shouldTwigDebug);
```

To render a template, simply call `render` at Twig namespace:

```php
echo Twig\render('pages/home.twig');
```

An passing parameters can be done by the second argument:

```php
$data = ['message' => 'Hello World'];
echo Twig\render('pages/home.twig', $data);
```

Something that can be confusing using Siler is that some function does outputs and other doesn't, like `Twig\render`. So remember that `Twig\render` will only return the rendered template within its given data and you should explicit output or let `Response` do it:

```php
$html = Twig\render('pages/home.twig');
Response\html($html);
```

Also, remember that **you can always bring you own template engine to the playground without any bridging stuff** or use PHP itself on your views.


# GraphQL

> GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data. GraphQL provides a complete and understandable description of the data in your API, gives clients the power to ask for exactly what they need and nothing more, makes it easier to evolve APIs over time, and enables powerful developer tools. — [graphql.org](http://graphql.org/)

Here is how you can create a GraphQL endpoint using Siler's simplicity powered by the [PHP's GraphQL implemention](http://webonyx.github.io/graphql-php/).

First, let's require it:

```
$ composer require webonyx/graphql-php
```

{% hint style="info" %}
Siler doesn't have direct dependencies, to stay fit, it favors peer dependencies, which means you have to explicitly declare a `graphql` dependency in your project in order to use it.
{% endhint %}

Now let's define our Schema. We're going to use a chat-like domain:

{% code title="schema.graphql" %}

```graphql
type Message {
  id: Int
  roomId: Int
  body: String
  timestamp: String
}

type Room {
  id: Int
  name: String
  messages: [Message]
}

type Query {
  messages(roomName: String): [Message]
  rooms: [Room]
}

type Mutation {
  start(roomName: String): Room
  chat(roomName: String, body: String): Message
}
```

{% endcode %}

Very simple, but if it's not familiar to you, take a look at [GraphQL](http://graphql.org/) first since this docs will not cover what is it, but how to use it.

For each Query and Mutation we can define our resolver functions. We'll be using [RedBean](http://www.redbeanphp.com/index.php) to help us as a simple SQLite storage ORM.

{% code title="resolvers.php" %}

```php
<?php

use RedBeanPHP\R;

R::setup('sqlite:'.__DIR__.'/db.sqlite');

$roomByName = function ($name) {
    return R::findOne('room', 'name = ?', [$name]);
};

$roomType = [
    'messages' => function ($room) {
        return R::findAll('message', 'room_id = ?', [$room['id']]);
    },
];

$queryType = [
    'rooms' => function () {
        return R::findAll('room');
    },
    'messages' => function ($root, $args) use ($roomByName) {
        $roomName = $args['roomName'];
        $room = $roomByName($roomName);
        $messages = R::find('message', 'room_id = ?', [$room['id']]);

        return $messages;
    },
];

$mutationType = [
    'start' => function ($root, $args) {
        $roomName = $args['roomName'];

        $room = R::dispense('room');
        $room['name'] = $roomName;

        R::store($room);

        return $room;
    },
    'chat' => function ($root, $args) use ($roomByName) {
        $roomName = $args['roomName'];
        $body = $args['body'];

        $room = $roomByName($roomName);

        $message = R::dispense('message');
        $message['roomId'] = $room['id'];
        $message['body'] = $body;
        $message['timestamp'] = new \DateTime();

        R::store($message);

        return $message;
    },
];

return [
    'Room'     => $roomType,
    'Query'    => $queryType,
    'Mutation' => $mutationType,
];
```

{% endcode %}

Awesome. We have type definitions and resolver functions. Let's put them together in a Schema:

{% code title="schema.php" %}

```php
<?php

use Siler\GraphQL;

$typeDefs = file_get_contents(__DIR__.'/schema.graphql');
$resolvers = include __DIR__.'/resolvers.php';

return GraphQL\schema($typeDefs, $resolvers);
```

{% endcode %}

Yeah, that simple! And it's exactly where Siler does it magic happen.\
Thanks to `webonyx/graphql-php` we can parse the `schema.graphql` into an actual Schema and Siler will override the default field resolver to work with the given `$resolvers`.

Now, let's create our HTTP endpoint:

{% code title="api.php" %}

```php
<?php

use Siler\GraphQL;
use Siler\Http\Request;
use Siler\Http\Response;

require 'vendor/autoload.php';

// Enable CORS
Response\cors();

// Respond only for POST requests
if (Request\method_is('post')) {
    // Retrive the Schema
    $schema = include __DIR__.'/schema.php';

    // Give it to siler
    GraphQL\init($schema);
}
```

{% endcode %}

#### **That's it!**

Start the server:

```bash
$ php -S localhost:8000 api.php
```

\*\*\*\*[**You can use Prima's GraphQL Playground to test it.**](https://github.com/prisma/graphql-playground)\*\*\*\*

Here are some queries you can execute:

Query available rooms:

```graphql
query {
  rooms {
    id
    name
  }
}
```

Yeah, there isn't any Rooms yet:

```javascript
{
  "data": {
    "rooms": []
  }
}
```

But. **It's working!**. Thanks to RedBean + SQLite we can play around without worrying about database setup and migrations.

Creating a new Room:

```graphql
mutation newRoom($roomName: String) {
  start(roomName: $roomName) {
    id
  }
}
```

**variables**

```javascript
{
  "roomName": "graphql"
}
```

Then our first room is created:

```javascript
{
  "data": {
    "start": {
      "id": 1
    }
  }
}
```

Call the query that fetches Rooms to check again:

```graphql
query {
  rooms {
    id
    name
  }
}
```

Yup! It's there:

```javascript
{
  "data": {
    "rooms": [
      {
        "id": 1,
        "name": "graphql"
      }
    ]
  }
}
```

Without any Messages yet:

```graphql
query roomMessages($roomName: String) {
  messages(roomName: $roomName) {
    id
    body
    timestamp
  }
}
```

**variables**

```javascript
{
  "roomName": "graphql"
}
```

No messages yet:

```javascript
{
  "data": {
    "messages": []
  }
}
```

So let's chat!

```graphql
mutation newMessage($roomName: String) {
  chat(roomName: $roomName, body: "hello") {
    id
  }
}
```

**variables**

```javascript
{
  "roomName": "graphql"
}
```

First message created:

```javascript
{
  "data": {
    "chat": {
      "id": 1
    }
  }
}
```

Let's refetch our messages to check:

```graphql
query roomMessages($roomName: String) {
  messages(roomName: $roomName) {
    id
    body
    timestamp
  }
}
```

**variables**

```javascript
{
  "roomName": "graphql"
}
```

Aha! Here we go:

```javascript
{
  "data": {
    "messages": [
      {
        "id": 1,
        "body": "hello",
        "timestamp": "2017-04-20 14:58:07"
      }
    ]
  }
}
```

Liked it? What about listening to added messages and enable real-time features?\
Sounds cool? That is **GraphQL Subscriptions** and we are going to cover next.

### GraphQL Subscriptions

Here is how you can add real-time capabilities to your GraphQL applications.

**Siler** implementation is based on [Apollo's WebSocket transport layer](https://github.com/apollographql/subscriptions-transport-ws).

{% embed url="<https://www.youtube.com/embed/wo9XFmW0W2c>" %}

We'll need some help to get WebSockets working, so let's require two libraries.

One is for the server-side:

```
$ composer require cboden/ratchet
```

And the other is for the client-side:

```
$ composer require textalk/websocket
```

First, let's add our **Subscription** type to our Schema:

{% code title="schema.graphql" %}

```graphql
# (...previous work...)

type Subscription {
  inbox(roomName: String): Message
}
```

{% endcode %}

Simple like that. We can subscribe to inboxes to receive new messages.

And our resolver will look like that:

{% code title="resolvers.php" %}

```php
# (...previous work...)

$subscriptionType = [
    'inbox' => function ($message) {
        return $message;
    },
];
```

{% endcode %}

Yeap, it is just resolving to the message that it receives.

**But where this message comes from?**

#### Siler powers!

Siler has a function at the `Graphql` namespace to define where your subscriptions are running:

```php
GraphQL\subscriptions_at('ws://127.0.0.1:3000');
```

Here we are assuming that they are running at localhost on port 8080.

#### And why is that for?

This is just a helper that adds the **Subscriptions endpoint** to the Siler container so another function can actually use it when needed. And this function is `publish`!

`Siler\GraphQL\publish` will make a WebSocket call to the Subscriptions server notifying that something has happened.

```php
GraphQL\publish('inbox', $message);
```

It's first argument is the **Subscription** that will be triggered and the second argument is a data payload, in our case, the new message that has been created.

Our `resolvers.php` will look like this:

{% code title="resolvers.php" %}

```php
<?php

use RedBeanPHP\R;
use Siler\GraphQL;

R::setup('sqlite:'.__DIR__.'/db.sqlite');

// Here we set where our subscriptions are running
GraphQL\subscriptions_at('ws://127.0.0.1:3000');

$roomByName = function ($name) {
    return R::findOne('room', 'name = ?', [$name]);
};

$roomType = [
    'messages' => function ($room) {
        return R::findAll('message', 'room_id = ?', [$room['id']]);
    },
];

$queryType = [
    'rooms' => function () {
        return R::findAll('room');
    },
    'messages' => function () use ($roomByName) {
        $roomName = $args['roomName'];
        $room = $roomByName($roomName);
        $messages = R::find('message', 'room_id = ?', [$room['id']]);

        return $messages;
    },
];

$mutationType = [
    'start' => function ($root, $args) {
        $roomName = $args['roomName'];

        $room = R::dispense('room');
        $room['name'] = $roomName;

        R::store($room);

        return $room;
    },
    'chat' => function ($root, $args) use ($roomByName) {
        $roomName = $args['roomName'];
        $body = $args['body'];

        $room = $roomByName($roomName);

        $message = R::dispense('message');
        $message['roomId'] = $room['id'];
        $message['body'] = $body;
        $message['timestamp'] = new \DateTime();

        R::store($message);

        // Then we can publish new messages that arrives from the chat mutation
        GraphQL\publish('inbox', $message); // <- Exactly what "inbox" will receive

        return $message;
    },
];

// Our added Subscription type
$subscriptionType = [
    'inbox' => function ($message) { // <- Received from "publish"
        return $message;
    },
];

return [
    'Room'         => $roomType,
    'Query'        => $queryType,
    'Mutation'     => $mutationType,
    'Subscription' => $subscriptionType, // Add to the resolver functions array
];
```

{% endcode %}

### Starting the server

As in `api.php` endpoint we need to setup the Subscriptions server:

{% code title="subscriptions.php" %}

```php
<?php

use function Siler\{GraphQL, Ratchet};

require_once '/vendor/autoload.php';

$schema = require_once __DIR__ . '/schema.php';
$manager = GraphQL\subscriptions_manager($schema);

Ratchet\graphql_subscriptions($manager)->run();
```

{% endcode %}

Yeah, easy like that. Let Siler do the boring stuff. You just give a Schema to the `subscriptions` function. This function will return an `IoServer` where you can call the `run` method.

```bash
php subscriptions.php
```

By default, Siler will run the subscriptions at localhost on port 8080.

### Production-grade

To run subscriptions server at a production-grade level, please consider using some long-running process manager like Supervisor. Take a look at <http://socketo.me/docs/deploy#supervisor>.

#### That's it

No kidding.

Behind the scenes [ReactPHP](http://reactphp.org/), [Ratchet](http://socketo.me/) and [Pawl](https://github.com/ratchetphp/Pawl) are doing the hard work of handling WebSocket communication protocol while **Siler** is doing the work of adding resolver resolution to [webonyx/graphql-php](https://github.com/webonyx/graphql-php) and handling [Apollo's sub-protocol](https://github.com/apollographql/subscriptions-transport-ws#client-server-communication).

#### Ready to test?

\*\*\*\*[**You can use Prisma's GraphQL Playground to test the subscriptions as well.**](https://github.com/prisma/graphql-playground)\*\*\*\*

A subscription query looks like this:

```graphql
subscription newMessages($roomName: String) {
  inbox(roomName: $roomName) {
    id
    body
  }
}
```

**variables**

```javascript
{
  "roomName": "graphql"
}
```

The result will not be immediate since we are now listening to new messages, not querying them.

Let's take a mutation from the [previous guide](https://github.com/leocavalcante/siler/blob/master/docs/graphql/README.md). Open the Graph\_i\_QL app in another tab and execute:

```graphql
mutation newMessage($roomName: String) {
  chat(roomName: $roomName, body: "hello") {
    id
  }
}
```

**variables**

```javascript
{
  "roomName": "graphql"
}
```

Now go back to the subscription tab and see the update!

```graphql
{
  "inbox": {
    "id": 42,
    "body": "hello"
  }
}
```

**Awesome!** Just one thing you probably have noticed. We have subscribed to all rooms. Make a test: create another Room if you haven't already and add a new message to that to see that our subscription tab, with `{"roomName": "graphql"}` as variables, just got this new message. How to solve this?

### Filters

Filters are part of **Siler** and based on Apollo's setup functions. Filter functions receives the published payload data as the first argument and the subscription variables as the second, so you can use them to perform matches:

{% code title="subscriptions.php" %}

```php
<?php

use function Siler\{GraphQL, Ratchet};

require_once '/vendor/autoload.php';

$filters = [
    'inbox' => function ($payload, $vars) {
        return $payload['room_name'] == $vars['roomName'];
    },
];

$schema = require_once __DIR__ . '/schema.php';
$manager = GraphQL\subscriptions_manager($schema, $filters);

Ratchet\graphql_subscriptions($manager)->run();
```

{% endcode %}

As you can see, we have extend our Subscriptions endpoint adding filters. The filters array keys should match corresponding Subscription names, in our case: `inbox`. We are just checking if the given payload `room_name` is the same as the provided by the Subscription variable `roomName`. **Siler** will perform this checks for each subscription before trying to resolve and broadcast them.

We need just a little thing to get working. Adding this `room_name` field to our payload since Message only has the `room_id`. At the chat resolver, before the publish, add this line:

```php
$message['roomName'] = $roomName; // For the inbox filter
GraphQL\publish('inbox', $message); // <- Exactly what "inbox" will receive
```

{% hint style="info" %}
When a `RedBeanObject` is encoded to JSON it automatically converts camel case properties to underscore ones. That is why we give `roomName`, but receive as `room_name`.
{% endhint %}

And that should be enough to solve our problem, now you only receive data from the subscribed rooms. **Enjoy!**

**You can see a complete example including file uploads and directives at:**[ **github.com/leocavalcante/siler/examples/graphql**](https://github.com/leocavalcante/siler/tree/master/examples/graphql)**.**


# @Annotations

On the previous guide you saw how to map resolvers (callables) from a existing SDL (.graphql or .gql). Annotations enables the other way around, it provides a GraphQL SDL from annotated PHP code.

## Thank you Doctrine

Siler's GraphQL Annotations uses the super-powers from Doctrine's Annotations and like any other dependency, is a peer that we should explicitly require:

```
$ composer require doctrine/annotations
```

## What is available:

There are 9 annotations fulfilling the GraphQL's ecosystem:

Class annotations are:

* ObjectType
* InterfaceType
* InputType
* EnumType
* UnionType
* Directive

Complementary method and property annotations are:

* Field
* Args
* EnumVal

They follow a ubiquitous language to GraphQL spec, so if you know GraphQL, there is nothing new here, you probably already know what each of them does just by its name.

## Hello, World!

Let's start by defining our root query:

```php
<?php declare(strict_types=1);

namespace App;

use GraphQL\Type\Definition\ResolveInfo;
use Siler\GraphQL\Annotation\Field;
use Siler\GraphQL\Annotation\ObjectType;

/** @ObjectType */
class Query
{
    /** @Field(description="A common greet") */
    public static function hello(): string
    {
        return 'Hello, World!';
    }
}
```

The `ObjectType` name will be inferred by the class name, so it will already be *Query*.

Then we just provide this class to the `annotated` function on the `Siler\GraphQL` namespace:

```php
<?php declare(strict_types=1);

namespace App;

use function Siler\GraphQL\{annotated, init};

require_once __DIR__ . '/vendor/autoload.php';

$schema = annotated([Query::class]);
init($schema);
```

**And that is it!** It auto-magically servers the following SDL:

```graphql
type Query {
  """
  A common greet
  """
  hello: String!
}
```

With the static `hello` method body already playing the **resolver** role, so:

```graphql
query {
  hello
}
```

Returns:

```javascript
{
  "data": {
    "hello": "Hello, World!"
  }
}
```

**For a full-featured example, please take a look at:** [**github.com/leocavalcante/siler/examples/graphql-annotations**](https://github.com/leocavalcante/siler/tree/master/examples/graphql-annotations)\*\*\*\*

## Caching

Parsing docblocks can be expensive, on production environments is recommended to cache this process by using a caching reader.

First, install `doctrine/cache`:

```javascript
composer require doctrine/cache
```

Then pass a `Doctrine\Common\Cache\Cache` to `Siler\GraphQL\Deannotator::cache()` like:

```javascript
Siler\GraphQL\Deannotator::cache(new ApcuCache());
```

{% hint style="info" %}
Make sure you do this before the `annotated()` call.
{% endhint %}


# Web Servers

By its nature, Siler itself is made to be lightweight and dependent-less.\
This means you can use whatever web-service technology you want.\
Here, you'll find a few example configurations for some well-known web-services.

### Composer-serve script

Siler's default configuration bundled in the `siler/project` template gives a runnable configuration that fits well for development purposes.

You can use it, while being in the root directory of your project (the folder containing your `composer.json` file), by running

```
composer serve
```

or, if you didn't install composer globally,

```
php composer.phar serve
```

*Note: The `serve` script stored in the `composer.json` file runs `php -S 0.0.0.0:8000 -t .`*

### PHP CLI

The PHP CLI can also be enough for small production servers, like for example a raspberry pi running in your home.

To run your website using the PHP CLI, you can use this simple command (adapted to your project):

```
php -S {ip/domain}[:{port}] -t .
```

#### The `{ip/domain}` block

The `{ip/domain}` block is the interface on which you want your server to listen to.

You have multiple choices:

* `127.0.0.1` will run it on `localhost` and you'll be able to access it by going to `http://127.0.0.1` or `http://localhost`in your browser.
* `192.168.x.x` (your local IP) will listen to requests coming from your local IP. You can retrieve it on \*nix systems by running `ifconfig` or `ip addr` and looking for an IP generally starting with `192.168.`.
* `0.0.0.0` will listen to requests coming from *every* network interface you have on your computer, that'd be `localhost`(the loopback), your local IP and more !

Additionally, the PHP CLI supports real domains, which means you can run your website by specifying the domain name of your computer instead of its local IP.

You can retrieve your computer's domain on \*nix systems with the simple command `hostname`.\
It'll return for example `jake-computer`.

You can then use this returned domain as the listening interface: `php -S jake-computer[:{port}] -t .`

#### The `[:{port}]` block

The `[:{port}]` block is the port you want to bind your process to.

> Important: On most \*nix systems, you can't set a port below 1025 without running it as root.\
> The usual ports used are `8080` and `8000`.

*Note: The `:{port}` block is optional, but recommended.*

### Apache

#### Subfolder

If you want to make this website available under a sub-folder of your Apache server, you'll need to make sure the `AllowOverride` contains `Options=Multiviews`, like:

```
<Directory /var/www/html>
    Options +FollowSymLinks +Multiviews
    AllowOverride All
    Order allow,deny
    Allow from all
</Directory>
```

Then, in your project's root folder, you can create a simple `.htaccess` file containing:

```
# HTTPD mod_rewrite required
RewriteEngine on
# If file/directory's present, serving it directly
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# Else, redirecting the request to the index.php file
RewriteRule . index.php
```

This file will try to see if the file or folder you're trying to access exists in the tree, then if it doesn't exist, it'll redirect the request to the `index.php` entry point in your project.

### Notes

There are a lot of other web services, like NGINX, LigHTTPD or IIS.\
If you want to provide a sample configuration for a server that's not listed here, don't hesitate to fork the repo, do your changes, then submit a pull request.


# Siler ❤️ Swoole

Flat files and plain-old PHP functions rocking on a production-grade, high-performance, scalable, concurrent and non-blocking HTTP server.

## Swoole

> Enables PHP developers to write high-performance, scalable, concurrent TCP, UDP, Unix socket, HTTP, Websocket services in PHP programming language without too much knowledge about non-blocking I/O programming and low-level Linux kernel. Compared with other async programming frameworks or softwares such as Nginx, Tornado, Node.js, Swoole has the built-in async, multiple threads I/O modules. Developers can use sync or async API to write the applications — [www.swoole.co.uk](https://www.swoole.co.uk/)

### Features

* Rapid development of high performance protocol servers & clients with PHP language
* Event-driven, asynchronous programming for PHP
* Event loop API
* Processes management API
* Memory management API
* [Golang style channels](https://en.wikipedia.org/wiki/Channel_%28programming%29) for inter-processes communication

### Use cases

* Web applications and systems
* Mobile communication systems
* Online game systems
* Internet of things
* Car networking
* Smart home systems

It is open source and free. Released under the license of Apache 2.0.

## Get started

{% hint style="info" %}
Forget about everything you know on how to run PHP on web servers like Apache and Nginx behind modules and CGI layers.
{% endhint %}

Swoole is released as a [PHP extension (PECL)](https://pecl.php.net/package/swoole) and runs as a PHP CLI application.\
The differences between Swoole with PHP-FPM the traditional PHP model are:

* Swoole forks a number of worker processes based on CPU core number to utilize all CPU cores.
* Swoole supports Long-live connections for websocket server or TCP/UDP server.
* Swoole supports more server-side protocols.
* Swoole can manage and reuse the status in memory.

### Docker

Swoole prerequisites operation system are: Linux, FreeBSD or MacOS, but **don't worry Windows-people**, *we* have [Docker](https://www.docker.com)! And I got us covered with [Dwoole](https://github.com/leocavalcante/dwoole):

{% code title="docker-compose.yml" %}

```yaml
version: '3'
services:
  swoole:
    container_name: siler_swoole
    image: leocavalcante/dwoole:dev
    ports:
      - '9501:9501'
    volumes:
      - ./:/app
```

{% endcode %}

{% hint style="info" %}
Beyond being cross-platform, Dwoole helps with others features like Composer and hot-restart that Unix people would also like.
{% endhint %}

## Siler

You already got Siler, right? Flat-files and plain-old PHP functions rockin'on! **Just simple**. A set of general purpose high-level abstractions aiming an API for declarative programming in PHP. **And this wouldn't be different about Swoole**.

The `Siler\Swoole` namespace get you covered.

### Hello World

{% code title="index.php" %}

```php
<?php declare(strict_types=1);

require_once 'vendor/autoload.php';

use Siler\Swoole;

$server = fn() => Swoole\emit('Hello World');

Swoole\http($server)->start();
```

{% endcode %}

That's it! This attaches a callback handler that always emits "Hello World" on every request and starts a HTTP server on port 9501. Run it using `docker-compose up` or just `php index.php` if you're not using Docker.

Go to `http://localhost:9051` or `http://<docker_machine_ip>:9051` and you should get a "Hello World" response as plain/text.

## More Siler!

You know, Siler can do a lot more, it abstracts things like [Routing](/main/routing) and [Twig Templating](/main/twig-templating). Let's add this to our **Swoole** server:

{% code title="index.php" %}

```php
<?php declare(strict_types=1);
require_once 'vendor/autoload.php';

use Siler\Swoole;
use Siler\Route;

$handler = function ($req) {
    Route\get('/', 'pages/home.php');
    Swoole\emit('Not found', 404);
};

Swoole\http($handler)->start();
```

{% endcode %}

Now we are forwarding **GET** requests from path `/` to file `pages/home.php`.

{% code title="pages/home.php" %}

```php
<?php declare(strict_types=1);

use Siler\Swoole;

return fn() => Swoole\emit('Hello World');
```

{% endcode %}

{% hint style="info" %}
When using Swoole, routes that use files should return a function to ensure a re-computation. Siler will require the file **only** on the first match, then on the next matches it will only re-execute the returned function. This makes possible the use of `require_once` while maintaining a way to re-execute something.
{% endhint %}

You may ask: **"What about** `Swoole\emit('Not found', 404)` **at the end?"**.

Nice question! `Siler\Swoole\emit()` function will **short-circuit** further emit attempts, so it will work exactly like you have imagined, when a route matches a path like `/` it will emit the proper response, but when no route matches and this means: no route will emit something, then `Swoole\emit('Not found', 404)` will emit a **404 Not found** response.

Go ahead, restart the server and go to <http://localhost:9501/>, you should still be seeing "Hello World", but going to any other path, like <http://localhost:9501/banana>, you should be seeing "Not found" and a proper 404 status code.

### [Twig Templating](/main/twig-templating)

Twig should work exactly the same as there is no Swoole behind it:

```php
<?php declare(strict_types=1);

use Siler\Swoole;
use Siler\Twig;

return fn() => Swoole\emit(Twig\render('home.twig'));
```

```php
<?php declare(strict_types=1);
require_once 'vendor/autoload.php';

use Siler\Swoole;
use Siler\Route;
use Siler\Twig;

Twig\init('pages');

$handler = function ($req) {
    Route\get('/', 'pages/home.php');
    Swoole\emit('Not found', 404);
};

Swoole\http($handler)->start();
```

```python
<div data-gb-custom-block data-tag="extends" data-0='_layout.twig'>

<div data-gb-custom-block data-tag="block">

    <p>Hello World</p>

</div>
```

```markup
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Siler + Swoole</title>
</head>
<body>
    

<div data-gb-custom-block data-tag="block"></div>

</body>
</html>
```

{% hint style="info" %}
Swoole's HTTP server will auto-magically output the Response header Content-type as text/html instead of text/plain now.
{% endhint %}

If you're sure that your template doesn't depend on the request, you can render it once:

{% code title="pages/home.php" %}

```php
<?php declare(strict_types=1);

use Siler\Swoole;
use Siler\Twig;

$html = Twig\render('home.twig');

return fn() => Swoole\emit($html);
```

{% endcode %}

This avoids the template to be re-rendered on each request unnecessarily.

### Serving static assets

The `Siler\Swoole\http` function returns a plain `Swoole\Http\Server` so you can give it to a variable and use regular methods from Swoole's documentation like `set`:

{% tabs %}
{% tab title="index.php" %}

```php
/*
 ├───api
 ├───pages
 └───public
    └───assets
*/

$server = Swoole\http($handler);
$server->set([
    'enable_static_handler' => true,
    'document_root' => __DIR__ . '/public',
]);

$server->start();
```

{% endtab %}

{% tab title="pages/:layout.twig" %}

```markup
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Siler + Swoole</title>
    
    <link rel="stylesheet" href="/assets/styles.css">
    <script defer src="/assets/scripts.js"></script>
    
</head>
<body>
    

<div data-gb-custom-block data-tag="block"></div>

</body>
</html>
```

{% endtab %}
{% endtabs %}

## Request's Query string, Body & Headers

Since there is no web server module or CGI layer, things like $\_GET won't work for query string parameters etc. **But fear nothing**, Siler provides getters for both Swoole's Request and Response objects: `Siler\Swoole\request()` and `Siler\Swoole\response()`.

Instead of always printing "Hello World", let's print the name that came from the URL parameter:

```php
<?php declare(strict_types=1);

use Siler\Swoole;
use Siler\Twig;

return function () {
    $name = Swoole\request()->get['name'] ?? 'World';
    Swoole\emit(Twig\render('home.twig', ['name' => $name]));
};
```

```python
<div data-gb-custom-block data-tag="extends" data-0='_layout.twig'>

<div data-gb-custom-block data-tag="block">

    <p>Hello {{ name }}</p>

</div>
```

Go to <http://localhost:9501/?name=Leo>, you should be seeing "Hello Leo" now.

{% hint style="info" %}
You can find more about Swoole's Request and Response objects at: [swoole.co.uk/docs/modules/swoole-http-server/methods-properties](https://www.swoole.co.uk/docs/modules/swoole-http-server/methods-properties)
{% endhint %}

## Building an API

This is as simple as **Siler** gets.\
We can add a new route/API endpoint to **GET** all of our `Todos`:

{% code title="index.php" %}

```php
<?php declare(strict_types=1);
require_once 'vendor/autoload.php';

use Siler\Route;
use Siler\Swoole;
use Siler\Twig;

Twig\init('pages');

$handler = function ($req, $res) {
    Route\get('/', 'pages/home.php');
    Route\get('/todos', 'api/todos.php');

    // None of the above short-circuited the response with Swoole\emit().
    Swoole\emit('Not found', 404);
};

Swoole\http($handler)->start();
```

{% endcode %}

Then you can return your JSON and within `json()`, Siler will automatically add the Content-type: application/json response header. Also you can enable CORS.

{% code title="api/todos.php" %}

```php
<?php declare(strict_types=1);

use Siler\Swoole;

$todos = [
    ['id' => 1, 'text' => 'foo'],
    ['id' => 2, 'text' => 'bar'],
    ['id' => 3, 'text' => 'baz'],
];

return function () {
    Swoole\cors();
    Swoole\json($todos);
};
```

{% endcode %}

Head to <http://localhost:9501/todos>. There we go!\
A **Siler** ❤️ **Swoole** powered API.

{% hint style="info" %}
You can still use any other Swoole module like Coroutines and Redis. More abstractions to come.
{% endhint %}


# λ Functional

Functional programming treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data. It is declarative, which means expressions instead of statements.

Siler is bundled with the `Siler\Functional` namespace. It brings some function declarations that aids the work with another **first-class** and **high-order** PHP functions!

## `identity()`

Returns a Closure that returns its given arguments.

```php
use Siler\Functional as λ;

array_map(λ\identity(), [1, 2, 3]);
// [1, 2, 3]
```

{% hint style="info" %}
Doesn't seem useful at first, but working with the functional paradigm, you'll find the reason shortly.
{% endhint %}

## `always($value)`

Almost like `identity()`, but it always returns the given value.

```php
use Siler\Functional as λ;

array_map(λ\always('foo'), range(1, 3));
// [foo, foo, foo]
```

## `if_else(callable $cond) -> $then -> $else`

A functional if/then/else.

```php
use Siler\Functional as λ;

$pred = λ\if_else(λ\equal('foo'))(λ\always('is foo'))(λ\always('isnt foo'));

echo $pred('foo'); // is foo
echo $pred('bar'); // isnt foo
```

## `partial(callable $callable, ...$partial)`

Partial application refers to the process of fixing a number of arguments to a function, producing another function of smaller [arity](https://en.wikipedia.org/wiki/Arity). Given a function![{\displaystyle f\colon (X\times Y\times Z)\to N}](https://wikimedia.org/api/rest_v1/media/math/render/svg/5c7acf81877307746cd88e2785967d9a2f287107), we might fix (or 'bind') the first argument, producing a function of type ![{\displaystyle {\text{partial}}(f)\colon (Y\times Z)\to N}](https://wikimedia.org/api/rest_v1/media/math/render/svg/d45fcfd39c660c562ebd3da8158dbfd8f673836e). <https://en.wikipedia.org/wiki/Partial_application>

Nothing like a good example:

```php
use Siler\Functional as λ;

$add = function ($a, $b) {
    return $a + $b;
};

$add2 = λ\partial($add, 2);

echo $add2(3); // 5
```

Works with any `callable`:

```php
use Siler\Functional as λ;

$explodeCommas = λ\partial('explode', ',');
print_r($explodeCommas('foo,bar,baz'));

/**
 * Array
 * (
 *  [0] => foo
 *  [1] => bar
 *  [2] => baz
 * )
 */
```

## `match(array $matches)`

A pattern-match attempt. Truthy Closure evaluations on the left calls and short-circuits evaluations on the right.

```php
use Siler\Functional as λ;

$nameOf = λ\matching([
    [λ\equal(1), λ\always('one')],
    [λ\equal(2), λ\always('two')],
    [λ\equal(3), λ\always('three')],
]);

echo $nameOf(1); // one
echo $nameOf(2); // two
echo $nameOf(3); // three
```

{% hint style="info" %}
There are a lot more of them. A good place it check it out are [the tests](https://github.com/leocavalcante/siler/blob/master/tests/Unit/Functional/FunctionalTest.php).
{% endhint %}


# Concepts & Philosophy

## Classes, OOP, MVC - and where is the Controller?

A random guy on Reddit had this reaction after I published Siler:

> Functions everywhere, files used as methods, directories used as classes. I guess if someone wanted to see what a REST API would look like in PHP4, that's the answer.

After I questioned him why files and functions are so bad, he responded:

> It's hard to sum up in a short comment 5+ decades of industry evolution in structured and object oriented programming, versus just throwing everything in global space and piecing logic together through files. I suppose if I said things like polymorphism, dependency injection, abstraction, composition, it wouldn't mean much to you. This is why I just said it uses an obsolete PHP4 age approach. Whoever doesn't mind that, I hope they enjoy this framework.

### What decades of the OOP mantra has produced

It seems that people today (in the PHP community, at least) are just using classes and trying to apply OOP concepts without even realizing why they are doing it in the first place. They just hear somewhere that you **must** code using OO then avoid anything different like the plague. They don't know why or what is a class, but using one makes their code more OO-ish.

*So, not knowing why he is using it, he probably doesn't know how to respond why I shouldn't, and appealed to the bandwagon fallacy ("decades of industry").*

Disclaimer: I do not hate OOP. I just realized that like any other thing, OOP is a tool, and every tool you put in your stack must be added wisely.

### HTTP handlers do not necessarily benefit from OO

Take a closer look at this thing called a **Controller** from 99.9% of PHP frameworks. At the end of the day, they just behave like a group of functions (named **actions**) that are wrapped up inside of a class and called once in a request-response life cycle. They aren't doing any OO thing **at all**.\
They act like chunks of imperative code, just like a **file or function** does in Siler. There is no encapsulation or message-passing going on; there is no OO.

### When is OO a good fit?

IMHO, OO is excellent for domain modeling. When some behaviors are very deep intrinsic to its context, then I can think of no better way than to use a **Type** abstraction for it. Even a purely functional language like Haskell has a Type Class concept that implements definitions that can be properly understood as methods allowing polymorphism throught overloading.

It is pretty common, and it is easy to model and reason about a behavior/verb being attached to a type/substantive. This makes OO a very useful tool for modeling domains, because we see things this way. But it isn't how everything works, so OO isn't always the best approach.

### What is the right tool for the job?

We should keep things simple. Don't use a tool just to tell everyone that you are using it. Classes don't make your code more OO-ish and Controller-classes as HTTP entry points have no benefit from OO. People (frameworks) do it this way because Composer does not yet autoload functions (I hope it will), and because they want to accomplish MVC architecture--which is another tool that does not have any benefit in a unidirectional layer such HTTP. It's concepts were wildly spread and adopted, but it was designed for user interface programming where a Controller can properly listen for user interactions like mouse movements and Views can subscribe to Models.

### References

To finish, here are some related talks and articles:

* [Functional programming design patterns - Scott Wlaschin](https://www.youtube.com/watch?v=E8I19uA-wGY)
* [Object-Oriented Programming is Bad - Brian Will](https://www.youtube.com/watch?v=QM1iUe6IofM)
* [Stop Writing Classes - Jack Diederich](https://www.youtube.com/watch?v=o9pEzgHorH0)
* [Advanced OOP in Elixir - Wojtek Mach](https://www.youtube.com/watch?v=5EtV2JUU0Z4) *(spoiler - is an ironic talk)*
* [Was object-oriented programming a failure? - Wouter van Oortmerssen](https://www.quora.com/Was-object-oriented-programming-a-failure/answer/Wouter-van-Oortmerssen)


# Siler

> Simplicity is the ultimate sophistication. – Leonardo Da Vinci

You can use it within any framework or standalone, as a micro-framework:

```bash
composer require leocavalcante/siler
```

## A Hello World example

```php
use Siler\Functional as λ; // Just to be cool, don't use non-ASCII identifiers ;)
use Siler\Route;

Route\get('/', λ\puts('Hello World'));
```

This outputs "Hello World" when the file is reached via HTTP using the GET method and an URI path that matches "/". **Got the idea, right?**


# Routing

```php
use Siler\Route;

Route\get('/path', <handler>);
Route\post('/path', <handler>);
Route\put('/path', <handler>);
Route\delete('/path', <handler>);
Route\options('/path', <handler>);
```

Also a facade to catch **any HTTP method:**

```php
Route\any('/path', <handler>);
```

Additional and **custom HTTP methods** can be set using the `route` function:

```php
Route\route('custom', '/path', <handler>);
```

With an `array`, you can listen for **multiple HTTP methods** on the same handler:

```php
Route\route(['post', 'put'], '/path', <handler>);
```

### **Route parameters**

Route parameters can be defined using Regular Expressions:

```php
Route\get('/number/([0-9]+)', <handler>);
```

Or using a little syntax for creating named groups, that is just wrapping the parameter name around curly-brackets:

```php
Route\get('/number/{n}', <handler>);
```

{% hint style="info" %}
The above will match anything, not only numbers. For a fine-grained control, please use Regular Expressions.
{% endhint %}

#### Optional parameters

Optional named parameters can be defined using the question mark `?` as sufix:

```php
Route\get('/hello/{name}?', <handler>);
```

{% hint style="info" %}
To avoid the need of a trailing slash, add a question mark after it, like regex (because it is regex):

```php
Route\get('/hello/?{name}?', <handler>);
```

{% endhint %}

### Route handlers

The `<handler>` placeholder you saw is where you can put the route logic and it can be contained on the following:

#### Callables

As [Anonymous functions](http://php.net/manual/en/functions.anonymous.php):

```php
Route\get('/hello/{name}', function (array $routeParams) {
    echo 'Hello '.($routeParams['name'] ?? 'World');
});
```

As a [Closures](http://php.net/manual/en/class.closure.php):

```php
$handler = function (array $routeParams) {
    echo 'Hello World';  
};

function create_handler() {
    return function (array $routeParams) {
        echo 'Hello World';
    };
}

Route\get('/', $handler);
Route\get('/', create_handler());
```

As a method call on array-syntax:

```php
class Hello {
    public function world(array $routeParams) {
        echo 'Hello World';
    }
}

$hello = new Hello();
Route\get('/', [$hello, 'world']);
```

As a static method call string-syntax:

```php
class Hello {
    static public function world(array $routeParams) {
        echo 'Hello World';
    }
}

Route\get('/', 'Hello::world');
```

As any kind of [callable](http://php.net/manual/en/language.types.callable.php):

```php
class Hello {
    public function __invoke(array $routeParams) {
        echo 'Hello World';
    }
}

Route\get('/', new Hello());
```

#### Filenames

Handlers can be a String representing the filename of another PHP file, route parameters will be available at the global `$params` variable:

{% code title="index.php" %}

```php
Route\get('/hello/{name}', 'pages/home.php');
```

{% endcode %}

{% code title="pages/home.php" %}

```php
echo 'Hello '.$params['name'];
```

{% endcode %}

#### Resources

CRUD routes can be auto-magically be defined for convenience using the Rails and Laravel pattern.

Given this resource declaration:

```php
Route\resource('/users', 'api/users');
```

Siler will look for files at `path/to/files` matching the HTTP URI according to the table below:

| HTTP Verb | URI                | File                     |
| --------- | ------------------ | ------------------------ |
| GET       | `/users`           | `/api/users/index.php`   |
| GET       | `/users/create`    | `/api/users/create.php`  |
| POST      | `/users`           | `/api/users/store.php`   |
| GET       | `/users/{id}`      | `/api/users/show.php`    |
| GET       | `/users/{id}/edit` | `/api/users/edit.php`    |
| PUT       | `/users/{id}`      | `/api/users/update.php`  |
| DELETE    | `/users/{id}`      | `/api/users/destroy.php` |

The file structure should look like:

```
index.php
/api
└── /users
    ├─ index.php
    ├─ create.php
    ├─ store.php
    ├─ show.php
    ├─ edit.php
    ├─ update.php
    └─ destroy.php
```

#### Files

You can also let Siler create the routes recursively looking for files at a base path. Then the files names will be used to define the method and the path.

```php
Route\files('controllers');
```

Siler will interpret periods (.) as slashes and also maintain folder structure at HTTP path:

| Filename                | Method | Path       |
| ----------------------- | ------ | ---------- |
| `index.get.php`         | GET    | `/`        |
| `index.post.php`        | POST   | `/`        |
| `foo.get.php`           | GET    | `/foo`     |
| `bar/index.get.php`     | GET    | `/bar`     |
| `foo.bar.get.php`       | GET    | `/foo/bar` |
| `foo/bar.get.php`       | GET    | `/foo/bar` |
| `foo/bar/index.get.php` | GET    | `/foo/bar` |

Since `{` and `}` are valid chars in a filename, route parameters should work as well, but you can define required parameters prefixing with `$` and optional parameters using `@`:

| Filename             | Method | Path           |
| -------------------- | ------ | -------------- |
| `foo.{slug}.get.php` | GET    | `/foo/{slug}`  |
| `foo.$slug.get.php`  | GET    | `/foo/{slug}`  |
| `foo.@slug.get.php`  | GET    | `/foo/{slug?}` |

Any method is valid, it is guessed based on the penultimate "token":

| Filename           | Method   | Path   |
| ------------------ | -------- | ------ |
| `foo.options.php`  | OPTIONS  | `/foo` |
| `foo.x-custom.php` | X-CUSTOM | `/foo` |

{% hint style="info" %}
Note on handlers
{% endhint %}

When creating routes, be careful about **early** and **lazy** evaluations.

```php
Route\get('/foo', [new FooController(), 'index']);
```

The example above is **early**, which means it will call `FooController` constructor for each request even if it's not a request to `/foo`.

To make it **lazy** you can wrap inside a `Closure`:

```php
Route\get('/foo', function () {
  $controller = new FooController();
  return $controller->index();
});
```

Now `FooController` is called only when there is a match for route `/foo`.\
One downside is that now you have to explicitly manage path parameters, on the other hand is a best practice to do so.\
It is a good time to validate parameters, convert plain string parameters to meaningful types on your domain or resolve dependencies.

```php
Route\get('/users/{id}', function (array $params) use ($ioc) {  
  if (!preg_match('/[0-9]+/', $params['id']) {
    $controller = $ioc->resolve(ErrorController::class);
    return $controller->invalid('IDs must be numbers');
  }
  
  $controller = $ioc->resolve(UsersController::class);
  return $controller->show($params['id']);
});
```

### Request

#### Body

You can grab the raw request body using:

```php
use Siler\Http\Request;

$body = Request\raw();
```

Parse as URL-encoded data using:

```php
$params = Request\params();
```

Parse as JSON using:

```php
$resource = Request\json();
```

#### $\_GET and $\_POST superglobals

Or get data from a Form and from the Query String using:

```php
$input = Request\post('input');
$searchTerm = Request\get('q');
```

Calling them without arguments will tell Siler to return all the values as an `array`:

```php
$data = Request\post();
$queryString = Request\get();
```

You can also pass a default value if the key isn't present in the GET or POST super-globals:

```php
$input = Request\post('input', 'default-value');
```

#### Headers

Also conveniently get a header as easy as for the body:

```php
$contentType = Request\header('Content-Type');
```

*e.g. Serving both JSON and Form requests:*

```php
$data = Request\header('Content-Type') == 'json' ? Request\json() : Request\post();
```

### Response

Siler also have convenient functions to simplify HTTP responses.

You can output JSON encoded body with proper headers in just one line:

```php
use Siler\Http\Response;

Response\json(['error' => false, 'message' => 'It works']);
```

It will already output the given data, you don't need to call `echo` or `print` so use carefully, it's not a encoder, it's an output-er.

#### Headers

Same easy as for Request, you can set HTTP response headers with the `header` function at `Response` namespace:

```php
Response\header('Access-Control-Allow-Origin', 'https://know-domain.tld');
Response\header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
```


# PSRs & Middleware Pipeline

This interfaces are already (and amazingly) implemented by Laminas at projects: [Diactoros](https://github.com/laminas/laminas-diactoros) and [Stratigility](https://github.com/laminas/laminas-stratigility). Siler wraps them and exposes a function-friendly API handling state internally while achieving a fully-featured and declarative way for: **Middleware Pipelining**.

## PSR-7 HTTP Messages

{% hint style="info" %}
Siler doesn't have direct dependencies, to stay fit, it favors peer dependencies, which means you have to explicitly declare a `diactoros` dependency in your project in order to use it.
{% endhint %}

```bash
composer require laminas/laminas-diactoros
```

You can create a *superglobals* seeded `ServerRequest` with `Siler\Diactoros\request()`:

```php
use Siler\Diactoros;

$request = Diactoros\request();
```

And create Responses through helpers:

```php
$json = Diactoros\json(['some' => 'data']);
$html = Diactoros\html('<p>some markup</p>');
$text = Diactoros\text('plain text');
```

If none of them fits your needs, you can create a raw Response:

```php
$response = Diactoros\response();
$response->getBody()->write('something');
```

To emit a Response, there is no big deal, if you got Siler, you already imagined that is about one or two function calls, but this time we get the help from [HttpHandlerRunner](https://github.com/laminas/laminas-httphandlerrunner):

```bash
composer require laminas/laminas-httphandlerrunner
```

Then

```php
HttpHandlerRunner\sapi_emit($response);
```

As in `Siler\Http\Response` namespace functions, the `HttpHandlerRunner\sapi_emit` will output headers and text to the buffer, use it carefully.

Example:

```php
<?php declare(strict_types=1);

require_once 'vendor/autoload.php';

use Siler\Diactoros;
use Siler\HttpHandlerRunner;
use Siler\Route;
use function Siler\array_get;

$request = Diactoros\request();
$response = Route\match([
    // /greet/Leo?salute=Hello
    Route\get('/greet/{name}', function ($params) use ($request) {
        $salute = array_get($request->getQueryParams(), 'salute', 'Olá');
        return Diactoros\text("{$salute} {$params['name']}");
    }, $request),

    Route\get('/', function () {
        return Diactoros\text('hello world');
    }, $request),

    Diactoros\text('not found', 404),
]);

HttpHandlerRunner\sapi_emit($response);
```

## PSR-15 Middleware Pipelining

```bash
composer require laminas/laminas-stratigility
```

{% hint style="info" %}
Siler doesn't have direct dependencies, to stay fit, it favors peer dependencies, which means you have to explicitly declare a `stratigility` dependency in your project in order to use it.
{% endhint %}

A very simple Hello World example:

```php
use function Siler\Diactoros\request;
use function Siler\Diactoros\text;
use function Siler\HttpHandlerRunner\sapi_emit;
use function Siler\Stratigility\handle;
use function Siler\Stratigility\pipe;

pipe(function ($request, $handler) {
    return text('hello world');
});

sapi_emit(handle(request()));
```

It's more `use`s than actual code because Siler is abstracting all the way down for you.

| API         | Description                                                                                                                                                                                                                  |
| ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `pipe`      | Creates a Stratigility `MiddlewarePipe` with a default name and pipes the given Clousure to it already wrapping it inside a `MiddlewareInterface` decorator, or you can pass any implementation `MiddlewareInterface` to it. |
| `text`      | Creates a Diactoros `TextResponse`. The Diactoros namespace in Siler is basically just helper functions for Responses.                                                                                                       |
| `sapi_emit` | Creates and immediately calls `emit` method on a HttpHandlerRunner `SapiEmitter`.                                                                                                                                            |
| `handle`    | Calls `handle` on a `MiddlewarePipe` marshaling the Request.                                                                                                                                                                 |
| `request`   | Creates a Diactoros `ServerRequest` using PHP's Globals.                                                                                                                                                                     |

### Siler's Routes

You can also run pipelines for specific routes:

```php
use Siler\Diactoros;
use Siler\Http\Request;
use Siler\HttpHandlerRunner;
use Siler\Route;
use Siler\Stratigility;

$userMiddleware = function ($request, $handler) {
    $token = Request\get('token');

    if (empty($token)) {
        return Diactoros\json('no user', 401);
    }

    $user = "get_user_by_token:$token";
    $request = $request->withAttribute('user', $user);

    return $handler->handle($request);
};

$homeHandler = function () {
    return Diactoros\json('welcome');
};

$adminHandler = function ($request) {
    return Diactoros\json(['user' => $request->getAttribute('user')]);
};

$secretHandler = function ($request) {
    return Diactoros\json(['user' => $request->getAttribute('user')]);
};

Stratigility\pipe($userMiddleware, 'auth');

$request = Diactoros\request();
$response = Route\match([
    Route\get('/', $homeHandler, $request),
    Route\get('/admin', Stratigility\process($request, 'auth')($adminHandler), $request),
    Route\get('/secret', Stratigility\process($request, 'auth')($secretHandler), $request),
    Diactoros\json('not found', 404),
]);

HttpHandlerRunner\sapi_emit($response);
```

The second argument on `pipe` here is a Pipeline name, you can pipe middlewares to any number of pipelines, then in `Stratigility\process` we marshal it, from the given `$request` and returns a Closure to be called on a final handler.


# Twig Templating

```
composer require twig/twig
```

{% hint style="info" %}
Siler doesn't have direct dependencies, to stay fit, it favors peer dependencies, which means you have to explicitly declare a `twig` dependency in your project in order to use it.
{% endhint %}

Siler will internally handle the `Twig_Environment` instance.

```php
use Siler\Twig;

Twig\init('path/to/templates');
```

Actually it is also returned at `init` function call, so you call add Twig plugins, filters and functions, for example, adding `Siler\Http\url` into Twig's Environment to later reference static assets on the public folder:

```php
Twig\init('path/to/templates')
    ->addFunction(new Twig_SimpleFunction('url', 'Siler\Http\url'));
```

At initialization, you can also provide a path to templates cache as second argument and if you want to let Twig debug as third argument (defaults to `false`):

```php
$shouldTwigDebug = true;
Twig\init('path/to/templates', 'path/to/templates/cache', $shouldTwigDebug);
```

To render a template, simply call `render` at Twig namespace:

```php
echo Twig\render('pages/home.twig');
```

An passing parameters can be done by the second argument:

```php
$data = ['message' => 'Hello World'];
echo Twig\render('pages/home.twig', $data);
```

Something that can be confusing using Siler is that some function does outputs and other doesn't, like `Twig\render`. So remember that `Twig\render` will only return the rendered template within its given data and you should explicit output or let `Response` do it:

```php
$html = Twig\render('pages/home.twig');
Response\html($html);
```

Also, remember that **you can always bring you own template engine to the playground without any bridging stuff** or use PHP itself on your views.


# GraphQL

> GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data. GraphQL provides a complete and understandable description of the data in your API, gives clients the power to ask for exactly what they need and nothing more, makes it easier to evolve APIs over time, and enables powerful developer tools. — [graphql.org](http://graphql.org/)

Here is how you can create a GraphQL endpoint using Siler's simplicity powered by the [PHP's GraphQL implemention](http://webonyx.github.io/graphql-php/).

First, let's require it:

```
$ composer require webonyx/graphql-php
```

{% hint style="info" %}
Siler doesn't have direct dependencies, to stay fit, it favors peer dependencies, which means you have to explicitly declare a `graphql` dependency in your project in order to use it.
{% endhint %}

Now let's define our Schema. We're going to use a chat-like domain:

{% code title="schema.graphql" %}

```graphql
type Message {
  id: Int
  roomId: Int
  body: String
  timestamp: String
}

type Room {
  id: Int
  name: String
  messages: [Message]
}

type Query {
  messages(roomName: String): [Message]
  rooms: [Room]
}

type Mutation {
  start(roomName: String): Room
  chat(roomName: String, body: String): Message
}
```

{% endcode %}

Very simple, but if it's not familiar to you, take a look at [GraphQL](http://graphql.org/) first since this docs will not cover what is it, but how to use it.

For each Query and Mutation we can define our resolver functions. We'll be using [RedBean](http://www.redbeanphp.com/index.php) to help us as a simple SQLite storage ORM.

{% code title="resolvers.php" %}

```php
<?php

use RedBeanPHP\R;

R::setup('sqlite:'.__DIR__.'/db.sqlite');

$roomByName = function ($name) {
    return R::findOne('room', 'name = ?', [$name]);
};

$roomType = [
    'messages' => function ($room) {
        return R::findAll('message', 'room_id = ?', [$room['id']]);
    },
];

$queryType = [
    'rooms' => function () {
        return R::findAll('room');
    },
    'messages' => function ($root, $args) use ($roomByName) {
        $roomName = $args['roomName'];
        $room = $roomByName($roomName);
        $messages = R::find('message', 'room_id = ?', [$room['id']]);

        return $messages;
    },
];

$mutationType = [
    'start' => function ($root, $args) {
        $roomName = $args['roomName'];

        $room = R::dispense('room');
        $room['name'] = $roomName;

        R::store($room);

        return $room;
    },
    'chat' => function ($root, $args) use ($roomByName) {
        $roomName = $args['roomName'];
        $body = $args['body'];

        $room = $roomByName($roomName);

        $message = R::dispense('message');
        $message['roomId'] = $room['id'];
        $message['body'] = $body;
        $message['timestamp'] = new \DateTime();

        R::store($message);

        return $message;
    },
];

return [
    'Room'     => $roomType,
    'Query'    => $queryType,
    'Mutation' => $mutationType,
];
```

{% endcode %}

Awesome. We have type definitions and resolver functions. Let's put them together in a Schema:

{% code title="schema.php" %}

```php
<?php

use Siler\GraphQL;

$typeDefs = file_get_contents(__DIR__.'/schema.graphql');
$resolvers = include __DIR__.'/resolvers.php';

return GraphQL\schema($typeDefs, $resolvers);
```

{% endcode %}

Yeah, that simple! And it's exactly where Siler does it magic happen.\
Thanks to `webonyx/graphql-php` we can parse the `schema.graphql` into an actual Schema and Siler will override the default field resolver to work with the given `$resolvers`.

Now, let's create our HTTP endpoint:

{% code title="api.php" %}

```php
<?php

use Siler\GraphQL;
use Siler\Http\Request;
use Siler\Http\Response;

require 'vendor/autoload.php';

// Enable CORS
Response\cors();

// Respond only for POST requests
if (Request\method_is('post')) {
    // Retrive the Schema
    $schema = include __DIR__.'/schema.php';

    // Give it to siler
    GraphQL\init($schema);
}
```

{% endcode %}

#### **That's it!**

Start the server:

```bash
$ php -S localhost:8000 api.php
```

[**You can use Prima's GraphQL Playground to test it.**](https://github.com/prisma/graphql-playground)

Here are some queries you can execute:

Query available rooms:

```graphql
query {
  rooms {
    id
    name
  }
}
```

Yeah, there isn't any Rooms yet:

```javascript
{
  "data": {
    "rooms": []
  }
}
```

But. **It's working!**. Thanks to RedBean + SQLite we can play around without worrying about database setup and migrations.

Creating a new Room:

```graphql
mutation newRoom($roomName: String) {
  start(roomName: $roomName) {
    id
  }
}
```

**variables**

```javascript
{
  "roomName": "graphql"
}
```

Then our first room is created:

```javascript
{
  "data": {
    "start": {
      "id": 1
    }
  }
}
```

Call the query that fetches Rooms to check again:

```graphql
query {
  rooms {
    id
    name
  }
}
```

Yup! It's there:

```javascript
{
  "data": {
    "rooms": [
      {
        "id": 1,
        "name": "graphql"
      }
    ]
  }
}
```

Without any Messages yet:

```graphql
query roomMessages($roomName: String) {
  messages(roomName: $roomName) {
    id
    body
    timestamp
  }
}
```

**variables**

```javascript
{
  "roomName": "graphql"
}
```

No messages yet:

```javascript
{
  "data": {
    "messages": []
  }
}
```

So let's chat!

```graphql
mutation newMessage($roomName: String) {
  chat(roomName: $roomName, body: "hello") {
    id
  }
}
```

**variables**

```javascript
{
  "roomName": "graphql"
}
```

First message created:

```javascript
{
  "data": {
    "chat": {
      "id": 1
    }
  }
}
```

Let's refetch our messages to check:

```graphql
query roomMessages($roomName: String) {
  messages(roomName: $roomName) {
    id
    body
    timestamp
  }
}
```

**variables**

```javascript
{
  "roomName": "graphql"
}
```

Aha! Here we go:

```javascript
{
  "data": {
    "messages": [
      {
        "id": 1,
        "body": "hello",
        "timestamp": "2017-04-20 14:58:07"
      }
    ]
  }
}
```

Liked it? What about listening to added messages and enable real-time features?\
Sounds cool? That is **GraphQL Subscriptions** and we are going to cover next.

### GraphQL Subscriptions

Here is how you can add real-time capabilities to your GraphQL applications.

**Siler** implementation is based on [Apollo's WebSocket transport layer](https://github.com/apollographql/subscriptions-transport-ws).

{% embed url="<https://www.youtube.com/embed/wo9XFmW0W2c>" %}

We'll need some help to get WebSockets working, so let's require two libraries.

One is for the server-side:

```
$ composer require cboden/ratchet
```

And the other is for the client-side:

```
$ composer require textalk/websocket
```

First, let's add our **Subscription** type to our Schema:

{% code title="schema.graphql" %}

```graphql
# (...previous work...)

type Subscription {
  inbox(roomName: String): Message
}
```

{% endcode %}

Simple like that. We can subscribe to inboxes to receive new messages.

And our resolver will look like that:

{% code title="resolvers.php" %}

```php
# (...previous work...)

$subscriptionType = [
    'inbox' => function ($message) {
        return $message;
    },
];
```

{% endcode %}

Yeap, it is just resolving to the message that it receives.

**But where this message comes from?**

#### Siler powers!

Siler has a function at the `Graphql` namespace to define where your subscriptions are running:

```php
GraphQL\subscriptions_at('ws://127.0.0.1:3000');
```

Here we are assuming that they are running at localhost on port 8080.<br>

#### And why is that for?

This is just a helper that adds the **Subscriptions endpoint** to the Siler container so another function can actually use it when needed. And this function is `publish`!

`Siler\GraphQL\publish` will make a WebSocket call to the Subscriptions server notifying that something has happened.

```php
GraphQL\publish('inbox', $message);
```

It's first argument is the **Subscription** that will be triggered and the second argument is a data payload, in our case, the new message that has been created.

Our `resolvers.php` will look like this:

{% code title="resolvers.php" %}

```php
<?php

use RedBeanPHP\R;
use Siler\GraphQL;

R::setup('sqlite:'.__DIR__.'/db.sqlite');

// Here we set where our subscriptions are running
GraphQL\subscriptions_at('ws://127.0.0.1:3000');

$roomByName = function ($name) {
    return R::findOne('room', 'name = ?', [$name]);
};

$roomType = [
    'messages' => function ($room) {
        return R::findAll('message', 'room_id = ?', [$room['id']]);
    },
];

$queryType = [
    'rooms' => function () {
        return R::findAll('room');
    },
    'messages' => function () use ($roomByName) {
        $roomName = $args['roomName'];
        $room = $roomByName($roomName);
        $messages = R::find('message', 'room_id = ?', [$room['id']]);

        return $messages;
    },
];

$mutationType = [
    'start' => function ($root, $args) {
        $roomName = $args['roomName'];

        $room = R::dispense('room');
        $room['name'] = $roomName;

        R::store($room);

        return $room;
    },
    'chat' => function ($root, $args) use ($roomByName) {
        $roomName = $args['roomName'];
        $body = $args['body'];

        $room = $roomByName($roomName);

        $message = R::dispense('message');
        $message['roomId'] = $room['id'];
        $message['body'] = $body;
        $message['timestamp'] = new \DateTime();

        R::store($message);

        // Then we can publish new messages that arrives from the chat mutation
        GraphQL\publish('inbox', $message); // <- Exactly what "inbox" will receive

        return $message;
    },
];

// Our added Subscription type
$subscriptionType = [
    'inbox' => function ($message) { // <- Received from "publish"
        return $message;
    },
];

return [
    'Room'         => $roomType,
    'Query'        => $queryType,
    'Mutation'     => $mutationType,
    'Subscription' => $subscriptionType, // Add to the resolver functions array
];
```

{% endcode %}

### Starting the server

As in `api.php` endpoint we need to setup the Subscriptions server:

{% code title="subscriptions.php" %}

```php
<?php

use function Siler\{GraphQL, Ratchet};

require_once '/vendor/autoload.php';

$schema = require_once __DIR__ . '/schema.php';
$manager = GraphQL\subscriptions_manager($schema);

Ratchet\graphql_subscriptions($manager)->run();
```

{% endcode %}

Yeah, easy like that. Let Siler do the boring stuff. You just give a Schema to the `subscriptions` function. This function will return an `IoServer` where you can call the `run` method.

```bash
php subscriptions.php
```

By default, Siler will run the subscriptions at localhost on port 8080.

### Production-grade

To run subscriptions server at a production-grade level, please consider using some long-running process manager like Supervisor. Take a look at <http://socketo.me/docs/deploy#supervisor>.

#### That's it

No kidding.

Behind the scenes [ReactPHP](http://reactphp.org/), [Ratchet](http://socketo.me/) and [Pawl](https://github.com/ratchetphp/Pawl) are doing the hard work of handling WebSocket communication protocol while **Siler** is doing the work of adding resolver resolution to [webonyx/graphql-php](https://github.com/webonyx/graphql-php) and handling [Apollo's sub-protocol](https://github.com/apollographql/subscriptions-transport-ws#client-server-communication).

#### Ready to test?

[**You can use Prisma's GraphQL Playground to test the subscriptions as well.**](https://github.com/prisma/graphql-playground)

A subscription query looks like this:

```graphql
subscription newMessages($roomName: String) {
  inbox(roomName: $roomName) {
    id
    body
  }
}
```

**variables**

```javascript
{
  "roomName": "graphql"
}
```

The result will not be immediate since we are now listening to new messages, not querying them.

Let's take a mutation from the [previous guide](https://github.com/leocavalcante/siler/blob/master/docs/graphql/README.md). Open the Graph*i*QL app in another tab and execute:

```graphql
mutation newMessage($roomName: String) {
  chat(roomName: $roomName, body: "hello") {
    id
  }
}
```

**variables**

```javascript
{
  "roomName": "graphql"
}
```

Now go back to the subscription tab and see the update!

```graphql
{
  "inbox": {
    "id": 42,
    "body": "hello"
  }
}
```

**Awesome!** Just one thing you probably have noticed. We have subscribed to all rooms. Make a test: create another Room if you haven't already and add a new message to that to see that our subscription tab, with `{"roomName": "graphql"}` as variables, just got this new message. How to solve this?

### Filters

Filters are part of **Siler** and based on Apollo's setup functions. Filter functions receives the published payload data as the first argument and the subscription variables as the second, so you can use them to perform matches:

{% code title="subscriptions.php" %}

```php
<?php

use function Siler\{GraphQL, Ratchet};

require_once '/vendor/autoload.php';

$filters = [
    'inbox' => function ($payload, $vars) {
        return $payload['room_name'] == $vars['roomName'];
    },
];

$schema = require_once __DIR__ . '/schema.php';
$manager = GraphQL\subscriptions_manager($schema, $filters);

Ratchet\graphql_subscriptions($manager)->run();
```

{% endcode %}

As you can see, we have extend our Subscriptions endpoint adding filters. The filters array keys should match corresponding Subscription names, in our case: `inbox`. We are just checking if the given payload `room_name` is the same as the provided by the Subscription variable `roomName`. **Siler** will perform this checks for each subscription before trying to resolve and broadcast them.

We need just a little thing to get working. Adding this `room_name` field to our payload since Message only has the `room_id`. At the chat resolver, before the publish, add this line:

```php
$message['roomName'] = $roomName; // For the inbox filter
GraphQL\publish('inbox', $message); // <- Exactly what "inbox" will receive
```

{% hint style="info" %}
When a `RedBeanObject` is encoded to JSON it automatically converts camel case properties to underscore ones. That is why we give `roomName`, but receive as `room_name`.
{% endhint %}

And that should be enough to solve our problem, now you only receive data from the subscribed rooms. **Enjoy!**

**You can see a complete example including file uploads and directives at:**[ **github.com/leocavalcante/siler/examples/graphql**](https://github.com/leocavalcante/siler/tree/master/examples/graphql)**.**


# @Annotations

On the previous guide you saw how to map resolvers (callables) from a existing SDL (.graphql or .gql). Annotations enables the other way around, it provides a GraphQL SDL from annotated PHP code.

## Thank you Doctrine

Siler's GraphQL Annotations uses the super-powers from Doctrine's Annotations and like any other dependency, is a peer that we should explicitly require:

```
$ composer require doctrine/annotations
```

## What is available:

There are 9 annotations fulfilling the GraphQL's ecosystem:

Class annotations are:

* ObjectType
* InterfaceType
* InputType
* EnumType
* UnionType
* Directive

Complementary method and property annotations are:

* Field
* Args
* EnumVal

They follow a ubiquitous language to GraphQL spec, so if you know GraphQL, there is nothing new here, you probably already know what each of them does just by its name.

## Hello, World!

Let's start by defining our root query:

```php
<?php declare(strict_types=1);

namespace App;

use GraphQL\Type\Definition\ResolveInfo;
use Siler\GraphQL\Annotation\Field;
use Siler\GraphQL\Annotation\ObjectType;

/** @ObjectType */
class Query
{
    /** @Field(description="A common greet") */
    public static function hello(): string
    {
        return 'Hello, World!';
    }
}
```

The `ObjectType` name will be inferred by the class name, so it will already be *Query*.

Then we just provide this class to the `annotated` function on the `Siler\GraphQL` namespace:

```php
<?php declare(strict_types=1);

namespace App;

use function Siler\GraphQL\{annotated, init};

require_once __DIR__ . '/vendor/autoload.php';

$schema = annotated([Query::class]);
init($schema);
```

**And that is it!** It auto-magically servers the following SDL:

```graphql
type Query {
  """
  A common greet
  """
  hello: String!
}
```

With the static `hello` method body already playing the **resolver** role, so:

```graphql
query {
  hello
}
```

Returns:

```javascript
{
  "data": {
    "hello": "Hello, World!"
  }
}
```

**For a full-featured example, please take a look at:** [**github.com/leocavalcante/siler/examples/graphql-annotations**](https://github.com/leocavalcante/siler/tree/master/examples/graphql-annotations)

## Caching

Parsing docblocks can be expensive, on production environments is recommended to cache this process by using a caching reader.

First, install `doctrine/cache`:

```javascript
composer require doctrine/cache
```

Then pass a `Doctrine\Common\Cache\Cache` to `Siler\GraphQL\Deannotator::cache()` like:

```javascript
Siler\GraphQL\Deannotator::cache(new ApcuCache());
```

{% hint style="info" %}
Make sure you do this before the `annotated()` call.
{% endhint %}


# Web Servers

By its nature, Siler itself is made to be lightweight and dependent-less.\
This means you can use whatever web-service technology you want.\
Here, you'll find a few example configurations for some well-known web-services.

### Composer-serve script

Siler's default configuration bundled in the `siler/project` template gives a runnable configuration that fits well for development purposes.

You can use it, while being in the root directory of your project (the folder containing your `composer.json` file), by running

```
composer serve
```

or, if you didn't install composer globally,

```
php composer.phar serve
```

*Note: The `serve` script stored in the `composer.json` file runs `php -S 0.0.0.0:8000 -t .`*

### PHP CLI

The PHP CLI can also be enough for small production servers, like for example a raspberry pi running in your home.

To run your website using the PHP CLI, you can use this simple command (adapted to your project):

```
php -S {ip/domain}[:{port}] -t .
```

#### The `{ip/domain}` block

The `{ip/domain}` block is the interface on which you want your server to listen to.

You have multiple choices:

* `127.0.0.1` will run it on `localhost` and you'll be able to access it by going to `http://127.0.0.1` or `http://localhost`in your browser.
* `192.168.x.x` (your local IP) will listen to requests coming from your local IP.\
  You can retrieve it on \*nix systems by running `ifconfig` or `ip addr` and looking for an IP generally starting with `192.168.`.
* `0.0.0.0` will listen to requests coming from *every* network interface you have on your computer, that'd be `localhost`(the loopback), your local IP and more !

Additionally, the PHP CLI supports real domains, which means you can run your website by specifying the domain name of your computer instead of its local IP.

You can retrieve your computer's domain on \*nix systems with the simple command `hostname`.\
It'll return for example `jake-computer`.

You can then use this returned domain as the listening interface: `php -S jake-computer[:{port}] -t .`

#### The `[:{port}]` block

The `[:{port}]` block is the port you want to bind your process to.

> Important: On most \*nix systems, you can't set a port below 1025 without running it as root.\
> The usual ports used are `8080` and `8000`.

*Note: The `:{port}` block is optional, but recommended.*

### Apache

#### Subfolder

If you want to make this website available under a sub-folder of your Apache server, you'll need to make sure the `AllowOverride` contains `Options=Multiviews`, like:

```
<Directory /var/www/html>
    Options +FollowSymLinks +Multiviews
    AllowOverride All
    Order allow,deny
    Allow from all
</Directory>
```

Then, in your project's root folder, you can create a simple `.htaccess` file containing:

```
# HTTPD mod_rewrite required
RewriteEngine on
# If file/directory's present, serving it directly
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# Else, redirecting the request to the index.php file
RewriteRule . index.php
```

This file will try to see if the file or folder you're trying to access exists in the tree, then if it doesn't exist, it'll redirect the request to the `index.php` entry point in your project.

### Notes

There are a lot of other web services, like NGINX, LigHTTPD or IIS.\
If you want to provide a sample configuration for a server that's not listed here, don't hesitate to fork the repo, do your changes, then submit a pull request.


# Siler ❤️ Swoole

Flat files and plain-old PHP functions rocking on a production-grade, high-performance, scalable, concurrent and non-blocking HTTP server.

## Swoole

> Enables PHP developers to write high-performance, scalable, concurrent TCP, UDP, Unix socket, HTTP, Websocket services in PHP programming language without too much knowledge about non-blocking I/O programming and low-level Linux kernel. Compared with other async programming frameworks or softwares such as Nginx, Tornado, Node.js, Swoole has the built-in async, multiple threads I/O modules. Developers can use sync or async API to write the applications — [www.swoole.co.uk](https://www.swoole.co.uk/)

### Features

* Rapid development of high performance protocol servers & clients with PHP language
* Event-driven, asynchronous programming for PHP
* Event loop API
* Processes management API
* Memory management API
* [Golang style channels](https://en.wikipedia.org/wiki/Channel_%28programming%29) for inter-processes communication

### Use cases

* Web applications and systems
* Mobile communication systems
* Online game systems
* Internet of things
* Car networking
* Smart home systems

It is open source and free. Released under the license of Apache 2.0.

## Get started

{% hint style="info" %}
Forget about everything you know on how to run PHP on web servers like Apache and Nginx behind modules and CGI layers.
{% endhint %}

Swoole is released as a [PHP extension (PECL)](https://pecl.php.net/package/swoole) and runs as a PHP CLI application.\
The differences between Swoole with PHP-FPM the traditional PHP model are:

* Swoole forks a number of worker processes based on CPU core number to utilize all CPU cores.
* Swoole supports Long-live connections for websocket server or TCP/UDP server.
* Swoole supports more server-side protocols.
* Swoole can manage and reuse the status in memory.

### Docker

Swoole prerequisites operation system are: Linux, FreeBSD or MacOS, but **don't worry Windows-people**, *we* have [Docker](https://www.docker.com)! And I got us covered with [Dwoole](https://github.com/leocavalcante/dwoole):

{% code title="docker-compose.yml" %}

```yaml
version: '3'
services:
  swoole:
    container_name: siler_swoole
    image: leocavalcante/dwoole:dev
    ports:
      - '9501:9501'
    volumes:
      - ./:/app
```

{% endcode %}

{% hint style="info" %}
Beyond being cross-platform, Dwoole helps with others features like Composer and hot-restart that Unix people would also like.
{% endhint %}

## Siler

You already got Siler, right? Flat-files and plain-old PHP functions rockin'on! **Just simple**. A set of general purpose high-level abstractions aiming an API for declarative programming in PHP. **And this wouldn't be different about Swoole**.

The `Siler\Swoole` namespace get you covered.

### Hello World

{% code title="index.php" %}

```php
<?php declare(strict_types=1);

require_once 'vendor/autoload.php';

use Siler\Swoole;

$server = fn() => Swoole\emit('Hello World');

Swoole\http($server)->start();
```

{% endcode %}

That's it! This attaches a callback handler that always emits "Hello World" on every request and starts a HTTP server on port 9501. Run it using `docker-compose up` or just `php index.php` if you're not using Docker.

Go to `http://localhost:9051` or `http://<docker_machine_ip>:9051` and you should get a "Hello World" response as plain/text.

## More Siler!

You know, Siler can do a lot more, it abstracts things like [Routing](/routing) and [Twig Templating](/twig-templating). Let's add this to our **Swoole** server:

{% code title="index.php" %}

```php
<?php declare(strict_types=1);
require_once 'vendor/autoload.php';

use Siler\Swoole;
use Siler\Route;

$handler = function ($req) {
    Route\get('/', 'pages/home.php');
    Swoole\emit('Not found', 404);
};

Swoole\http($handler)->start();
```

{% endcode %}

Now we are forwarding **GET** requests from path `/` to file `pages/home.php`.

{% code title="pages/home.php" %}

```php
<?php declare(strict_types=1);

use Siler\Swoole;

return fn() => Swoole\emit('Hello World');
```

{% endcode %}

{% hint style="info" %}
When using Swoole, routes that use files should return a function to ensure a re-computation. Siler will require the file **only** on the first match, then on the next matches it will only re-execute the returned function. This makes possible the use of `require_once` while maintaining a way to re-execute something.
{% endhint %}

You may ask: **"What about** `Swoole\emit('Not found', 404)` **at the end?"**.

Nice question! `Siler\Swoole\emit()` function will **short-circuit** further emit attempts, so it will work exactly like you have imagined, when a route matches a path like `/` it will emit the proper response, but when no route matches and this means: no route will emit something, then `Swoole\emit('Not found', 404)` will emit a **404 Not found** response.

Go ahead, restart the server and go to <http://localhost:9501/>, you should still be seeing "Hello World", but going to any other path, like <http://localhost:9501/banana>, you should be seeing "Not found" and a proper 404 status code.

### [Twig Templating](/twig-templating)

Twig should work exactly the same as there is no Swoole behind it:

{% tabs %}
{% tab title="pages/home.php" %}

```php
<?php declare(strict_types=1);

use Siler\Swoole;
use Siler\Twig;

return fn() => Swoole\emit(Twig\render('home.twig'));
```

{% endtab %}

{% tab title="index.php" %}

```php
<?php declare(strict_types=1);
require_once 'vendor/autoload.php';

use Siler\Swoole;
use Siler\Route;
use Siler\Twig;

Twig\init('pages');

$handler = function ($req) {
    Route\get('/', 'pages/home.php');
    Swoole\emit('Not found', 404);
};

Swoole\http($handler)->start();
```

{% endtab %}

{% tab title="pages/home.twig" %}

```python
{% extends "_layout.twig" %}

{% block page %}
    <p>Hello World</p>
{% endblock %}
```

{% endtab %}

{% tab title="pages/\_layout.twig" %}

```markup
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Siler + Swoole</title>
</head>
<body>
    {% block page %}{% endblock %}
</body>
</html>
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
Swoole's HTTP server will auto-magically output the Response header Content-type as text/html instead of text/plain now.
{% endhint %}

If you're sure that your template doesn't depend on the request, you can render it once:

{% code title="pages/home.php" %}

```php
<?php declare(strict_types=1);

use Siler\Swoole;
use Siler\Twig;

$html = Twig\render('home.twig');

return fn() => Swoole\emit($html);
```

{% endcode %}

This avoids the template to be re-rendered on each request unnecessarily.

## Serving static assets

The `Siler\Swoole\http` function returns a plain `Swoole\Http\Server` so you can give it to a variable and use regular methods from Swoole's documentation like `set`:

{% tabs %}
{% tab title="index.php" %}

```php
/*
 ├───api
 ├───pages
 └───public
    └───assets
*/

$server = Swoole\http($handler);
$server->set([
    'enable_static_handler' => true,
    'document_root' => __DIR__ . '/public',
]);

$server->start();
```

{% endtab %}

{% tab title="pages/\_layout.twig" %}

```markup
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Siler + Swoole</title>
    
    <link rel="stylesheet" href="/assets/styles.css">
    <script defer src="/assets/scripts.js"></script>
    
</head>
<body>
    {% block page %}{% endblock %}
</body>
</html>
```

{% endtab %}
{% endtabs %}

## Request's Query string, Body & Headers

Since there is no web server module or CGI layer, things like $\_GET won't work for query string parameters etc. **But fear nothing**, Siler provides getters for both Swoole's Request and Response objects: `Siler\Swoole\request()` and `Siler\Swoole\response()`.

Instead of always printing "Hello World", let's print the name that came from the URL parameter:

{% tabs %}
{% tab title="pages/home.php" %}

```php
<?php declare(strict_types=1);

use Siler\Swoole;
use Siler\Twig;

return function () {
    $name = Swoole\request()->get['name'] ?? 'World';
    Swoole\emit(Twig\render('home.twig', ['name' => $name]));
};
```

{% endtab %}

{% tab title="pages/home.twig" %}

```python
{% extends "_layout.twig" %}

{% block page %}
    <p>Hello {{ name }}</p>
{% endblock %}
```

{% endtab %}
{% endtabs %}

Go to <http://localhost:9501/?name=Leo>, you should be seeing "Hello Leo" now.

{% hint style="info" %}
You can find more about Swoole's Request and Response objects at: [swoole.co.uk/docs/modules/swoole-http-server/methods-properties](https://www.swoole.co.uk/docs/modules/swoole-http-server/methods-properties)
{% endhint %}

## Building an API

This is as simple as **Siler** gets.\
We can add a new route/API endpoint to **GET** all of our `Todos`:

{% code title="index.php" %}

```php
<?php declare(strict_types=1);
require_once 'vendor/autoload.php';

use Siler\Route;
use Siler\Swoole;
use Siler\Twig;

Twig\init('pages');

$handler = function ($req, $res) {
    Route\get('/', 'pages/home.php');
    Route\get('/todos', 'api/todos.php');

    // None of the above short-circuited the response with Swoole\emit().
    Swoole\emit('Not found', 404);
};

Swoole\http($handler)->start();
```

{% endcode %}

Then you can return your JSON and within `json()`, Siler will automatically add the Content-type: application/json response header. Also you can enable CORS.

{% code title="api/todos.php" %}

```php
<?php declare(strict_types=1);

use Siler\Swoole;

$todos = [
    ['id' => 1, 'text' => 'foo'],
    ['id' => 2, 'text' => 'bar'],
    ['id' => 3, 'text' => 'baz'],
];

return function () {
    Swoole\cors();
    Swoole\json($todos);
};
```

{% endcode %}

Head to <http://localhost:9501/todos>. There we go!\
A **Siler** ❤️ **Swoole** powered API.

{% hint style="info" %}
You can still use any other Swoole module like Coroutines and Redis. More abstractions to come.
{% endhint %}


# λ Functional

Functional programming treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data. It is declarative, which means expressions instead of statements.

Siler is bundled with the `Siler\Functional` namespace. It brings some function declarations that aids the work with another **first-class** and **high-order** PHP functions!

## `identity()`

Returns a Closure that returns its given arguments.

```php
use Siler\Functional as λ;

array_map(λ\identity(), [1, 2, 3]);
// [1, 2, 3]
```

{% hint style="info" %}
Doesn't seem useful at first, but working with the functional paradigm, you'll find the reason shortly.
{% endhint %}

## `always($value)`

Almost like `identity()`, but it always returns the given value.

```php
use Siler\Functional as λ;

array_map(λ\always('foo'), range(1, 3));
// [foo, foo, foo]
```

## `if_else(callable $cond) -> $then -> $else`

A functional if/then/else.

```php
use Siler\Functional as λ;

$pred = λ\if_else(λ\equal('foo'))(λ\always('is foo'))(λ\always('isnt foo'));

echo $pred('foo'); // is foo
echo $pred('bar'); // isnt foo
```

## `partial(callable $callable, ...$partial)`

Partial application refers to the process of fixing a number of arguments to a function, producing another function of smaller [arity](https://en.wikipedia.org/wiki/Arity). Given a function![{\displaystyle f\colon (X\times Y\times Z)\to N}](https://wikimedia.org/api/rest_v1/media/math/render/svg/5c7acf81877307746cd88e2785967d9a2f287107), we might fix (or 'bind') the first argument, producing a function of type ![{\displaystyle {\text{partial}}(f)\colon (Y\times Z)\to N}](https://wikimedia.org/api/rest_v1/media/math/render/svg/d45fcfd39c660c562ebd3da8158dbfd8f673836e).\
<https://en.wikipedia.org/wiki/Partial_application>

Nothing like a good example:

```php
use Siler\Functional as λ;

$add = function ($a, $b) {
    return $a + $b;
};

$add2 = λ\partial($add, 2);

echo $add2(3); // 5
```

Works with any `callable`:

```php
use Siler\Functional as λ;

$explodeCommas = λ\partial('explode', ',');
print_r($explodeCommas('foo,bar,baz'));

/**
 * Array
 * (
 *  [0] => foo
 *  [1] => bar
 *  [2] => baz
 * )
 */
```

## `match(array $matches)`

A pattern-match attempt. Truthy Closure evaluations on the left calls and short-circuits evaluations on the right.

```php
use Siler\Functional as λ;

$nameOf = λ\match([
    [λ\equal(1), λ\always('one')],
    [λ\equal(2), λ\always('two')],
    [λ\equal(3), λ\always('three')],
]);

echo $nameOf(1); // one
echo $nameOf(2); // two
echo $nameOf(3); // three
```

{% hint style="info" %}
There are a lot more of them. A good place it check it out are [the tests](https://github.com/leocavalcante/siler/blob/master/tests/Unit/Functional/FunctionalTest.php).
{% endhint %}


# Concepts & Philosophy

## Classes, OOP, MVC - and where is the Controller?

This is a quote from a random guy on reddit after I published Siler:

> Functions everywhere, files used as methods, directories used as classes. I guess if someone wanted to see what a REST API would look like in PHP4, that's the answer.

That was his response after I questioned him why files and functions are bad:

> It's hard to sum up in a short comment 5+ decades of industry evolution in structured and object oriented programming, versus just throwing everything in global space and piecing logic together through files. I suppose if I said things like polymorphism, dependency injection, abstraction, composition, it wouldn't mean much to you. This is why I just said it uses an obsolete PHP4 age approach. Whoever doesn't mind that, I hope they enjoy this framework.

### Maybe a reflect from decades of inside-the-box OOP mantra

It seams that people today (in PHP community, at least) are just using classes and trying to apply OOP concepts without even realizing why they are doing it in first place. They just hear somewhere that you **must** code using OO then avoids anything different like the plague. They don't know why or what is a class, but using one makes their code more OO-ish.

*So, not knowing why he is using it, he probably don't know how to respond why I shouldn't, and called for a fallacy as "decades of industry"*.

Before going further, I'd like to disclaim that I do not hate OOP. I just realized that like any other thing, OOP is a tool, and every tool you put in your stack must be added wisely.

### HTTP entry points does not benefit from OO, at all

Take a closer look at this thing called **Controller** from 99.9% of PHP frameworks. At the end of the day, they just behave like a group of functions (named **actions**) masked under a class and called once in a request-response life cycle. They aren't doing any OO thing **at all**.\
They act like chunks of imperative code, just like a **file or function** does in Siler. There is no encapsulation or message-passing going on, there is no OO.

### When I think OO is a good fit

IMHO, OO is excellent for domain modeling. Some behaviors are very deep intrinsic to its context, then this rules will not have a better place than a **Type** abstraction for it. Even a purely functional language like Haskell has a Type Class concept that implements definitions that can be properly understood as methods allowing polymorphism throught overloading.

It is pretty common and easy to model and understand a behavior/verb being attached to a type/substantive. This makes OO a very useful tool to model a domain, 'cause we see things this way, but it isn't how everything works, OO isn't a bullet-proof concept.

### The right tool for the right job

We should keep things simple. Don't use a tool just to tell everyone that you are using it, classes don't make your code more OO-ish and Controller-classes as HTTP entry points have no benefit from OO, people (frameworks) just use it because Composer does not autoload functions (yet, I hope it will) and they want to accomplish MVC architecture that is another tool that does not have any benefit in a unidirectional layer such HTTP. Its concepts were wildly spread and adopted, but its was designed for user interface programming where a Controller can properly listen user interaction like mouse movements and Views can subscribe to Models.

To finish, I would like to leave some reference links, but do not bother with the click-bait titles.

* [Functional programming design patterns - Scott Wlaschin](https://www.youtube.com/watch?v=E8I19uA-wGY)
* [Object-Oriented Programming is Bad - Brian Will](https://www.youtube.com/watch?v=QM1iUe6IofM)
* [Stop Writing Classes - Jack Diederich](https://www.youtube.com/watch?v=o9pEzgHorH0)
* [Advanced OOP in Elixir - Wojtek Mach](https://www.youtube.com/watch?v=5EtV2JUU0Z4) *(spoiler - is an ironic talk)*
* [Was object-oriented programming a failure? - Wouter van Oortmerssen](https://www.quora.com/Was-object-oriented-programming-a-failure/answer/Wouter-van-Oortmerssen)

Thank you.


