monogatari-php / remote-storage
A library for implementating Monogatari.io RemoteStorage endpoint.
Requires
- php: >=8.0
- psr/http-factory: ^1.0
- psr/http-message: ^1.0
Requires (Dev)
- codeception/codeception: ^4.1
- codeception/module-asserts: ^1.0.0
- codeception/module-webdriver: ^2.0
- guzzlehttp/psr7: ^2.1
- phpstan/phpstan: ^1.3
- phpunit/php-code-coverage: ^9.2
README
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:
StorageFactoryInterface
: The object to initialize aStorageInterface
with theStorageRequest
provided.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
- Basic Example: A simple example with FileSystemStorage.
- 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).