monogatari-php/remote-storage

A library for implementating Monogatari.io RemoteStorage endpoint.

v1.0 2022-02-04 11:31 UTC

This package is auto-updated.

Last update: 2024-04-28 10:59:48 UTC


README

CI badge-coverage Packagist

A RemoteStorage implementation for the Monogatari visual novel engine.

Usage

To use a remote storage solution, first you should configure your Monogatari in options.js with the Storage setting:

// Define what storage engine should be used to save the game data. *
// Adapters Available:
// - LocalStorage: This one is used by default
// - SessionStorage: Same as LocalStorage but will be cleared when the page
//                   is closed.
// - IndexedDB: The information is saved using the IndexedDB web API
// - RemoteStorage: The information will be sent and retrieved from a given
//                  URL Endpoint providing a REST API.
'Storage': {
    'Adapter': 'RemoteStorage',
    'Store': 'MyStore',
    'Endpoint': '/gameSave.php/'
},

Then you need install the library. You can install it from packagist:

composer require monogatari-php/remote-storage

Finally you can write the PHP code to serve the Endpoint mentioned in the config above (i.e. /gameSave.php):

use GuzzleHttp\Psr7\HttpFactory;
use Monogatari\RemoteStorage\Http\Controller;
use Monogatari\RemoteStorage\StorageEngine\FileSystemStorage;
use Monogatari\RemoteStorage\StorageEngine\SimpleStorageFactory;

require_once 'vendor/autoload.php';

$httpFactory = new HttpFactory();
$controller = new Controller(
    new SimpleStorageFactory(fn () => (new FileSystemStorage('./gameSave.json'))),
    $httpFactory, // StreamFactoryInterface implementation
    $httpFactory, // ResponseFactoryInterface implementation
);
Controller::emit($controller->handleRequest(Controller::requestFromEnvironment()));

We used the Guzzle library in the example to demonstrate functionality. But we're not limiting support to only Guzzle. The function Controller::handleRequest returns PSR-7 compliant ResponseInterface. Should be safe to use in any modern PHP framework.

Multiple StorageEngine implementations come with this library, include file system, mysql, postgres and sqlite. If you need other storage solution, implementing StorageInterface should be easy. See source code in StorageEngine folder for more details.

Lifecycle

The basic lifecycle of a request handle starts with formulating a StorageRequest. You can use the function Controller::requestFromEnvironment or you can write your own.

The request created will then be feed to the StorageFactoryInterface implementation (e.g. SimpleStorageFactory). The factory will determine what StorageInterface to load.

Then the controller will use the StorageInterface to handle the RESTful request.

Extending the Library

The request from Controller::requestFromEnvironment only accounted for a very narrow use case (PHP_INFO based routing). It can be rewritten / appended before feeding to the controller instance.

The 2 main interfaces to override would be:

  1. StorageFactoryInterface: The object to initialize a StorageInterface with the StorageRequest provided.
  2. StorageInterface: The object to create, load, save, delete values.

Should be quite flexible to extend.

Advanced Example

This example demonstrates how to use the library with PSR-7 ServerRequest, which is commonly implemented and used by php web frameworks like Symfony, Laravel and Druapl.

use GuzzleHttp\Psr7\HttpFactory;
use GuzzleHttp\Psr7\ServerRequest;
use Monogatari\RemoteStorage\Http\Controller;
use Monogatari\RemoteStorage\Http\StorageRequest;
use Monogatari\RemoteStorage\StorageEngine\DatabaseStorage\MySqlStorage;
use Monogatari\RemoteStorage\StorageEngine\SimpleStorageFactory;

require_once 'vendor/autoload.php';

// Connect to database
$pdo = new \PDO('mysql:host=127.0.0.1;port=3306;dbname=test', 'test_user', 'test_password');

// Prepare Controller
$httpFactory = new HttpFactory();
$controller = new Controller(
    new SimpleStorageFactory(
        fn (StorageRequest $request) => new MySqlStorage(
            $pdo,
            'monogatari_storage',
            $request->store_name,
            $request->context['username'] ?? '',
        ),
    ),
    $httpFactory, // StreamFactoryInterface implementation
    $httpFactory, // ResponseFactoryInterface implementation
);

// Parse StoreRequest from PSR-7 ServerRequest
$request = ServerRequest::fromGlobals();
if (!preg_match('/\/gameSave\.php(|\/((?<store_name>.+)\/|)(?<key>.*?))$/', $request->getUri()->getPath(), $uriVariables)) {
    throw new \Exception('Unable to parse uri: ' . $request->getUri()->getPath());
}
$uriVariables += ['store_name' => null, 'key' => null];
preg_match('/^(?<username>.+?)(|:(?<password>.*))$/', $request->getUri()->getUserInfo() ?? '', $userContext);
$userContext += ['username' => '', 'password' => ''];
$storageRequest = new StorageRequest(
    $request->getMethod(),
    $uriVariables['store_name'],
    $uriVariables['key'],
    ($request->getQueryParams()['keys'] ?? null) === 'true',
    $request->getBody(),
    $userContext,
);

// Generate PSR-7 Response
$response = $controller->handleRequest($storageRequest);

// Emit the Response
Controller::emit($response);

Full Examples

  1. Basic Example: A simple example with FileSystemStorage.
  2. Advanced Example: A more complete example with MySqlStorage and PSR-7 ServerRequest

License

This library is licensed under the MIT License (a copy is attached here).