読者です 読者をやめる 読者になる 読者になる

HHeLiBeXの日記 正道編

日々の記憶の記録とメモ‥

CodeIgniterを試してみる

PHP

CodeIgniterというPHPフレームワークがあるらしい、ということで、ざっと試してみるテスト。

前提

今回は、以下の環境での構築とする。

  • Ubuntu Server 10.04 LTS
    • インストール時に LAMP Server を追加インストール
  • Apache httpd 2.2.14
  • PHP 5.3.2

導入

まずは、以下のサイトからダウンロード。

今回は、ci-ja-all-in-one-2.0.3-1.zip をダウンロード。
ダウンロードしたら展開し、中身を丸ごと公開ディレクトリに‥‥って、ちょっと待て。いろんなソースを公開ディレクトリに置くなんて‥と思ってみてみたら、以下のような内容の .htaccess ファイルが system ディレクトリと application ディレクトリに置いてあるらしい。

Deny from all

ただ、ディレクトリ全体に対してのアクセスの禁止だし、そのポリシーが変わるとは思えないので、公開ディレクトリへの不必要なファイルの配置はしない方向で調整。
まず、以下のようなディレクトリ構成を作る。

/var/www/ci-test/
 + public
 |  - index.php
 |  + user_guide
 |  + user_guide_ja
 + private
 |  + system
 |  + application

で、Apacheの仮想ホストの設定を以下のように行う。(もちろん、ホスト名"app.example.com"が名前解決可能であるという前提。必要に応じて、IPアドレスに書き換えるなど。)

<VirtualHost *:80>
    ServerAdmin admin@example.com
    DocumentRoot "/var/www/ci-test/public"
    ServerName app.example.com
    DirectoryIndex index.php index.html

    <Directory "/var/www/ci-test/public">
        Options FollowSymLinks
        AllowOverride None

        RewriteEngine on
        RewriteBase /
        RewriteRule !\.(js|ico|gif|jpg|png|css|html|xml|txt)$ index.php

        Order allow,deny
        Allow from all
    </Directory>
</VirtualHost>

CodeIgniterは、index.phpをリクエストハンドラとして、URLに応じたコントローラ、ビューが呼び出される仕組みなので、index.phpが(特定拡張子以外のURIへの)リクエストを常に処理するようにするためにURL書き換えを使用する。
さらに、初期状態ではrewrite_moduleがロードされないので、ロードするように以下の作業を行う。

$ cd /etc/apache2/mods-enabled
$ sudo ln -s ../mods-available/rewrite.load rewrite.load

このrewrite.loadの内容は以下のような感じ。

LoadModule rewrite_module /usr/lib/apache2/modules/mod_rewrite.so


さて、system ディレクトリと application ディレクトリの配置を調整したので、そのパスを教えてやる必要があるのだが、それはindex.php内にある以下の行を編集することで対応する。(以下は編集後の内容)

//(前略)
/*
 *---------------------------------------------------------------
 * SYSTEM FOLDER NAME
 *---------------------------------------------------------------
 *
 * This variable must contain the name of your "system" folder.
 * Include the path if the folder is not in the same  directory
 * as this file.
 *
 */
    $system_path = '../private/system';

/*
 *---------------------------------------------------------------
 * APPLICATION FOLDER NAME
 *---------------------------------------------------------------
 *
 * If you want this front controller to use a different "application"
 * folder then the default one you can set its name here. The folder
 * can also be renamed or relocated anywhere on your server.  If
 * you do, use a full server path. For more info please see the user guide:
 * http://codeigniter.com/user_guide/general/managing_apps.html
 *
 * NO TRAILING SLASH!
 *
 */
    $application_folder = '../private/application';
//(以下略)


ここまでの作業が終わった時点で、ブラウザから以下のURLにアクセスすると、「CodeIgniter へようこそ!」ページが表示されるはず。(もちろん、ホスト名"app.example.com"が名前解決可能であるという前提)

  • http://app.example.com/

で、ようこそページに、参照すべきファイルのパスが書いてあったので、どうなっているのかな、と「application/views/welcome_message.php」を覗いてみたら、ファイルのパスはハードコーディングでした まる(謎)

コントローラとアクション

上記では、何も考えずにようこそページを表示させたが、これはデフォルトのコントローラが呼ばれた結果。

  • application/config/routes.php
$route['default_controller'] = "welcome";

これは、コントローラが指定されなければ「application/controllers/welcome.php」を呼び出すということを指定している。(拡張子".php"を除いたファイル名を指定するので、環境にもよるが、大文字小文字に注意)
さらに、アクションを明示しなければ「index」アクションが呼び出される。
では、コントローラやアクションはどのように指定するかというと、以下のように指定する。

  • http://app.example.com/index.php/welcome/index/
  • http://app.example.com/welcome/index/

URL書き換えの処理を組み込んであるので、前者でも後者でも同じように動作する。
アクションを省略すると以下のような感じ。

  • http://app.example.com/index.php/welcome/
  • http://app.example.com/welcome/

さらにコントローラも省略すると以下のような感じ。

  • http://app.example.com/index.php
  • http://app.example.com/

テンプレート

「application/controllers/welcome.php」を見てみると、以下のような感じでテンプレートファイルを読み込んでいる。

    public function index()
    {
        $this->load->view('welcome_message');
    }

これは先に書いた「application/views/welcome_message.php」を読み込むわけだが、拡張子が".php"というのがどうにも気持ち悪いので、どうなっているのかを追ってみた。
「system/core/Controller.php」でインスタンスを生成している「system/core/Loader.php」を見てみると、CI_Loaderクラスのviewメソッド(339行目あたり)で_ci_loadメソッドを呼んでいる。その_ci_loadメソッド(654行目あたり)を見てみると以下のような処理をしているところがある。

        // Set the path to the requested file
        if ($_ci_path != '')
        {
            $_ci_x = explode('/', $_ci_path);
            $_ci_file = end($_ci_x);
        }
        else
        {
            $_ci_ext = pathinfo($_ci_view, PATHINFO_EXTENSION);
            $_ci_file = ($_ci_ext == '') ? $_ci_view.'.php' : $_ci_view;

            foreach ($this->_ci_view_paths as $view_file => $cascade)
            {
                if (file_exists($view_file.$_ci_file))
                {
                    $_ci_path = $view_file.$_ci_file;
                    $file_exists = TRUE;
                    break;
                }
// (以下略)

呼び出し元のview関数では、以下のような呼び出しなので"_ci_path"は指定されない。

    public function view($view, $vars = array(), $return = FALSE)
    {
        return $this->_ci_load(array('_ci_view' => $view, '_ci_vars' => $this->_ci_object_to_array($vars), '_ci_return' => $return));
    }

したがって、拡張子が検出できたら指定したとおりのファイルを、拡張子が検出できなければ".php"を付加したファイルをテンプレートファイルとして読み込む、らしい。


そこで‥以下のようなファイルを作って実験。

  • application/controllers/sample01.php
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

class Sample01 extends CI_Controller {
    public function test01() {
        $this->load->view('sample01/test00');
    }
    public function test02() {
        $this->load->view('sample01/test00.html');
    }
    public function test03() {
        $this->load->view('sample01/test00.php');
    }
}
  • application/views/sample01/test00.php
<div>
This is "sample01/test00.php".
</div>
  • application/views/sample01/test00.html
<div>
This is "sample01/test00.html".
</div>

まぁ、結果はご想像の通り(謎)。

セグメント

CodeIgniterでは、コントローラ名とアクション名をURL内にスラッシュで区切って指定するが、さらにパラメータみたいなものを指定することもできる(セグメントと呼ばれるらしい)。
ものは試し、以下のようなサンプルを作成。

  • application/controllers/sample02.php
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

class Sample02 extends CI_Controller {
    public function index() {
        // セグメント数
        $nSeg = $this->uri->total_segments();
        $data['total_segments'] = $nSeg;
        // URI文字列
        $data['uri_string'] = $this->uri->uri_string();
        // 各セグメントを順に。1-originであることに注意
        $data['segment'] = array();
        for ($i = 0; $i < $nSeg; ++$i) {
            $data['segment'][$i + 1] = $this->uri->segment($i + 1);
        }
        // スラッシュつき。後ろ
        $data['slash_segment'] = array();
        for ($i = 0; $i < $nSeg; ++$i) {
            $data['slash_segment'][$i + 1] = $this->uri->slash_segment($i + 1);
        }
        // スラッシュつき。前
        $data['slash_segment_leading'] = array();
        for ($i = 0; $i < $nSeg; ++$i) {
            $data['slash_segment_leading'][$i + 1] = $this->uri->slash_segment($i + 1, 'leading');
        }
        // スラッシュつき。両方
        $data['slash_segment_both'] = array();
        for ($i = 0; $i < $nSeg; ++$i) {
            $data['slash_segment_both'][$i + 1] = $this->uri->slash_segment($i + 1, 'both');
        }
        // 連想配列に。
        //   デフォルトでは、「3番目=4番目」、「5番目=6番目」‥という感じ
        $data['uri_to_assoc'] = $this->uri->uri_to_assoc();
        // セグメントを一括で配列として取得
        $data['segment_array'] = $this->uri->segment_array();
        $this->load->view('sample02/index.html', $data);
    }
}
  • application/views/sample02/index.html
<div>
This is "sample02/index.html".
</div>
<table border="1">
    <tr>
        <th>total_segments</th>
        <td><?php echo $total_segments;?></td>
    </tr>
    <tr>
        <th>uri_string</th>
        <td><?php echo $uri_string;?></td>
    </tr>
    <tr>
        <th>segment</th>
        <td>
            <?php foreach ($segment as $idx => $seg):?>
            <?php echo $idx . '=>' . $seg;?><br />
            <?php endforeach;?>
        </td>
    </tr>
    <tr>
        <th>slash_segment</th>
        <td>
            <?php foreach ($slash_segment as $idx => $seg):?>
            <?php echo $idx . '=>' . $seg;?><br />
            <?php endforeach;?>
        </td>
    </tr>
    <tr>
        <th>slash_segment_leading</th>
        <td>
            <?php foreach ($slash_segment_leading as $idx => $seg):?>
            <?php echo $idx . '=>' . $seg;?><br />
            <?php endforeach;?>
        </td>
    </tr>
    <tr>
        <th>slash_segment_both</th>
        <td>
            <?php foreach ($slash_segment_both as $idx => $seg):?>
            <?php echo $idx . '=>' . $seg;?><br />
            <?php endforeach;?>
        </td>
    </tr>
    <tr>
        <th>uri_to_assoc</th>
        <td>
            <?php foreach ($uri_to_assoc as $idx => $seg):?>
            <?php echo $idx . '=>' . $seg;?><br />
            <?php endforeach;?>
        </td>
    </tr>
    <tr>
        <th>segment_array</th>
        <td>
            <?php foreach ($segment_array as $idx => $seg):?>
            <?php echo $idx . '=>' . $seg;?><br />
            <?php endforeach;?>
        </td>
    </tr>
</table>

「$this->uri」はURIクラスのインスタンスで、リクエストURIから自動的に生成される。
例えば、以下のようなURLにアクセスしてみると‥

  • http://app.example.com/sample02/index/aaa/bbb/ccc/ddd/eee/fff/ggg/

セグメントは、"sample02"、"index"、"aaa"、"bbb"、"ccc"、"ddd"、"eee"、"fff"、"ggg"の9個


まぁ、基本はこんなところか‥
あとは、ドキュメントを見ながらぼちぼちと(謎)。