# Siler ❤️ Swoole

## 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.md) and [Twig Templating](/main/twig-templating.md). 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.md)

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 %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://siler.leocavalcante.dev/main/swoole.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
