CodeIgniter 3.1.10 と Smarty 3.1を連携する開発環境を構築する


# はじめに

PHP5系でCodeIgniter 2.0.xで開発されたシステムをPHP7系に移植する作業を依頼されました。
腕のいい開発者さんが作ったのでしょう。シンプルな中にも質実剛健のような作りで、見ていて気持ちがいいです。

ですが、この世に産み落とされてから早ｘ年。
時代は、CodeIgniterからLaravelに移り、開発者さん自身もメンテナンスを放棄されてしまったようで、私のところへの依頼となりました。

さて、CodeIgniterの2.0.xから2.2までは問題なくアップデートができたのですが、3系へのアップグレード時に、使われていたSmartyが使えなくなりました。
そこで、CodeIgniter3系でSmartyのインストール方法を探しましたが、ロストテクノロジーなので、メチャメチャ苦労しました。
そのため、ここで導入方法を記録しておきます。


驚いたことに、Smartyの導入さえできれば、開発されたコードは、そのまま使えましたので、まだまだ現役稼働ができる見込みが立ちました。
システムのリフォーム？　リストア？　蘇ると嬉しいですね。


今回のサンプルコードをGitHubにあげています。もし、参考になったのでしたら、スターをつけていただくとモチベーションがあがります
https://github.com/idani/CodeIgniter3.1-Smarty3.1-Smaple


# 環境

* php 7.3(5.4)
    * 移行絡みでしたので、5系、7系の両方を用意して対応してます。
* [CodeIgniter 3.1.10](https://www.codeigniter.com/)
* [Smarty 3.1.33](https://www.smarty.net/)


# CodeIgniterの特徴

さて、私、CodeIgniterって今回、所見でした。なので、自分用のメモとして触った感じをメモ。

applicationとsystemディレクトリに分かれています。
systemの機能を拡張したい場合でも、applicaitonディレクトリの方で、継承、上書きなどを行って独自にカスタマイズできることが可能です。

また、この構成のおかげで、Systemのバージョンアップをする場合にも、systemディレクトリを最新バージョンで上書きするだけで動くという、すっごい楽なメンテナンス性がよいですね。

# 開発環境構築

まずは、CodeIgniterを動作させるためのPHPの実行・開発環境を用意します。
https://github.com/idani/CodeIgniter3.1-Smarty3.1-Smaple

ディレクトリ構成は、このようになっています。
基本的にSRCの部分を差し替えて、いろいろのPHPの開発で使いまわしています。

![2019-08-04_20h51_32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133291/4f362c71-378a-7d67-b7a5-3bb2a1f3c19d.png)

このあたり、Dockerの部分がわからない場合は、以下のサンプルコードと記事を見てもらえれば理解できます。

https://github.com/idani/nginx-http2-php-mysql


今回は、SRCに、「codeignter-3.1.10」を展開からスタートしました。


# smartyのインストール

docker-compose.ymlの最後の部分でComposerイメージを使ってインストールします。


まずは、以下のようにdocker-compose.ymlを書き換えます。

```docker:docker-compose.yaml
  composer:
    image: composer
    volumes: 
      - ./src/application:/app
    command: composer require smarty/smarty
```

その上で、docker-composeを実行して、インストールを行います。
Smartyのダウンロードを行いますので、終了するまでネットワーク環境によりますが、２，３分かかります。

```bash
$ docker-compose up composer
Creating network "codeigniter31-smarty31-smaple_default" with the default driver
Creating codeigniter31-smarty31-smaple_composer_1 ... done
Attaching to codeigniter31-smarty31-smaple_composer_1
Gracefully stopping... (press Ctrl+C again to force)
Stopping codeigniter31-smarty31-smaple_composer_1 ... done
```
# CodeIgniterでのComposerの読み込み設定

「application/config/config.php」で「composer_autoload」をTrusにする。

![2019-08-04_19h37_45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133291/c283266d-f24f-8ffd-dc90-bca465cc8652.png)

config.phpに書いてありますが、
「application/vendor/autoload.php」を読み込む場合は、先のように「$config['composer_autoload'] = TRUE;」にするだけでOKです。

CodeIgniterのインストールしたディレクトリを変更する場合は、「$config['composer_autoload'] = '/path/to/vendor/autoload.php';」と設定します。


# ディレクトリの作成

Smarty用のテンプレートディレクトリとテンプレートキャッシュ用のディレクトリを作成します。

application/views/templates
application/views/templates_c
の2つのディレクトリをchmod 777で作成しておきます。

# Smarty用のライブラリ作成

参考文献記載のライブラリを作成して、CodeIgniterとSmartyの橋渡しをします。

ここで、CodeIgniter側の制限ですが、ファイル名とクラス名は合わせる必要があり、また、すでにSmartyクラスはあるので、「Smartie」としています。

ここは気持ち悪いのですが、後ほど、ライブラリの読み込み時に「Smarty」に補正をかけます。


```php:application/library/Smartie.php
<?php  if (!defined('BASEPATH')) exit('No direct script access allowed');

/**
 * Smartie Class
 *
 * @package        CodeIgniter
 * @subpackage     Libraries
 * @category       Smarty
 * @author         Kepler Gelotte
 * @link           http://www.coolphptools.com/codeigniter-smarty
 */

class Smartie extends Smarty {

    var $debug = false;

    function __construct()
    {
        parent::__construct();

        $this->setTemplateDir(APPPATH . "views/templates");
        $this->setCompileDir(APPPATH . "views/templates_c");
        if ( ! is_writable( $this->compile_dir ) )
        {
            // make sure the compile directory can be written to
            @chmod( $this->compile_dir, 0777 );
        } 

        // Uncomment these 2 lines to change Smarty's delimiters
        // $this->left_delimiter = '{{';
        // $this->right_delimiter = '}}';

        $this->assign( 'FCPATH', FCPATH );     // path to website
        $this->assign( 'APPPATH', APPPATH );   // path to application directory
        $this->assign( 'BASEPATH', BASEPATH ); // path to system directory

        log_message('debug', "Smarty Class Initialized");
    }

    function setDebug( $debug=true )
    {
        $this->debug = $debug;
    }

    /**
     *  Parse a template using the Smarty engine
     *
     * This is a convenience method that combines assign() and
     * display() into one step. 
     *
     * Values to assign are passed in an associative array of
     * name => value pairs.
     *
     * If the output is to be returned as a string to the caller
     * instead of being output, pass true as the third parameter.
     *
     * @access    public
     * @param    string
     * @param    array
     * @param    bool
     * @return    string
     */
    function view($template, $data = array(), $return = FALSE)
    {
        if ( ! $this->debug )
        {
            $this->error_reporting = false;
        }
        $this->error_unassigned = false;

        foreach ($data as $key => $val)
        {
            $this->assign($key, $val);
        }
        
        if ($return == FALSE)
        {
            $CI =& get_instance();
            if (method_exists( $CI->output, 'set_output' ))
            {
                $CI->output->set_output( $this->fetch($template) );
            }
            else
            {
                $CI->output->final_output = $this->fetch($template);
            }
            return;
        }
        else
        {
            return $this->fetch($template);
        }
    }
}
// END Smartie Class
```

# ライブラリを読み込む

「application/config/autoload.php」の「libraries」の項目を以下のように修正します。

先程の「smartie」という部分を「smarty」に読み替えてロードしています。

```php:application/config/autoload.php
/*
| -------------------------------------------------------------------
|  Auto-load Libraries
| -------------------------------------------------------------------
| These are the classes located in system/libraries/ or your
| application/libraries/ directory, with the addition of the
| 'database' library, which is somewhat of a special case.
|
| Prototype:
|
|	$autoload['libraries'] = array('database', 'email', 'session');
|
| You can also supply an alternative library name to be assigned
| in the controller:
|
|	$autoload['libraries'] = array('user_agent' => 'ua');
*/
$autoload['libraries'] = array('smartie'=>'smarty');
```

# 動作確認

デバッグメッセージを出力させるために、Configをいじります。
今回は「４：All Messages」を設定します。

```php:application/config/config.php
/*
|--------------------------------------------------------------------------
| Error Logging Threshold
|--------------------------------------------------------------------------
|
| You can enable error logging by setting a threshold over zero. The
| threshold determines what gets logged. Threshold options are:
|
|	0 = Disables logging, Error logging TURNED OFF
|	1 = Error Messages (including PHP errors)
|	2 = Debug Messages
|	3 = Informational Messages
|	4 = All Messages
|
| You can also pass an array with threshold levels to show individual error types
|
| 	array(2) = Debug Messages, without Error Messages
|
| For a live site you'll usually only enable Errors (1) to be logged otherwise
| your log files will fill up very fast.
|
*/
$config['log_threshold'] = 4;

```

ブラウザで「 http://127.0.0.1:8000/ 」を表示させます。
正常に動作したなら、以下のデフォルトのページが表示されます。

![2019-08-04_19h02_16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133291/6039b682-441a-a224-66e9-b626caf5e962.png)

そして、ログにもSmartyライブラリの初期化完了のログ「Smarty Class Initialized」が出力されます。

```log:application/logs/log-<日付>.php
・・・
INFO - 2019-08-04 20:07:34 --> Loader Class Initialized
DEBUG - 2019-08-04 20:07:34 --> Smarty Class Initialized
INFO - 2019-08-04 20:07:34 --> Controller Class Initialized
INFO - 2019-08-04 20:07:34 --> File loaded: /src/application/views/welcome_message.php
INFO - 2019-08-04 20:07:34 --> Final output sent to browser
DEBUG - 2019-08-04 20:07:34 --> Total execution time: 0.0578
```

# サンプルを作成する

CodeIgniterが初見なので、サンプルを書いてみます。

```application/controllers/HelloWorld.php
<?php
defined('BASEPATH') OR exit('No direct script access allowed');

class HelloWorld extends CI_Controller {

	public function index() {
        $data = [
            'title' => 'Hello World!',
        ];
		$this->smarty->view('HelloWorld.tpl',$data);
	}
}

```

```application/views/template/HelloWorld.tpl
{$title}

```

ブラウザで、「 http://127.0.0.1:8000/HelloWorld 」にアクセスをすると、以下のように表示されます。

「HelloWorld.php」の$data['title']を変更して、動的に表示が変更されることを確認してみてください。

![2019-08-04_20h22_00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133291/d54a398c-b0d5-1378-56f0-3bac2e98ed21.png)


以上の構築した環境を、以下のGitHubで公開をしています。スターを付けていただけると、誰かの役に立ったんだとわかって、モチベーションがあがります。よろしくお願い致します。

https://github.com/idani/CodeIgniter3.1-Smarty3.1-Smaple


# 参考文献

* [CodeIgniter + Smarty Perfect Together](http://www.coolphptools.com/codeigniter-smarty)
* [Codeigniter3にSmartyをComposerで設定](https://owani.net/php/codeigniter3-smarty3/474/amp/)
* [CodeIgniter 3 とSmarty連携させる
PHP](https://qiita.com/fgnhssb/items/a7bce303874b324a9fea)
* [[ CodeIgniter ] CodeIgniter3+Smarty](https://memo.abridge-lab.com/?p=73)