BEAR.Sunday のリソースは、アプリケーション内のどこからでもURIを通じてアクセス可能です。URIを通じてというのは、ウェブからでもコマンドラインからでも同じリソースにアクセスできるということです。
これはこれで便利ですが、一方でコマンドラインからのみ利用したいような機能があったりします。え、ないですか?聞こえません。
この記事では、BEAR.Sundayでコマンドラインのみから利用できるリソースを作成してみます。
最終形
-
src/Resource/Command/
以下にリソースを作成できる -
src/Resource/Command/
以下のリソースがコマンドラインのみから利用できる
以下に順次課題を解決していきましょう。
1. src/Resource/Command/
以下にリソースを作成できる
BEAR.Sunday のディレクトリ構成のなかで、 src/Resource/
以下は特殊な意味を持っています。
リソースは World Wide Web において重要な概念で、アクセス可能な情報源のことを指しますが、BEAR.Sundayでも中心的な概念になっています。 src/Resource/
以下の情報がBEAR.Sundayにおける「リソース」です。このリソースには、WWWとおなじように、URI1を通じてアクセスできるようになっています。
Resource ディレクトリ配下の App
や Page
はURIにおけるスキーム2にマッピングされています。例えば、src/Resource/App/Hoge.php は内部的に app://self/hoge
でアクセス可能であり、 src/Resource/Page/Fuga.php は page://self/fuga
でアクセスできます。
BEAR.Sundayではデフォルトで app
page
http
スキームが登録されています。http
スキームは src/Resource 以下にないのですが、ウェブからアクセスしたときにデフォルトのスキームが利用されるようになっています(通常は page
スキーム)。
src/Resource/Command/
にリソースを登録するにはスキームを拡張する必要がありそうです。
scheme の拡張
カスタムスキームの追加については、以下の記事に詳しく書かれています。
BEAR.SundayのResourceにカスタムスキームを追加する方法
この記事を参考にしながら command://
というスキームを作成します。
まずは SchemeCollectionInterface
を返す Provider を作成します。
<?php
namespace Vendor\Package\Module;
use Ray\Di\ProviderInterface;
use BEAR\Resource\Annotation\AppName;
use BEAR\Resource\AppAdapter;
use Ray\Di\InjectorInterface;
use BEAR\Resource\Module\SchemeCollectionProvider;
final class CommandSchemeCollectionProvider implements ProviderInterface
{
/**
* @var string
*/
private $appName;
/**
* @var InjectorInterface
*/
private $injector;
/**
* @var SchemeCollectionProvider
*/
private $provider;
/**
* @param string $appName
*
* @AppName("appName")
*/
public function __construct($appName, InjectorInterface $injector, SchemeCollectionProvider $provider)
{
$this->appName = $appName;
$this->injector = $injector;
$this->provider = $provider;
}
public function get()
{
$collection = $this->provider->get();
$adapter = new AppAdapter($this->injector, $this->appName);
$collection->scheme('command')->host('self')->toAdapter($adapter);
return $collection;
}
}
このプロバイダを SchemeCollectionInterface
に束縛すれば command://
というスキームが追加されます。AppModule
などで
$this->bind(SchemaCollectionInterface::class)->toProvider(CommandSchemeCollectionProvider::class);
とすれば、 command://
というスキームが利用可能になります。
2. src/Resource/Command/
以下のリソースがコマンドラインのみから利用できる
ところで、お題としては「コマンドラインからのみ利用できるスキーム」です。これをこのまま AppModule
で束縛したらグローバルにスキームが追加され、ウェブからでも利用できてしまいます。どこでどのように束縛すれば良いでしょうか。
そんなときにはアプリケーションコンテキストを利用しましょう。
アプリケーションコンテキスト
BEAR.Sunday にはアプリケーションコンテキストという概念があります。
アプリケーションコンテキストはアプリケーションの実態がなにかを決定するものです(一番最初に起動する $app
オブジェクトの中身)。通常のフレームワークでは prod とか dev とかのコンテキストを渡してアプリケーションオブジェクトを初期化しますが、BEAR.SundayではこれがDecoratorによって表現されています。これによってモジュール3の組み合わせがほとんど無際限に拡がるので、BEAR.Sundayの拡張性のミソになっています。
bin/app.php
を見てみましょう。
<?php
require dirname(__DIR__) . '/autoload.php';
exit((require dirname(__DIR__) . '/bootstrap.php')(PHP_SAPI === 'cli' ? 'cli-hal-api-app' : 'hal-api-app'));
このハイフンつなぎの文字はそれぞれがコンテキストを表します。イメージとしてはこんなかんじのデコレーションになっています。
$app = new CliModule(new HalModule(new ApiModule(new AppModule))));
bootstrap に渡す値は任意です。
HogeModule
が読み込み可能のとき、 hoge-app
を bootstrap にわたせば new HogeModule(new App)
になります。
これを使えば「コマンドラインからのみ利用できる」ような機能が作れそうです。
bin/command.php
みたいなものを作って、アプリケーション初期化時に cli-command-app
を渡して、 CommandModule
を作成してみます。
CommandModule
はこんな感じです。
<?php
namespace Vendor\Package\Module;
use Ray\Di\AbstractModule;
use BEAR\Sunday\Annotation\DefaultSchemeHost;
use BEAR\Resource\SchemeCollectionInterface;
use BEAR\Resource\Module\SchemeCollectionProvider;
/**
* CommandSchemeCollectionProvider を利用して command:// スキームを追加
* src/Resource/Command 以下にリソースを作成できるようにする
*/
class CommandModule extends AbstractModule
{
public function configure()
{
$this->bind(SchemeCollectionProvider::class);
$this->bind(SchemeCollectionInterface::class)->toProvider(CommandSchemeCollectionProvider::class);
$this->bind()->annotatedWith(DefaultSchemeHost::class)->toInstance('command://self');
}
}
annotatedWith(DefaultSchemeHost::class)
をしているのは、BEAR.Sunday内部でURIを通じてリソースにアクセスする際に、ルーティング処理がデフォルトスキームを選択する(通常は page://
スキーム)ためです。このモジュールが読み込まれるときだけは command://
スキームをデフォルトにしましょう。
仕上げに、 bin/command.php
を作ってこのモジュールを読み込むようデコレーションします。
<?php
require dirname(__DIR__) . '/autoload.php';
exit((require dirname(__DIR__) . '/bootstrap.php')('cli-command-app'));
これで完成です。
リソースの作成
あとはリソースを作成するだけです。
<?php
namespace Vendor\Package\Resource\Command;
use BEAR\Resource\ResourceObject;
class Index extends ResourceObject
{
public function onGet(): ResourceObject
{
$this['command'] = 'hogehoge';
$this['test'] = 'fugafuga';
return $this;
}
}
これで、次のようにアクセスしてみます。
$ php bin/command.php get /
200 OK
Content-Type: application/json
{
"command": "hogehoge",
"test": "fugafuga"
}
bin/app.php
やウェブからだと、CommandModuleが読み込まれていないので、 src/Resource/Command/
以下のリソースにはアクセスできません。
まとめ
BEAR.Sundayにおけるスキームの拡張と、アプリケーションコンテキストを見てきました。このような拡張が必要になるかどうかは状況次第ですが、こういった組み合わせの自由さはフレームワークでみることがあまりないかなと思います。
いろいろな使い方を試してみて、思うように拡張してみるのもBEAR.Sundayというフレームワークの楽しさだと思うので、ぜひ皆さんもいろいろ試してみてください。