[Zend Framework] Zend_Application動作原理の翻訳

Zend_Application動作原理の原文はこちら。

4.3. 動作原理

MVCアプリケーションを設定して動かすためには、データベース、ビューやビューヘルパー、レイアウトなどの設定や、プラグイン、アクションヘルパーの登録などなど、多くの機能を利用するためのコードがたくさん必要になる。

加えて、テストやcronタスク、サービススクリプトなどを実行するために、同じコードを再利用したいこともよくあるだろう。*1そういう処理は、ブートストラップ(起動準備処理*2)に書いてしまうのが簡単なやり方だが、環境依存の初期化処理が必要となることもあるだろう。たとえば、cronタスクにMVCはいらないし、サービススクリプトにはDBレイヤーだけあればいい。

Zend_Applicationでは、OOPによりブートストラップをカプセル化して再利用可能にすることで、そのような要望に応えようとしている。

Zend_Applicationは以下の3つの領域をサポートする。

  • Zend_Application: PHP環境設定の読み込み、インクルードパスおよび(クラスの)オートロードへの対応、対応するブートストラップのインスタンス化。
  • Zend_Application_Bootstrap: ブートストラップへのインターフェース。Zend_Application_Bootstrap_Bootstrapが、依存性チェックや必要に応じたリソースの読み込みなど、ブートストラップで必要となる機能群を提供する。
  • Zend_Application_Resource: ブートストラップによって必要に応じてロードされるリソースや、いくつかのデフォルトのリソースなど、標準的なリソースへのインターフェースを提供する。

開発者はアプリケーションのために、Zend_Application_Bootstrap_Bootstrapクラスを継承するか、あるいはZend_Application_Bootstrap_Bootstrapperインターフェースを(最低限)実装したブートストラップクラスを作成する。その呼び出し部(=public/index.php)ではZend_Applicationが読み込まれ、以下のパラメータを使ってインスタンス化される。

  • 現状の環境(定義名)
  • ブートストラップオプション群

ブートストラップオプション群は、ブートストラップクラスが書かれたファイルへのパスを含み、その他オプションとして以下のような要素を持つ。

  • 追加するインクルードパス
  • オートローダーに追加登録するネームスペース
  • 初期化に利用するphp.ini設定
  • ブートストラップクラスのクラス名(未指定ならば"Bootstrap")
  • ペアで使われるリソースのパスとプレフィックス*3
  • 利用するリソース(クラス名もしくは短縮名)
  • 追加でロードする設定ファイルのパス
  • その他の設定値

設定値はPHPの配列、Zend_Configオブジェクト、もしくは設定ファイルへのパスで表現する。

4.3.1. ブートストラップ

Zend_Applicationの二つ目の役割は、アプリケーションのブートストラップ(起動準備処理)を行うことだ。ブートストラップはZend_Application_Bootstrap_Bootstrapperインターフェースに以下のようなAPIとして定義されており、それらを最低限実装する必要がある。


interface Zend_Application_Bootstrap_Bootstrapper
{
public function __construct($application);
public function setOptions(array $options);
public function getApplication();
public function getEnvironment();
public function getClassResources();
public function getClassResourceNames();
public function bootstrap($resource = null);
public function run();
}

このAPIは、環境(定義名)と設定オプションをアプリケーションオブジェクトから受け取り、ブートストラップ時に用意する必要があるリソース群を知らせ、そしてブートストラップとアプリケーションの実行を行うためのものだ。

これらのインターフェースをスクラッチで、あるいはZend_Application_Bootstrap_BoostrapAbstractを継承して、あるいはZend_Application_Bootstrap_Bootstrapを利用して実装することができる。

これ以外にも、あなたが慣れなければならないことはたくさんある。

4.3.1.1. リソースメソッド

Zend_Application_Bootstrap_BootstrapAbstractの実装では、クラスにリソースメソッドというものを定義するようになっている。_initから始まるprotectedメソッドは、すべてリソースメソッドとして扱われる。

一つのリソースメソッドを実行するには、bootstrap()メソッドにそのリソース名を渡して呼ぶ。リソース名とは、メソッド名から_initを取り除いたものだ。

いくつかのリソースメソッドを実行したいならリソース名の配列を渡せばいい。すべてのリソースメソッドを実行する場合は引数なしで呼ぶ。

以下のブートストラップクラスを見てみよう。


class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
protected function _initFoo()
{
// ...
}

protected function _initBar()
{
// ...
}

protected function _initBaz()
{
// ...
}
}

_initFoo()メソッドを実行したい場合は、以下のようにする。


$bootstrap->bootstrap('foo');

_initFoo()と_initBar()メソッドを実行したい場合は、以下のようになる。


$bootstrap->bootstrap(array('foo', 'bar'));

すべてのリソースメソッドを実行する場合は、引数なしでbootstrap()を呼ぶ。


$bootstrap->bootstrap();

4.3.1.2. リソースプラグインを利用したブートストラップ

よりブートストラップの再利用性を高くしたい人のために、リソースプラグインという仕組みも用意されている。これは(コードではなく)設定を使ってシンプルにリソースを利用できるようにする。後で作り方について説明するが、ここではまず使い方についてのみ説明する。

もしブートストラップでリソースプラグインを使いたいならば、Zend_Application_Bootstrap_ResourceBootstrapperインターフェースを追加実装する必要がある。このインターフェースはリソースプラグインの配置や登録、読み込みを行う。


interface Zend_Application_Bootstrap_ResourceBootstrapper
{
public function registerPluginResource($resource, $options = null);
public function unregisterPluginResource($resource);
public function hasPluginResource($resource);
public function getPluginResource($resource);
public function getPluginResources();
public function getPluginResourceNames();
public function setPluginLoader(Zend_Loader_PluginLoader_Interface $loader);
public function getPluginLoader();
}

リソースプラグインが提供する基本機能は、アプリケーションをまたがって再利用可能なリソースの初期化機構だ。これにより、ブートストラップを比較的クリーンに保つことができる。ブートストラップコードに手を加えず、新しいリソースを導入することができるのだ。

Zend_Application_Bootstrap_BootstrapAbstract(とそれを継承したZend_Application_Bootstrap_Bootstrap)にはこのインターフェースも実装されており、リソースプラグインを利用できるようになっている。

リソースプラグインを利用するには、アプリケーションオブジェクトやブートストラップのオプションとして、その設定を行う必要がある。これらは設定ファイルやオプションパラメータとして渡される。設定はリソース名のキーを持つ値のペアとなる。リソース名はクラスプレフィックスの後に続く部分となる。たとえばZend Frameworkに同梱されるリソースは、"Zend_Application_Resource_"というクラスプレフィックスを持ち、その後にリソース名が続くことになる。例を挙げると、


$application = new Zend_Application(APPLICATION_ENV, array(
'resources' => array(
'FrontController' => array(
'controllerDirectory' => APPLICATION_PATH . '/controllers',
),
),
));

これは、(controllerDirecotry)オプションが指定された"FrontController"リソースが使われることを示している。

独自のリソースプラグインを作る、もしくはサードパーティ製のリソースプラグインを使う場合、ブートストラップにそれらがどこにあるかを教えておく必要がある。ブートストラップは内部でZend_Loader_PluginLoaderを使うので、クラスプレフィックスと検索パスのペアが必要となる。

たとえば、APPLICATION_PATH/resources/に独自のリソースプラグインがあるとして、そのクラスプレフィックスがMy_Resourceだとすると、アプリケーションオブジェクトに以下のような情報を渡すことになる。


$application = new Zend_Application(APPLICATION_ENV, array(
'pluginPaths' => array(
'My_Resource' => APPLICATION_PATH . '/resources/',
),
'resources' => array(
'FrontController' => array(
'controllerDirectory' => APPLICATION_PATH . '/controllers',
),
),
));

これで指定したディレクトリのリソースを使えるようになる。

リソースメソッドのように、bootstrap()メソッドを使ってリソースプラグインを実行できる。リソースメソッド同様に、一つのリソースプラグイン、複数のプラグイン(配列を使って)、すべてのプラグインを指定できる。加えていうと、リソースメソッドと混在して実行することも可能だ。


// 一つ実行
$bootstrap->bootstrap('FrontController');

// 複数実行
$bootstrap->bootstrap(array('FrontController', 'Foo'));

// すべてのプラグインとメソッドを実行
$bootstrap->bootstrap();

4.3.1.3. リソースレジストリ

たいていのリソースメソッドやプラグインは、オブジェクトの初期化を行う。そして多くの場合、そのオブジェクトはアプリケーションの他のところでも利用される。その場合、オブジェクトにはどうやってアクセスすればいいだろうか?

Zend_Application_Bootstrap_BootstrapAbstractは、それらのオブジェクトを保存するためのローカルなレジストリを用意しており、(リソースメソッドやプラグインで)リソースオブジェクトをreturnするだけで保存が行われる。

柔軟性を高くするために、レジストリは内部では"container"として参照される。オブジェクトだけがそこに保存される。リソースはリソース名にちなんだプロパティとして登録される。デフォルトでは、Zend_Registryインスタンスが利用されるが、他のオブジェクトを使うこともできる。setContainer()、getContainer()メソッドを使ってコンテナを操作できる。getResource($resource)でコンテナからリソースを取得し、hasResource($resource)でリソースが登録済みかどうかを調べることができる。

以下の例は、基本的なビューリソースだと思って欲しい


class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
protected function _initView()
{
$view = new Zend_View();
// more initialization...

return $view;
}
}

登録されているか確認したり、リソースを取得したりするのは、以下のようなコードとなる。


// has/getResource()の二つを使う
if ($bootstrap->hasResource('view')) {
$view = $bootstrap->getResource('view');
}

// コンテナを通して
$container = $bootstrap->getContainer();
if (isset($container->view)) {
$view = $container->view;
}

レジストリやコンテナがグローバルではないことに気をつけて欲しい。つまり、リソース(オブジェクト)を取得するにはブートストラップにアクセスする必要があるのだ。Zend_Application_Bootstrap_Bootstrapはそのための方法を用意している。run()が実行されている間、ブートストラップは自分自身をフロントコントローラの"bootstrap"パラメータとして登録している。それを使えば、ルーターやディスパッチャー、プラグインやアクションコントローラからリソースオブジェクトにアクセスできるようになる。

たとえばアクションコントローラの中からビューリソースにアクセスしたい場合は、以下のようになる。


class FooController extends Zend_Controller_Action
{
public function init()
{
$bootstrap = $this->getInvokeArg('bootstrap');
$view = $bootstrap->getResource('view');
// ...
}
}

4.3.1.4. 依存性の追跡

リソースメソッドやプラグインを実行するだけでなく、それらが必ず1度だけ実行されることを保証する必要がある。アプリケーションをブートストラップするたびに、(リソースの初期化処理が)複数回実行されてしまうのではオーバーヘッドを招くことになるからだ。

また、いくつかのリソースは他のリソースに依存している可能性がある。この二つの問題を解決するために、Zend_Application_Bootstrap_BootstrapAbstractは簡単で効果的な依存性の追跡機構を備えている。

前記の通り、すべてのリソース(メソッドもしくはプラグイン)は、bootstrap($resource)メソッドの$resource指定によって、一つのリソース、もしくは複数のリソース(配列)、もしくはすべてのリソース(引数なし)がブートストラップされるようになっている。

あるリソースが他のリソースに依存する場合、依存先のリソースがすでにブートストラップ済みであることを保証するために、依存元のリソース(メソッドやプラグイン)コードの中で(依存先のリソースをブートストラップする)bootstrap()メソッドを呼んでおくべきだ。その後でもしも同じリソースがブートストラップされたとしても、その呼び出しは無視されることになる。

リソースメソッド内でのそのような(依存するリソースの)呼び出しを行う場合、以下のように書く。


class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
protected function _initRequest()
{
// フロントコントローラが初期化済みであることを保証する
$this->bootstrap('FrontController');

// ブートストラップレジストリからフロントコントローラを取得する
$front = $this->getResource('FrontController');

$request = new Zend_Controller_Request_Http();
$request->setBaseUrl('/foo');
$front->setRequest($request);

// リクエストオブジェクトがブートストラップレジストリに保存されるようにする
return $request;
}
}

4.3.2. リソースプラグイン

前記の通り、再利用性の高いブートストラップリソースを作成し、あなたのコードから関係が薄い(リソース初期化の)コードを取り除きたいのならば、リソースプラグインを利用するといいだろう。Zend Frameworkはいくつもの標準的なリソースプラグインを同梱しているが、その意図は、開発者は独自に必要となるような初期化コードのみを書くべきだと考えるからだ。

リソースプラグインはZend_Application_Resource_Resourceインターフェースを実装すればいい。より簡単にはZend_Application_Resource_ResourceAbstractクラスを継承すればいい。基本インターフェースはシンプルだ。


interface Zend_Application_Resource_Resource
{
public function __construct($options = null);
public function setBootstrap(
Zend_Application_Bootstrap_Bootstrapper $bootstrap
);
public function getBootstrap();
public function setOptions(array $options);
public function getOptions();
public function init();
}

このインターフェースには、リソースはコンストラクタでオプションを受け取る、オプションの設定や取得が可能、ブートストラップオブジェクトの設定や取得が可能、そして初期化を実行するメソッドが定義されている。

次のサンプルは、アプリケーションで利用される一般的なビューの初期化処理を想定している。ドキュメントタイプ、CSSJavaScriptを扱い、オプションから得た基本ドキュメントタイトルを渡す。そのような機能を持つリソースは以下のようになる。


class My_Resource_View extends Zend_Application_Resource_ResourceAbstract
{
protected $_view;

public function init()
{
// ビューを返し、レジストリに保存する
return $this->getView();
}

public function getView()
{
if (null === $this->_view) {
$options = $this->getOptions();
$title = '';
if (array_key_exists('title', $options)) {
$title = $options['title'];
unset($options['title']);
}

$view = new Zend_View($options);
$view->doctype('XHTML1_STRICT');
$view->headTitle($title);
$view->headLink()->appendStylesheet('/css/site.css');
$view->headScript()->appendfile('/js/analytics.js');

$viewRenderer =
Zend_Controller_Action_HelperBroker::getStaticHelper(
'ViewRenderer'
);
$viewRenderer->setView($view);

$this->_view = $view;
}
return $this->_view;
}
}

このリソースプラグインプレフィックスパスを登録すると、アプリケーションから使えるようになる。プラグインローダーを利用することにより、デフォルトの"View"リソースプラグインが上書きされ、独自のプラグインが代わりに利用されるようになる。

*1:サービススクリプトって、定期実行するわけではないコマンドライン系の処理のことか? たとえば初期セットアップスクリプトとか。日本語でもサービススクリプトっていうんだろうか? cronの方ならバッチが一番近い感じなんだけど。

*2:ブートストラップは日本語にせずに、そのままカタカナで書いてしまうことにした

*3:Bootstrapで使われているpluginpaths設定のことかなー。だったら「リソースのプレフィックスとパスのペア」って感じになる?

Zend_Applicationクイックスタートの翻訳

Zend_Applicationクイックスタートの原文はこちら

4.2. Zend_Applicationクイックスタート

Zend_Applicationによる開発をスタートする方法は、プロジェクトの始め方によって二通りある。どちらの場合でも、Bootstrapクラスと関連する設定ファイルを作成することから始まる。

新しいプロジェクトの作成にZend_Toolを使うのならば、このまま続きをどうぞ。すでに作りかけているプロジェクトにZend_Applicationを追加したい場合は、リンク先までスキップを。

4.2.1. Zend_Toolを使う場合

Zend_Toolを使ってプロジェクトスケルトンを生成するのが、もっとも素早いZend_Applicationを使ったアプリケーション開発の始め方だ。これによりBootstrapクラス&ファイルなども生成される。

プロジェクトを生成するには、zfコマンド(*nixシステムでは)を実行する。


% zf create project newproject

Windowsではzf.batコマンドとなる。


C:> zf.bat create project newproject

どちらも以下のようなプロジェクトスケルトンを生成する。


newproject

-- application
-- Bootstrap.php
-- configs
`-- application.ini
-- controllers
-- ErrorController.php
`-- IndexController.php
-- models
`-- views
-- helpers
`-- scripts
-- error
`-- error.phtml
`-- index
`-- index.phtml
-- library
-- public
-- .htaccess*1
`-- index.php

`-- tests
|-- application
| `-- bootstrap.php
|-- library
| `-- bootstrap.php
`-- phpunit.xml

上図の中で、起動処理(bootstrap)はnewproject/application/Bootstrap.phpに置かれ、以下のような内容となっている。


class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
}

設定ファイルが、newproject/configs/applicaiton.iniとして生成されている点にも注目しよう。以下のような内容となっている。


[production]
phpSettings.display_startup_errors = 0
phpSettings.display_errors = 0
includePaths.library = APPLICATION_PATH "/../library"
bootstrap.path = APPLICATION_PATH "/Bootstrap.php"
bootstrap.class = "Bootstrap"
resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers"

[staging : production]

[testing : production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1

[development : production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1

ここに書かれたすべての設定は、Zend_Applicationおよび起動処理で使われる。

他には、Zend_Applicationの呼び出し(invoke)・実行(dispatch)を行うnewproject/public/index.phpファイルも興味深い。


// アプリケーションディレクトリの定義
defined('APPLICATION_PATH')
|| define('APPLICATION_PATH',
realpath(dirname(__FILE__) . '/../application'));

// アプリケーション環境の定義
defined('APPLICATION_ENV')
|| define('APPLICATION_ENV',
(getenv('APPLICATION_ENV') ? getenv('APPLICATION_ENV')
: 'production'));

/** Zend_Application */
require_once 'Zend/Application.php';

// アプリケーションの生成・起動・実行
$application = new Zend_Application(
APPLICATION_ENV,
APPLICATION_PATH . '/configs/application.ini'
);
$application->bootstrap()
->run();

このままクイックスタートを続けるなら、リソースの項までスキップを。

4.2.2. Zend_Applicationを既存のアプリケーションに追加する場合

Zend_Applicationの基本はとてもシンプルだ。

  • Bootstrapクラスが書かれたapplication/Bootstrap.phpファイルを作成する。
  • Zend_Applicationに必要とされる最低限の設定が書かれた設定ファイル、application/configs/application.iniを作成する。
  • public/index.phpをZend_Applicationを利用するように書き換える。

最初に、Bootstrapクラスを作成しよう。以下のような内容のapplication/Bootstrap.phpファイルを作成する。


class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
}

続いて、設定ファイルを用意する。このチュートリアルでは、INI形式の設定ファイルを利用するが、XMLPHP形式の設定ファイルを利用してもかまわない。以下のような内容を持つ、application/configs/application.iniを作成しよう。


[production]
phpSettings.display_startup_errors = 0
phpSettings.display_errors = 0
includePaths.library = APPLICATION_PATH "/../library"((「includePaths.library = APPLICATION_PATH "/../library"」みたいな記述って可能だったんだ。INIファイル内で定数が展開されるってのは知ってたけど、定数と文字列を連結したりも出来るのね。))
bootstrap.path = APPLICATION_PATH "/Bootstrap.php"
bootstrap.class = "Bootstrap"
resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers"

[staging : production]

[testing : production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1

[development : production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1

今度は窓口となるスクリプト、public/index.phpを修正しよう。もしそのようなファイルが存在しない場合は作成し、以下のような内容に書き換えよう。*2


// アプリケーションディレクトリの定義
defined('APPLICATION_PATH')
|| define('APPLICATION_PATH',
realpath(dirname(__FILE__) . '/../application'));

// アプリケーション環境の定義
defined('APPLICATION_ENV')
|| define('APPLICATION_ENV',
(getenv('APPLICATION_ENV') ? getenv('APPLICATION_ENV')
: 'production'));

// 通常は、あなたのlibrary/ディレクトリもinclude_pathに追加したいだろう。
// 特にそこにZFがインストールされている場合などは。
set_include_path(implode(PATH_SEPARATOR, array(
dirname(dirname(__FILE__)) . '/library',
get_include_path(),
)));

/** Zend_Application */
require_once 'Zend/Application.php';

// アプリケーションの生成・起動・実行
$application = new Zend_Application(
APPLICATION_ENV,
APPLICATION_PATH . '/configs/application.ini'
);
$application->bootstrap()
->run();

アプリケーション環境定数が、環境変数"APPLICATION_ENV"から取得されていることに注目して欲しい。Webサーバーの環境変数として設定する方法を推奨している。Apacheではバーチャルホスト設定もしくは.htaccessで設定できる。public/.htaccess*3に以下のように記述することで、推奨設定を実現できる。


SetEnv APPLICATION_ENV development

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
RewriteRule ^.*$ index.php [NC,L]



mod_rewriteに関して

上記のリライトルールは、バーチャルホストのドキュメントルート以下に置かれたすべてのファイルへのアクセスを許可する。公開されたくないファイルがある場合は、よりアクセスを制限するルールが必要だ。ApacheのWebサイトでmod_rewriteについてより詳しく調べるといいだろう。

これであなたはZend_Applicationのすべての恩恵を受ける準備が整った。*4

4.2.3. リソースの追加と作成

ここまで指示通りに進めてきたなら、あなたの起動(bootstrap)クラスはフロントコントローラを使って動作するようになっているだろう。しかし、たいていの場合もうちょっと設定をいじる必要が出てくるはずだ。

このセクションでは、あなたのアプリケーションに二つのリソースを追加していく。まずレイアウトのセットアップを行い、それからビューオブジェクトのカスタマイズを行う。

"layout"リソースは、Zend_Applicationが提供する標準リソースの一つだ。これを使うには、Zend_Layoutインスタンスの設定を行うための設定値を指定しておく必要がある。

これを使うためには、設定ファイルを更新するだけでいい。


[production]
phpSettings.display_startup_errors = 0
phpSettings.display_errors = 0
bootstrap.path = APPLICATION_PATH "/Bootstrap.php"
bootstrap.class = "Bootstrap"
resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers"
; 以下の行を追加
resources.layout.layout = "layout"
resources.layout.layoutPath = APPLICATION_PATH "/layouts/scripts"

[staging : production]

[testing : production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1

[development : production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1

もしまだ、application/layouts/scriptsディレクトリとその中のlayout.phtmlファイルを作っていないなら、それらを用意しよう。最初のレイアウトとしては、以下のような内容がいいだろう(そして、これは次に説明するビューリソースと連動する)。


<?php echo $this->doctype() ?>
<html>
<head>
<?php echo $this->headTitle() ?>
<?php echo $this->headLink() ?>
<?php echo $this->headStyle() ?>
<?php echo $this->headScript() ?>
</head>
<body>
<?php echo $this->layout()->content ?>
</body>
</html>

これでレイアウト機能が動くようになった。

続いて、ビューのカスタマイズ機能を追加しよう。ビューを初期化する際に、HTMLのドキュメントタイプ情報やHTMLのHEAD部分で利用するタイトルの初期値をセットしたい。これは、Bootstrapクラスにメソッドを追加することで実現できる。


class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
protected function _initView()
{
// ビューの初期化
$view = new Zend_View();
$view->doctype('XHTML1_STRICT');
$view->headTitle('My First Zend Framework Application');

// ViewRendererに追加
$viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper(
'ViewRenderer'
);
$viewRenderer->setView($view);

// returnすることでbootstrapに保存される
return $view;
}
}

このメソッドは、アプリケーションを起動すると自動的に実行され、アプリケーションが必要なときにはビューが確実に初期化されていることが保証される。

4.2.4. Zend_Applicationを使った次のステップ

ここまでの説明で、Zend_Applicationの利用を開始し、アプリケーションを起動させることができるようになっただろう。ここからはリソースメソッドや最高の再利用性のためのリソースプラグインクラスの作成を始めていく。続きを読んで、より詳しく学ぼう。

*1:原文では.htaccessが書かれていないけど、あった方がいいだろう

*2:「; otherwise」の接続がよくわからなかったんで適当

*3:ここだけ本文では.htaccesってsが一個抜けてるな

*4:たぶんyou're all set toって言い回しがあるんだろうけど、よくわからないから雰囲気で適当に

Zend Frameworkのお勉強

さて、それではそろそろプロトタイプでも作り始めるかと思ったのだが、Zend Frameworkがいつの間にか1.8.5までアップデートされていた。特に1.8以降ではZend_ApplicationやZend_Toolなどが追加されており、ディレクトリ構成や起動ファイル、設定ファイルの書き方の作法が従来から変更(というか、新しく策定)されている。そのため、私の古い知識ではその部分をきちんと調べないと、プロトタイプを書き始めることすらできない。

というわけで、プロトタイプを書き始まる前に、Zend Framework 1.8以降で新しく策定されたZend Frameworkアプリケーションの標準的な書き方というものについて調べていく。まだZend_Applicationのマニュアルの日本語訳が一部しかないみたいなんで、翻訳しながら読み進めていきつつ、適当の訳した文章も参考までにここに載せておく。

[設計] URLの設計

DB設計によって内部的なサービスのイメージがだいぶ固まってきたので、今度はURL設計を行うことで、外部から見たサービスのイメージを固めていく。

  • / - サイトトップページ。
  • /login - ログイン。
  • /logout - ログアウト。
  • /about - サイトの説明。
  • /search - 検索ページ。
  • /config - ユーザー設定変更。
  • /register - ユーザー登録。
  • /home/ - ログインしたユーザーのホームページ。
  • /uris/ - URI統計ページ。新着、注目など。
  • /uri/[uri_string]/ - [uri_string]というURLに関するページ。
  • /asins/ - ASIN統計ページ。同上。
  • /asin/[asin]/ - [asin]というASINに関するページ。
  • /location/[longitude]/[latitude]/ - [longitude]、[latitude]という位置に関するページ。
  • /tags/ - タグ統計ページ。同上。
  • /tag/[tagname]/ - [tagname]というタグに関するページ。
  • /dj/[personality_name]/ - [personality_name]のトップページ。
  • /dj/[personality_name]/date/[yyyy]/[mm]/[dd]/ - [personality_name]の[yyyy][mm][dd]の更新。
  • /dj/[personality_name]/talk/[talk_id]/ - [personality_name]の[talk_id]発言。

あまり機能を増やしすぎても、あまり使われないわりにはサービスが重くなるだけなので、この程度の機能があれば十分だろうか。実際に作り始めると、もう少しページは増えてしまうだろう。

また、上記URL群は情報表示のそれぞれの切り口での起点ページと位置づけ、実際にはそこに表示された要素からユーザーアクションなどに応じて、(Ajaxなどを使って)必要な情報が後付で追記されていく構成を考えている。

ただ、最初からその辺まで含めて設計を行うのは難しいので、まずはプロトタイプ的に上記URL構成のシンプルなWebアプリケーションを開発し、それを実際に使いながらユーザーインターフェースを洗練させていくというアプローチを取っていくつもりだ。

DB設計ベースでのサービス設計

私の場合、DBのテーブル設計を考えながらアプリケーションの機能をまとめていくので、まずはどういうサービスにしたいのかを考えながら、ざっくりとしたテーブル設計を書いてみた。

利用者はパーソナリティ(personalities)テーブルで管理される。NetJockeyというのは、twitterライクなマイクロブログの語り手をラジオのDJ(パーソナリティ)、その言葉の連なりをDJのトークに見立てて名付けたネーミングだ。だからパーソナリティ(personalities)がトーク(talks)するという形でのテーブル設計となっている。

トークに含まれるURL(talks_uris)、ASIN(talks_asins)、タグ(talks_tags)、位置情報(talks_locations)などを抽出することで、同じ話題に関するトークをまとめることができる。またURL(uris)に関しては、URLのFQDN(uris.hostname)に別途インデックスを用意することによって、URL完全一致だけでなく、「あるドメイン上のURLに関する話題」のような範囲を広げた形で話題を追うことも可能にする。位置情報に関してもGIS関連機能を使って「○○近くの話題」などを追いやすくする。

NetJockey内部でのやりとりに関しては、twitterの「@〜」に相当するパーソナリティ対パーソナリティ(talks_personalities)のほかに、発言同士の関連性(talks_talks)も管理する。特定の発言に対する反応を明示する仕組みだ。表記法としては2chライクに「>>発言番号」などで表してもいいかもしれない。

話題をフォローする方法については、発言者(following_personalities)のほかに、URL(following_uris)、ASIN(following_asins)、タグ(following_tags)、トーク(following_talks)、位置(following_locations)などさまざまな方法を用意している。ただ、これをユーザーが手動でいちいち登録するというのでは使い勝手が良くないように思うので、基本的には自分のトーク中に含まれるURL、ASIN、タグ、位置情報、発言者、トークなどを自動的にフォロー対象に追加していく(古いものは自動消去?)といった形にすることを考えている。これならば自然な形でこれらの機能を利用することができるだろう。もちろん明示的に特定のフォロー対象を追加・削除することも可能にする。

フォローの反対に、見たくない情報を見なくて済むようにする方法としては、人間単位でのブロック(blocked_personalities)を用意している。個人レベルでのブロック機能については、私はあまり重きを置いていないのだが、スパム対策としてはこの手の機能は必要だろう。また、システム全体としてのブロック(public_blocked)も用意しておく。こちらに登録されたパーソナリティは、いわゆるスパム系パーソナリティとして通常の表示対象から除去することができるようにする。その逆に、システムからの通知などを強制的に表示するためのシステム全体としてのフォロー(public_following)と言うものも考えているが、実際に利用するかどうか微妙なところだ。

認証(authentications)に関しては、1470.netと同様に外部認証系APIを主に利用することを考えている。はてな認証APItypekeyOpenID、携帯端末IDあたりが主な認証手段となる。また、APIなどで利用するためのサービスローカルなパスワードも一応保持しておくことを考えている。

本当はもう一つ、ディレクトリ(directories)という話題をまとめる単位を用意することも考えていた。ディレクトリとは、2chにおける板的なもの。あるいは、NetNewsにおけるニュースグループ的なもの。つまり、システム側である程度話題の分類指針を示しておき、利用者がそこから自分の好きなディレクトリを選んで発言することによって、半強制的に話題をある程度分類してしまう、というアプローチだ。このディレクトリ自体をWikiとしてユーザーが自分たちで編集できるようにしておくことで、世の中の話題のカテゴライズがリアルタイムで視覚化できるのではないか、などとも妄想した。

これはこれで非常に面白そうにも思えるのだが、それがなくてもすでに話題の分類のための手段は十分に用意されているし、ディレクトリ+Wikiという仕組みはそれ自体がある程度以上に複雑すぎて、まともに立ち上げるのはすごく困難そうに思えた。そこでこの機能は割愛することに決めた。ただし、メインのサービスが十分うまく立ち上がった後に余力があれば、このアイディアは復活させるのも面白いように思っている。

いろいろとオープンにやってみようかと思っている

でまあ、単に新しいサービスを作るだけだったら、勝手に作って公開すればいいだけなんだけど、今回はちょっといつもと違った感じで作ろうかと考えている。というのは、ここに書いているような開発過程の情報だけでなく、サービスのソースコード自体もオープンに作ってしまおうかと思っていたりするのだ。ライセンスはまだ決めていないんだけど、基本フリーソフトウェア系のライセンスを適用するつもり。

実運用サービスのソースコードを公開してしまうってのはセキュリティ的にどうなんだとか、ソースコードを公開するとなるとその手間がかかってしまって開発が進まないんじゃないかとか、いろいろネガティブ要素はあるんだけど、それでも基本的にソースコード公開でやってしまいたいと思っている最大の理由は、個人でサービス運用するのがいい加減きつくなっているから。

趣味でやっているサービスはそれに対するモチベーションがあるうちはいいんだけど、モチベーションを失った場合に放り投げにくい。特にそれなりの規模になってくると、運用コストがお金的にも労力的にもかかってしまうんだけど、そういうものをモチベーションを失った状態で維持するのは、とってもきびしい。ソースコードを誰でも利用可能な形で公開しておけば、私がモチベーションを失った場合でも、誰かやる気とスキルがある人間がいれば、比較的手軽にクローンサービスを継続することが可能になってくるんで、だいぶ気が楽になってくる。

あと、今回はZend Frameworkのお勉強がてらサービスを作っていこうかと思っているんだけど、こういうそれなりの規模のサービス的なものをZend Frameworkで作ったソースコードが公開されているってのは、結構意義があるんじゃないかと思う。特にPHPに限らず、Webアプリケーションフレームワークを使ったソースコードって、小さなサンプルくらいしか見かけたことがない気がするんで、それなりの規模のサンプルアプリがあってもいいんじゃないだろうか。

ちなみに、ソースコードを公開して作ると言っても、基本的には自分一人で作ってしまうつもり。意見とか参考となるコードとかをいただけたら有用なものは取り込ませてもらうだろうけれども、アプリの開発自体にみんなの力を借りようと思って、ソースコードを公開するわけではない。特に、あまり多くの人の意見を聞きすぎると開発速度が遅くなってしまいそうなので、開発自体は基本ワンマンでやっていこうと思っている。

まあこうやって言うだけ言って、どのくらいちゃんと進めていくことができるかわからないけれども、ひとまずこんなことを考えてこのblogを立ち上げてみたわけですよ。

はじめに

ここは“NetJockey”(ネットジョッキー)の開発に関するさまざまな情報を記録するためのblogである。

“NetJockey”とは、現在構想&設計中のオンラインサービスの名前である。現在が絶賛放置運営中である1470.netの後継サービス的な内容となる。私の作るサービスは基本的に「自分がほしいサービスは自分で作りつつ、他人にも使えるようにしておこう」なので、要は自分が使いたいサービスを思いついたので、作ってみようと考えているわけだ。

どんなサービスなのかというと、ぶっちゃけSBMtwitterである。twitterのように、シンプルなテキストエリアにどんどんテキストを放り込んでいく。そこにURLが含まれているのならばそれはURLブックマークと同じである。同様にASINや位置情報などが含まれていればそれらも認識する。一つのテキストに複数のURLが含まれていてもかまわないし、その記述方法はURLとして機械的に抽出できるものならば、文頭にあろうが文中にあろうがかまわない。URLが含まれないテキストも許容する。

結局やりたいことは、

  • 入力のシンプル化 - 1470.netでやっていた自由度の高い(複数のURL、ASIN、位置情報などに対応した)ブックマークサービスを、フリーワード入力欄一つでサポートできるようにする
  • ミニブログ対応 - URLやASINなどのブックマーク対象がないテキストも許容する
  • リアルタイムで流れていくテキスト - 興味を持った人、URL、タグなどに関するテキストを抽出し、twitterのようにリアルタイムで読み流していくスタイルの閲覧に対応する

といった内容である。