We will create a bridge to use Node's File System module in PHP. This is not especially useful but it will show you how Rialto works and handles pretty much everything for you.
Import Rialto in your project:
composer require nesk/rialto
npm install @nesk/rialto
You will need to create at least two files for your package:
An entry point (FileSystem.php
): this PHP class inherits AbstractEntryPoint
and its instanciation creates the Node process. Every instruction (calling a method, setting a property, etc…) made on this class will be intercepted and sent to Node.
use Nesk\Rialto\AbstractEntryPoint;
class FileSystem extends AbstractEntryPoint
{
public function __construct()
{
parent::__construct(__DIR__.'/FileSystemConnectionDelegate.js');
}
}
A connection delegate (FileSystemConnectionDelegate.js
): this JavaScript class inherits ConnectionDelegate
and will execute the instructions made with PHP (calling a method, setting a property, etc…).
const fs = require('fs'),
{ConnectionDelegate} = require('@nesk/rialto');
module.exports = class FileSystemConnectionDelegate extends ConnectionDelegate
{
handleInstruction(instruction, responseHandler, errorHandler)
{
// Define on which resource the instruction should be applied by default,
// here we want to apply them on the "fs" module.
instruction.setDefaultResource(fs);
let value = null;
try {
// Try to execute the instruction
value = instruction.execute();
} catch (error) {
// If the instruction fails and the user asked to catch errors (see the `tryCatch` property in the API),
// send it with the error handler.
if (instruction.shouldCatchErrors()) {
return errorHandler(error);
}
throw error;
}
// Send back the value returned by the instruction
responseHandler(value);
}
}
With these two files, you should already be able to use your bridge:
use Nesk\Puphpeteer\Fs\FileSystem;
$fs = new FileSystem;
$stats = $fs->statSync('/valid/file/path'); // Returns a basic resource representing a Stats instance
$stats->isFile(); // Returns true if the path points to a file
Note: You should use the synchronous methods of Node's FileSystem module. There is no way to handle asynchronous callbacks with Rialto for the moment.
The example above returns a BasicResource
class when the JavaScript API returns a resource (typically, a class instance). See this example:
$buffer = $fs->readFileSync('/valid/file/path'); // Returns a basic resource representing a Buffer instance
$stats = $fs->statSync('/valid/file/path'); // Returns a basic resource representing a Stats instance
Its possible to know the name of the resource class:
$buffer->getResourceIdentity()->className(); // Returns "Buffer"
$stats->getResourceIdentity()->className(); // Returns "Stats"
However, this is not convenient. That's why you can create specific resources to improve that. We will create 3 files:
A process delegate (FileSystemProcessDelegate.php
): this PHP class implements ShouldHandleProcessDelegation
and is responsible to return the class names of the specific and default resources.
use Nesk\Rialto\Traits\UsesBasicResourceAsDefault;
use Nesk\Rialto\Interfaces\ShouldHandleProcessDelegation;
class FileSystemProcessDelegate implements ShouldHandleProcessDelegation
{
// Define that we want to use the BasicResource class as a default if resourceFromOriginalClassName() returns null
use UsesBasicResourceAsDefault;
public function resourceFromOriginalClassName(string $jsClassName): ?string
{
// Generate the appropriate class name for PHP
$class = "{$jsClassName}Resource";
// If the PHP class doesn't exist, return null, it will automatically create a basic resource.
return class_exists($class) ? $class : null;
}
}
A resource to represent Buffer instances (BufferResource.php
): this class inherits BasicResource
by convenience but the only requirement is to implement the ShouldIdentifyResource
interface.
use Nesk\Rialto\Data\BasicResource;
class BufferResource extends BasicResource
{
}
A resource to represent Stats instances (StatsResource.php
):
use Nesk\Rialto\Data\BasicResource;
class StatsResource extends BasicResource
{
}
Once those 3 files are created, you will have to register the process delegate in your entry point (FileSystem.php
):
use Nesk\Rialto\AbstractEntryPoint;
class FileSystem extends AbstractEntryPoint
{
public function __construct()
{
// Add the process delegate in addition to the connection delegate
parent::__construct(__DIR__.'/FileSystemConnectionDelegate.js', new FileSystemProcessDelegate);
}
}
Now you will get specific resources instead of the default one:
$fs->readFileSync('/valid/file/path'); // Returns BufferResource
$fs->statSync('/valid/file/path'); // Returns StatsResource
$fs->statSync('/valid/file/path')->birthtime; // Returns a basic resource representing a Date instance
Specific resources can also help you to improve your API by adding methods to them:
use Nesk\Rialto\Data\BasicResource;
class StatsResource extends BasicResource
{
public function birthtime(): \DateTime
{
return (new \DateTime)->setTimestamp($this->birthtimeMs / 1000);
}
}
$fs->statSync('/valid/file/path')->birthtime(); // Returns a PHP's DateTime instance
Your first bridge with Rialto is ready, you can learn more by reading the API documentation.