Symfony 2.8(lts) と SmartyBundle(~2.0) で Smarty のプリフィルタを利用するサンプル


[Symfony](https://symfony.com) 2.8 で [SmartyBundle](https://github.com/noiselabs/SmartyBundle) を利用している場合の Smarty プリフィルタの実装サンプルです。

ドキュメントを参考にしたところ、思いのほかハマって、結局 SmartyBundle のソースコードを部分的に読む事になってしまったので、メモを残しておきます。

## 参考: SmartyBundle のドキュメント
[6.8. Enabling custom Extensions]("http://smartybundle.readthedocs.io/en/latest/extensions.html#enabling-custom-extensions")


## プリフィルタを利用するサンプル実装

SmartyBundle でプリフィルタを利用する際は、Extension を用意します。
SmartyBundle の Extension は、ExtensionInterface を実装したクラスで、AbstractExtension は ExtensionInterface のデフォルト実装を用意してくれているので、AbstractExtension を継承すれば必要なメソッド(今回は `getFilters` )のみオーバーライドすればOKです。

サンプル実装は SSI のタグを include タグに置換するプリフィルタを登録するものです。

```php:src/Ext/SmartyExtensionBundle/Extension/TemplateFiltersExtension.php
<?php

namespace Ext\SmartyExtensionBundle\Extension;

use Psr\Log\LoggerInterface as Logger;
use Symfony\Component\DependencyInjection\ContainerInterface as Container;
use NoiseLabs\Bundle\SmartyBundle\Extension\AbstractExtension;
use NoiseLabs\Bundle\SmartyBundle\Extension\Filter\PreFilter;

class TemplateFiltersExtension extends AbstractExtension
{
    // not required.
    public function __construct(Container $container, Logger $logger)
    {
        $this->container = $container;
        $this->logger = $logger;
    }

    // required (the official doc. said.)
    public function getName()
    {
        // the extension name.
        return 'ext.ssi2include';
    }

    public function getFilters()
    {
        return array(
            // filter name, extension obj, filter function(method) name
            new PreFilter('prefilter_ssi2include', $this, 'ssi2include'),
        );
    }

    // filter function(method).
    // example: replace ssi-tags to smarty include tags.
    public function ssi2include($source) {

        $left_delimiter = $this->container->getParameter('smarty.opt.delimiter.left');
        $right_delimiter = $this->container->getParameter('smarty.opt.delimiter.right');
        $source = preg_replace('/<!--#include\ (virtual|file)="\/?(.+)?"-->/i',
            $left_delimiter . 'include file=\'' . $_SERVER['DOCUMENT_ROOT'] . '/' . "$2" . '\'' . $right_delimiter,
            $source
        );

        return $source;
    }
}
```

コンストラクタは必須ではないですが、`getName` メソッドは必須です。

Smartyのフィルタを利用する場合は、`getFilters` の戻り値として、`PreFilter`(`FilterInterface` の実装クラス)の配列を返すことで、`$smarty->register_prefilter()` に対象のメソッドが渡されます。

公式のドキュメントでは、第一引数が `$this`、第二引数がフィルタ本体のメソッド名となっていますが、インストールされたバージョンでは第一引数はフィルタ名を指定するようになっています。

何かの設定で変更できるとは思いますが、デフォルトではフィルタの実装の変更を反映させる際は毎回キャッシュをクリアしてやる必要がありました。


## サービスコンテナに登録

サービスコンテナに登録する際、`smarty.extension` というタグを付けるだけで SmartyBundle の Extension として動作します。

```yaml:src/Ext/SmartyExtensionBundle/Resources/config/services.yml
services:
    smarty.extension.template_filters:
        class: Ext\SmartyExtensionBundle\Extension\TemplateFiltersExtension
        arguments: [ "@service_container", "@logger" ]
        tags:
            - { name: smarty.extension }
```

arguments のサービスコンテナはパラメータを取得するために渡しています。



## おまけ: サンプルで参照している設定

サンプルで利用しているだけで、本筋とは関係ありませんが、参照しているパラメータ等は下記のように設定しています。


```yaml:app/config/config.yml

parameters:
    smarty.opt.delimiter.left:  "{"
    smarty.opt.delimiter.right: "}"

smarty:
    options:
        left_delimiter:  "%smarty.opt.delimiter.left%"
        right_delimiter: "%smarty.opt.delimiter.right%"
```