Skip to content
This repository has been archived by the owner on Oct 14, 2022. It is now read-only.

Architecture guide #33

Draft
wants to merge 6 commits into
base: dev
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 108 additions & 0 deletions docs/architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# Understanding the code architecture behind Rialto

This guide is here to help you understand how Rialto works behind the scene and ease code contributions. Before reading it, make sure you understand the basic usage of Rialto [by reading the tutorial](https://github.com/rialto-php/rialto/blob/dev/docs/tutorial.md).

## Process communication

Basically, when you instanciate the entrypoint of a Rialto implementation, a Node process is spawned with some configuration provided by PHP. Once running, the Node process opens a socket on a random port (provided by the operating system) and outputs the port on the stdout stream, which will be retrieved by the PHP process. Once the Node port is retrieved, the PHP process connects to the socket with the provided port. The processes are now communicating!

## The entrypoint

When you write a Rialto implementation, you start by creating an entrypoint, it's a simple PHP class inheriting the [`AbstractEntryPoint`](https://github.com/rialto-php/rialto/blob/architecture-guide/src/AbstractEntryPoint.php) class. It has 2 roles:

- Once instanciated, it starts the Node process (via the [`ProcessSupervisor`](https://github.com/rialto-php/rialto/blob/architecture-guide/src/ProcessSupervisor.php) class) and opens a connection with it.
- All instructions made on the entrypoint (property read/write, method call, etc…) will be intercepted and send to the default resource set in the connection handler.

## The connection delegate

The connection delegate is required and its main tasks are:

- defining the default resource;
- executing the instructions;
- handling errors.

### The default resource

The default resource is the underlying JavaScript object you will use when calling instructions on the PHP entrypoint.

For example, [PuPHPeteer defines its default resource](https://github.com/rialto-php/puphpeteer/blob/f9a9c17d62076e5e5652df38d38fe26fc565b6f8/src/PuppeteerConnectionDelegate.js#L31) with the result of `require('puppeteer')`:

```js
async handleInstruction(instruction, responseHandler, errorHandler) {
const puppeteer = require('puppeteer')
instruction.setDefaultResource(puppeteer)

// ...
}
```

That means, when you instanciate the `Nesk\Puphpeteer\Puppeteer` class and call a method on it, on the JS side the method will be called on the default resource.

When you write:

```php
$puppeteer = new Nesk\Puphpeteer\Puppeteer;
$puppeteer->launch();
```

Node will execute:

```js
const puppeteer = require('puppeteer')
puppeteer.launch()
```

### Instruction execution and error handling

Instructions are not automatically executed by Rialto, instead it provides the necessary objects to let the connection delegate execute the instructions the way it wants. For example, [here's how PuPHPeteer implements this whole process](https://github.com/rialto-php/puphpeteer/blob/f9a9c17d62076e5e5652df38d38fe26fc565b6f8/src/PuppeteerConnectionDelegate.js#L35-L43):

```js
async handleInstruction(instruction, responseHandler, errorHandler) {
// ...

try {
// The "instruction" object has a simple "execute()" method which will run the code sent by PHP.
value = await instruction.execute()
} catch (error) {
// We always catch the errors, however we rethrow them if the code
// sent by PHP doesn't explicitly require the errors to be catched.
// See: https://github.com/rialto-php/rialto/blob/3f3420ad/docs/api.md#node-errors
if (instruction.shouldCatchErrors()) {
return errorHandler(error)
}

throw error
}

responseHandler(value)
}
```

### Other usages

The connection delegate can also be used to track some resources for various tasks. For example, PuPHPeteer tracks [the `Page` objects](https://pptr.dev/#?product=Puppeteer&version=v5.3.1&show=api-class-page) to [log the console messages](https://github.com/rialto-php/puphpeteer/blob/f9a9c17d62076e5e5652df38d38fe26fc565b6f8/src/PuppeteerConnectionDelegate.js#L54-L56) and output them in the PHP logger [if the user asked for it](https://github.com/rialto-php/puphpeteer/tree/f9a9c17d62076e5e5652df38d38fe26fc565b6f8#puppeteers-class-must-be-instantiated).

## Instruction flow

When you execute an instruction on the PHP side, it will be:

- intercepted by the [`CommunicatesWithProcessSupervisor` trait](https://github.com/rialto-php/rialto/blob/df5a6b1b2c15a742773f48baaf1ac763664591de/src/Traits/CommunicatesWithProcessSupervisor.php);
- converted to a [PHP `Instruction`](https://github.com/rialto-php/rialto/blob/df5a6b1b2c15a742773f48baaf1ac763664591de/src/Instruction.php#L10) and serialized;
- sent to the Node process;
- [unserialized](https://github.com/rialto-php/rialto/blob/df5a6b1b2c15a742773f48baaf1ac763664591de/src/node-process/Data/Unserializer.js) and converted to a [JS `Instruction`](https://github.com/rialto-php/rialto/blob/df5a6b1b2c15a742773f48baaf1ac763664591de/src/node-process/Instruction.js);
- sent to the connection delegate.

Once the connection delegate produces a return value, it will be:

- [serialized](https://github.com/rialto-php/rialto/blob/df5a6b1b2c15a742773f48baaf1ac763664591de/src/node-process/Data/Serializer.js);
- sent back to the PHP process, which is in a blocking state until a response is provided (or until [`read_timeout`](https://github.com/rialto-php/rialto/blob/df5a6b1b2c15a742773f48baaf1ac763664591de/docs/api.md#options) is reached);
- [unserialized](https://github.com/rialto-php/rialto/blob/df5a6b1b2c15a742773f48baaf1ac763664591de/src/Data/UnserializesData.php);
- provided as the return value of the original instruction.

>describe resources and how they are tracked

# TODO

- instruction flow
- basic resource, specific resources, and resource repository
- php delegate