smarty2.xをPHP7系で動作するよう救済する措置

受注した案件でPHP5.1を現役に利用しているシステムにsmartyが使われていました。
こいつをPHP7.2で動かそうと思ったら画面が真っ白になったり、エラー表示もエラーログも吐き出されず頭を捻りたしたので、次回同じことがあっても、時間をかけないために記録しておきます。
※リプレース時に、詰まったこと、これはしておかないと行けないことがあれば追記していきます。

# Templateがコンパイルされず、キャッシュファイルの中身がない
smartyは、テンプレートをもとに__/templates_c/__ にphpのキャシュファイルを生成します。
ところが今回は、下記のように生成されたキャッシュファイルの中身が空っぽな状態が発生しました。
エラーやらなんやら見て回って、「まさかキャッシュファイルが不完全ってこと…。」

「あああああああ！！templates_cが中途半端にしか作成されない！！！！！」

```php:templates_c/%%3F^3FB^3FB22E73%%index.tpl.php
<?php /* Smarty version 2.6.x, created on 2020-12-09 22:22:22
         compiled from index.tpl*/ ?>
    <?php // からっぽ ?>
```

なんてこった…。
こりゃ画面も真っ白になるよ。


とりあえず試したこと

- templates_c/配下のキャッシュファイルを削除して再読み込み　→　　キャッシュファイルは生成される
- templates_c/を削除してみて再読み込み　→　templates_c/がないとエラーハンドリングされる
- TemplateHtmlclassをインスタンスして出力してみる　→　ちゃんとインスタンスは生成されている

おそらくテンプレートをコンパイルするときになんか悪さしてるんだろうなぁと当たりをつけて調べてみる
 
参考になったサイト様：[Mac Smartyで画面が真っ白になる - 今日もちょいつか](https://heinlein.hatenablog.com/entry/2018/06/16/170944)
上記サイトに解決策を記載されていたため、試してみる。

```php:Smarty_Compiler.class.php//変更前
    /**
     * compile a resource
     *
     * sets $compiled_content to the compiled source
     * @param string $resource_name
     * @param string $source_content
     * @param string $compiled_content
     * @return true
     */
    function _compile_file($resource_name, $source_content, &$compiled_content)
    {
          /* 中略 */
                      /* replace special blocks by "{php}" */
            $source_content = preg_replace($search.'e', "'"
                                           . $this->_quote_replace($this->left_delimiter) . 'php'
                                           . "' . str_repeat(\"\n\", substr_count('\\0', \"\n\")) .'"
                                           . $this->_quote_replace($this->right_delimiter)
                                           . "'"
                                           , $source_content);
    }
```

こいつを以下のように変更

```php:Smarty_Compiler.class.php//変更後
    /**
     * compile a resource
     *
     * sets $compiled_content to the compiled source
     * @param string $resource_name
     * @param string $source_content
     * @param string $compiled_content
     * @return true
     */
    function _compile_file($resource_name, $source_content, &$compiled_content)
    {
          /* 中略 */
            /* replace special blocks by "{php}" */
            // create_functionはPHP7.2 以降非推奨
            $source_content = preg_replace_callback($search, create_function ('$matches', "return '"
                                            . $this->_quote_replace($this->left_delimiter) . 'php'
                                            . "' . str_repeat(\"\n\", substr_count('\$matches[1]', \"\n\")) .'"
                                            . $this->_quote_replace($this->right_delimiter)
                                            . "';")
                                            , $source_content);
    }
```

こいつで試してみたところキャッシュファイルがちゃんと生成され、中身のあるファイルになりました…。
私の4時間は無駄にならなかった。

ちなみに、この案件ではテスト・本番（PHP5.1）と開発環境（PHP7.2）でPHPのバージョンが違うため、もし環境に差異があるならバージョンを意識した書き方をしておくことをお奨めします。（本当は本番と同じ環境を用意するのが一番良いが時間がないときもある）

今回の事例だと

```php:Smarty_Compiler.class.php
// PHPバージョン対策
// PHP_VERSION_IDはPHP5.2.7以降でないと定義されていない
if (!defined('PHP_VERSION_ID')) {
    $version = explode('.', PHP_VERSION);

    define('PHP_VERSION_ID', ($version[0] * 10000 + $version[1] * 100 + $version[2]));
}

...
        /* 中略 */
        // PHP5.2.7以前とそれ以降で分岐を追加しておく
        if(PHP_VERSION_ID < 50207) {
            /* replace special blocks by "{php}" */
            $source_content = preg_replace($search.'e', "'"
                                           . $this->_quote_replace($this->left_delimiter) . 'php'
                                           . "' . str_repeat(\"\n\", substr_count('\\0', \"\n\")) .'"
                                           . $this->_quote_replace($this->right_delimiter)
                                           . "'"
                                           , $source_content);
        } else {
            // PHP7.2 以降はcreate_functionが非推奨になる
            $source_content = preg_replace_callback($search, create_function ('$matches', "return '"
                                            . $this->_quote_replace($this->left_delimiter) . 'php'
                                            . "' . str_repeat(\"\n\", substr_count('\$matches[1]', \"\n\")) .'"
                                            . $this->_quote_replace($this->right_delimiter)
                                            . "';")
                                            , $source_content);
        }


```

とすると環境の差異があっても無事に動くので、余計な気を回さなくて済みそう。
時間があるときに __create_function ()__ を使わない手法で書き直しておくことにする