10
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

BEAR.SundayAdvent Calendar 2019

Day 15

BEAR.Sundayでコマンドラインだけから利用できるリソースを作る

Last updated at Posted at 2019-12-14

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 ディレクトリ配下の AppPage は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というフレームワークの楽しさだと思うので、ぜひ皆さんもいろいろ試してみてください。

  1. URIは Uniform Resource Identifierの略。基本的にはBEAR.Sundayの「リソース」という概念もここから来ていると思いますが、ここでは議論のスコープから外します。

  2. 余談ですが、http などの「スキーム」のスペルは scheme で、JsonSchema のスペルは schema 、どちらもよく使うBEAR.Sundayでは混乱必定です。

  3. BEAR.Sundayでのモジュールは、DIにおける束縛を決定します。読み込みたいライブラリなどはモジュールで定義します。

10
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?