Zend FrameworkでのViewクラスの差替・拡張方法まとめ

Zend FrameworkはデフォルトではZend_Viewというテンプレートエンジンを使用します。(正確に言うと素のPHPに足りない便利関数集といった感じ。)

SmartyやTwigといったテンプレートエンジンを連携させる場合など、Zend_View以外を使いたい場合のやり方をまとめてみます。

# 拡張方法の選択

Zend Frameworkは拡張方法がたくさん用意されているフレームワークです。 **まずはどう拡張するのかを検討** します。

## Viewヘルパーをまず検討
単純な機能拡張であれば、Viewヘルパーを作れないか検討しましょう。要は「関数が足りないので増やしたい」、という場合です。
http://framework.zend.com/manual/ja/zend.view.helpers.html

Viewヘルパーはこのまとめでは扱いません。標準で付属するViewヘルパーが充分高機能なので、標準のViewヘルパーだけで何とかなることが多いでしょう。ヘルパーを作る前に「既に標準でできる内容でないか？」調べたほうがいいです。

ちなみにViewヘルパーを推奨するのは、「継承しなくて済むから」です。Viewクラスの書き換えより簡単に作れますし、拡張性も犠牲になりません。


## Viewクラスを直接拡張する
以降は「 **Zend\_Viewを使いたくない** 」「 **Zend\_Viewの挙動を差し替えたい** 」というような場合のやり方です。これらのケースではViewクラスを直接拡張しなくてはなりません。

Zend Frameworkで使うためのViewクラスの条件は、「 **Zend\_View\_Interfaceをimplementsしていること** 」だけです。ただし、インターフェースには実装コードは全く書かれていないため、Interfaceから作ると面倒かもしれません。
Zend Frameworkには Zend\_View\_Abstractという、すでにZend\_View\_Interfaceをimplementsしているクラスがあり、これは必須メソッドが揃っています。単純なメソッド追加であればこれを使うのも手です。

1. Zend\_View\_Interface をimplementsして、必須メソッドをすべて書く
2. Zend\_View\_Abstract をextendsして、最低限だけ上書きする
3. Zend\_View をextendsして、最低限だけ上書きする

上に行くほど書くコードは多くなりますが、下に行くほどZend\_Viewの複雑な実装を引き継ぐことになります。
ちなみにZend\_View\_AbstractとZend\_Viewの差はごくわずかです。具体的にはソースコードを読みましょう。

「Zend\_Viewは全く使いたくない！ Twigさえあれば十分だ」ということなら、Zend\_View\_Interfaceから書いていくのがいいでしょう。

「Zend\_Viewの微妙な点が気に食わない！」なら、Zend\_Viewを継承して気に食わない部分だけ上書きすればいいです。

ここではZend\_Viewをextendsして、escape()メソッドを上書きしてみます。これはhtmlspecialchars()のラッパー関数なのですが、標準のescape()は拡張性の反面、非効率な書き方をしているので、拡張性を捨ててもっとシンプルな実装に差し替えます。

```php:library/My/View.php
<?php
class My_View extends Zend_View
{
  public function escape($str)
  {
    return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
  }
}
```

簡単なユニットテストはこんなイメージ。Zend\_View\_Interfaceのインスタンスだと判定されればOK.

```php:tests/unit/My_ViewTest.php
<?php
require_once 'Zend/Loader/Autoloader.php';
Zend_Loader_Autoloader::getInstance()
  ->registerNamespace('My');

$view = new My_View;
assert($view instanceof Zend_View_Interface);
```

# Zend Frameworkへの組み込み

さて、先程も言ったとおりZend Frameworkは非常に自由度が高いフレームワークで、様々な構成で作ることができます。
ここでは標準的な構成の場合に使えるものを2つ書きますが、構成が違う場合は使えないケースもあるのでご注意ください。

…標準のViewリソースプラグインがクラスの差し替えに対応していれば、こんなややこしいことをしなくてもいいのですが。。。「new Zend\_View()」ってハードコーディングされてるんだよね。。


あ、以降のソースコードではオートローダーのコードは省略します。index.phpの先頭あたりにこんなコードが書いてあるものとします。

```php:public/index.php
<?php
require_once 'Zend/Loader/Autoloader.php';
Zend_Loader_Autoloader::getInstance()->registerNamespace('My');
// ...（略
```



## 方法1・Bootstrap.phpで初期化する

通常はZend\_Application\_Resource\_Viewが勝手に起動して、勝手にZend\_Viewをインスタンス化します。
しかしBootstrap.phpで\_initView()というメソッドを作っておけば、リソースプラグインより優先されます。これを使って手動でViewをインスタンス化することができます。

```php:application/Bootstrap.php
<?php
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
  protected function _initView()
  {
    $view = new My_View;
    $viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
    $viewRenderer->setView($view);
    return $view; 
  }
}
```

## 方法2・Resourceプラグインを自作する

個人的にはこちらの方法がおすすめ。Bootstrapにベタ書きする手法だと、アプリを作るごとにコードをコピペすることになるので、再利用性が低くなります。

まず、My\_Application\_Resource\_Viewクラスを作成します。これがいわゆるリソースプラグインです。名前が長いのは諦めましょう。

```php:library/My/Application/Resource/View.php
<?php
class My_Application_Resource_View extends Zend_Application_Resource_View
{
    public function getView()
    {
        if (null === $this->_view) {
            $options = $this->getOptions();

            if (isset($options['classname'])) {
                $class = $options['classname'];
            } else {
                $class = 'My_View';
            }   
            $this->_view = new $class;
        }
        return $this->_view;
    }
}
```

このファイルを配置したら、あとはapplication.iniでpluginpathsに追加するだけ。

```ini:application/configs/application.ini
[production]
;..中略..
pluginpaths.My_Application_Resource = "My/Application/Resource"
;..中略..
```

これで、Zend\_Viewの代わりにMy\_Viewが使用されるようになります。。長かったｗ