Laravel4のcomposer installでエラーになる場合

laravel4のインストールでは php composer を利用しますが、仮想環境などのパフォーマンスが良くない環境で行うとエラーになることがありました。

$ composer install

Loading composer repositories with package information Installing dependencies (including require-dev)
- Installing symfony/translation (v2.3.2) Downloading: 100% ....

Generating optimized class loader
[Symfony\Component\Process\Exception\RuntimeException]
The process timed-out.
optimize
Script php artisan optimize handling the post-install-cmd event returned with an error

こんな感じで、どうも php artisan optimize の実行でタイムアウトのエラーが出ています。

composer.jsonを見てみると、composer の install、updateで optimize が実行されている模様です。

        "scripts": {
                "post-install-cmd": [
                        "php artisan optimize"
                ],
                "pre-update-cmd": [
                        "php artisan clear-compiled"
                ],
                "post-update-cmd": [
                        "php artisan optimize"
                ],

optimize が何をしているのか詳しく把握していませんが、エラーでぐぐっているとこんなissueを発見しました。

PHP Artisan Optimize - Timed out? · Issue #1050 · laravel/framework · GitHub

optimizeで使用している vendor/symfony/process/Symfony/Component/Process/Process.php のプログラムが、実行時間が60秒を超えるとタイムアウトしてしまうみたいです。

__construct で、$timeout = 60 と指定されている部分を、$timeout = 0(無制限) にして、php artisan optimize してみると正常に終了するようになりました。90秒くらいかかっている感じです。

ちょっと困った事になってしまいましたが、optimize は composer.json から実行されているので、composer.json の scripts で、optimizeの前後に timeout の値を置換する処理を入れて暫定的に対応しました。

        "scripts": {
                "post-install-cmd": [
                        "perl -pi -e 's/timeout = 60/timeout = 0/' vendor/symfony/process/Symfony/Component/Process/Process.php",
                        "php artisan optimize",
                        "perl -pi -e 's/timeout = 0/timeout = 60/' vendor/symfony/process/Symfony/Component/Process/Process.php"
                ],
                "pre-update-cmd": [
                        "php artisan clear-compiled"
                ],
                "post-update-cmd": [
                        "perl -pi -e 's/timeout = 60/timeout = 0/' vendor/symfony/process/Symfony/Component/Process/Process.php",
                        "php artisan optimize",
                        "perl -pi -e 's/timeout = 0/timeout = 60/' vendor/symfony/process/Symfony/Component/Process/Process.php"
                ],
                "post-create-project-cmd": [
                        "php artisan key:generate"
                ]
        },

名前空間について

現時点で安定版のKohana3.2はPHP5.2でも動作するためPHP5.3からのnamespaceはサポートしてません。

一応独自の仕組みがあって、classesの下にディレクトリを作成することで一意なクラス名を作成することが出来ます。
例えば、「classes/request/client/http.php」というpathになっていれば、「Request_Client_Http」というディレクトリをアンダーバー(_)で繋げたクラス名になります。classesが1つしかなければこれで問題無いのですが、Kohanaのクラス置き場は大きなくくりで言うとapplicationとmodulesとsystemの3カ所あり、それぞれがclassesディレクトリを持っていてプレフィックス等も付かないので、何らかの対策をしておかないと衝突してしまうことがあります。
優先順位は決まっていてapplication>modules>systemで、どこから呼び出してもこのルールが適用されてしまいます。例えば何かのmoduleにAccountクラス(module名/classes/account.php)を作っていて、applicationにも/classes/account.phpを作ってしまうと、Accountクラスはどこから利用したとしてもapplication配下のAccountが利用されることになります。

/application
    classes/
        account.php
/modules
    account/
        classes/
            account.php
※このような構成にしてしまうと、どちらもAccountクラスとなってしまい、優先度の違いによってaccountモジュールのaccount.phpは参照されないものとなってしまいます。

後は公式ドキュメントの図が参考になります。
http://kohanaframework.org/3.2/guide/kohana/files

このように共通の空間を利用することになるため、application配下しか利用していなければ大丈夫ですがmoduleを機能毎に分割しようとすると問題になります。
以下は私がやっている事で、正解では無いかもしれませんが名前空間の問題は防ぐことが出来ています。

ファイルの配置方法

まずsystemについてはKohana標準のクラス置き場で触る事は無いです。ここはsystem/classes/kohanaというディレクトリに全てのクラスの実態が置かれているため、Kohana_というプレフィックスが付く事になります。

applicationには共通で利用したいクラスを配置しています。そのためプロジェクト名でのプレフィックスを付けるため、classes以下にプロジェクト名のディレクトリを作成しています。「example」プロジェクトであれば、「application/classes/example/」となります。

modulesには各モジュール名でディレクトリを作ります。「account」モジュールを作ったとすると、「modules/account/classes/account/」

controllerとmodelについてはclasses直下に置く必要があるため、その下にディレクトリを作成します。
「modules/account/classes/controller/account/」

まとめるとこんな感じです。
classes、controller、model以下に直接クラスを配置せずに、applicationならプロジェクト名、modulesならモジュール名のディレクトリ名でプレフィックスを付けてやるということです。

example/
    application/
        classes/
            example/
            controller/
                example/
            model/
                example/
    modules/
        classes/
            account/
            controller/
                account/
            model/
                account/

ディレクトリ階層が深くなったり手間だったりしますが、無駄にハマらないように分けておいた方が無難だと考えています。

紹介 その4 MVCのC

※この記事は以下の公式ドキュメントを元に書いています。
http://kohanaframework.org/3.2/guide/kohana/mvc

今回はフレームワークの肝であるMVCのControllerについて紹介します。

Controllerについて

MVCフレームワークの基本動作はControllerがリクエストを受け取り、Modelから情報を取得してブラウザにViewを返します。では、Controllerはどのように呼び出されるのでしょう。
まずスタートするのはindex.phpです。Webサーバは.htaccess等により、index.phpを呼び出すように設定されています。index.phpはapplicationやmodules、systemへのpathの設定がされています。そしてapplication/bootstrap.phpをrequireします。
このbootstrapでアプリケーションの重要な設定を行います。その中にRouteの設定があります。以下はデフォルトのbootstrapに用意されているRouteの設定です。

Route::set('default', '(<controller>(/<action>(/<id>)))')
        ->defaults(array(
                'controller' => 'welcome',
                'action'     => 'index',
        ));

この意味については後日詳しく紹介します。ここで、リクエストされたURLを元にどのControllerを呼び出すかを決定し、Controllerクラスをnewして実行します。
この設定だとapplicationかmodulesの、classes/controller/welcome.phpが呼び出されます。デフォルトのwelcome.phpはapplication/classes/controller/welcome.phpにあり、中身はこのようになっています。

<?php defined('SYSPATH') or die('No direct script access.');

class Controller_Welcome extends Controller {

        public function action_index()
        {
                $this->response->body('module hello, world!');
        }

} // End Welcome

ここではModelもViewも登場しません。Controllerはclasses/controllerの下、もしくはそこに作られたサブディレクトリに設置する必要があります。そしてControllerクラスを継承する必要があります。あとはクラスロードの仕組み紹介 その2 クラスロードについて - isherの日記に従います。
以下のような例になります。

// classes/controller/foobar.php
class Controller_Foobar extends Controller {
 
// classes/controller/admin.php
class Controller_Admin extends Controller {
Controllers can be in sub-folders:

// classes/controller/baz/bar.php
class Controller_Baz_Bar extends Controller {
 
// classes/controller/product/category.php
class Controller_Product_Category extends Controller {

それと、継承元(System)のControllerには他にもいくつかの種類があります。Controller_Templateであったり、Controller_RESTです。これらの説明をすると長くなるので省略しますが、テンプレートやRESTなリクエストを処理するのに便利な仕組みが用意されています。もちろん、あなた自身もControllerを継承して独自のベースControllerを作る事が出来ます。共通の認証処理等をまとめて行うことが可能です。

actionについて

Controllerはクラスであり、処理を行うのはactionと呼ばれるメソッドです。この定義はControllerクラスに action_ のprefixを付けたpublicなメソッドになります。action_が付いていなかったりpublicでないメソッドはRouteから呼び出されることはありません。
actionの役割はデータをブラウザに返す事ですが、ここでViewを利用することになります。(利用しなくても良いですが)

public function action_hello()
{
    $this->response->body(View::factory('hello/world', $data)); // This will load views/hello/world.php
}

Controllerにはbefore()とafter()というメソッドを定義出来ます。before()はactionが呼び出される前に呼び出され、after()はactionが終了した後に呼び出されます。例えばログインが必要な処理をbefore()にまとめて記述することが可能です。

// Checking auth/login in before, and redirecting if necessary:
 
Controller_Admin extends Controller {
 
    public function before()
    {
        // If this user doesn't have the admin role, and is not trying to login, redirect to login
        if ( ! Auth::instance()->logged_in('admin') AND $this->request->action !== 'login')
        {
            $this->request->redirect('admin/login');
        }
    }
 
    public function action_login() {
        ...

紹介 その3 モジュールについて

※この記事は以下の公式ドキュメントを元に書いています。
http://kohanaframework.org/3.2/guide/kohana/modules

Kohanaが用意しているモジュール

標準のモジュールはmodulesディレクトリの配下におさめられています。これらを有効にするにはapplication/bootstrap.phpで場所を指定します。コメントアウトを解除すると利用可能になります。自分で作ったモジュールは別の場所に配置して読み込ませることも可能です。


/**
* Enable modules. Modules are referenced by a relative or absolute path.
*/
Kohana::modules(array(
// 'auth' => MODPATH.'auth', // Basic authentication
// 'cache' => MODPATH.'cache', // Caching with multiple backends
// 'codebench' => MODPATH.'codebench', // Benchmarking tool
// 'database' => MODPATH.'database', // Database access
// 'image' => MODPATH.'image', // Image manipulation
// 'orm' => MODPATH.'orm', // Object Relationship Mapping
// 'unittest' => MODPATH.'unittest', // Unit testing
// 'userguide' => MODPATH.'userguide', // User guide and API documentation
));

モジュール(modules)とは?

標準で用意されているモジュールはクラスを中心としたUtil系のライブラリですが、モジュールはそれ1つをアプリケーションにする事が出来ます。modulesディレクトリの構成はapplication配下とほぼ同じになっていて、クラスはもちろん、コントローラやモデル、ビュー、コンフィグ、多言語化のファイルまで含む事が出来ます。

これは巷ではHMVCと呼ばれているらしく、機能毎にモジュール化しておく事で再利用性も高まりますし見通しも良くなります。例えば私は会員制のサイトで以下の3つのモジュールを作成しています。

  1. site
  2. manage
  3. account

siteはトップ画面や問い合わせ画面など、ログインしなくてもアクセス出来る画面を集めています。
manageはログイン後に利用する管理画面を集めています。
accountはアカウント登録画面やログイン画面などのユーザ情報に関わる部分を集めています。
このモジュール間を連動させることで、siteやmanage内のクラスは、accountモジュールからログイン情報を受け取るといった使い方をしています。manageとaccountを分離しておくことで、新たなアプリを作ったとしてもログイン系の機能はaccountモジュールに任せることが可能となり、テストや機能追加がやりやすくなります。

私の個人的な意見としては、application以下は利用せずに、全てモジュールとして作成することをおすすめします。

モジュールを利用する際の注意点

Kohanaではautoloadを利用してクラスを読み込んでいます。紹介 その2 クラスロードについて - isherの日記
modulesディレクトリの構成はapplication以下とほぼ同じと書きましたが、名前空間を利用しているわけではなく、安易な名前をつけるとクラス名が衝突してしまうことが良く有ります。優先順位はapplication > modules > systemとなっていて、modules内の衝突は先に書いた物が優先されるようです。
例えばapplicationにapplication/classes/class1.phpを作成し、mymoduleというモジュールを作成してそこにmodules/mymodule/classes/class1.phpを作成したとします。どちらもclasses直下となるのでクラス名はClass1となります。この場合はどこで利用したとしても、application配下のClass1が利用されることになります。
これは便利でもあり、気をつけないとハマってしまう点でもあります。バッドノウハウになりますが、classesの下にモジュール名でディレクトリを作成する等すればクラス名の衝突は避けられると思います。

紹介 その4 MVCのC - isherの日記

紹介 その1 インストール方法

KohanaはPHPが動くWebサーバがあれば簡単に動かすことが出来ます。

ダウンロードする

まずはKohanaのWebサイトから最新版をダウンロードします。現在v3.2.0でした。

zipを展開してWebサーバに設置する

zipを展開すると以下のような構成になっています。


kohana-3.2-master-1/
LICENSE.md
README.md
application/
example.htaccess
index.php
install.php
modules/
system/

application/cacheとapplication/logsディレクトリの権限はWebサーバが書き込めるようになっていないといけません。ここでは二つのディレクトリをchmod 777してしまいます。

chmod 777 application/cache application/logs

そしてディレクトリごとApacheのDocumentRootに設定してやります。以下はApacheの設定例です。


DocumentRoot /path_to/kohana-3.2-master-1
DirectoryIndex index.php
ServerName example.com

AllowOverride all
Allow from all


ブラウザで確認してみる

設定が済んだらアクセスしてみます。以下のインストール確認画面が表示されると成功です。logsディレクトリやcacheディレクトリに書き込み権限が無かったり、足りないモジュールがあったりするとエラーが出るので修正します。

Optional Testsは全部成功する必要はありません。

このインストール確認ページはindex.phpから呼出されますが、ただ表示されるだけで特に何をするものでもありません。不要になればinstall.phpを削除するか名前を変えてやります。

install.phpを排除して再度確認してみる


hello, world!が表示されると成功です。
これはデフォルトControllerであるapplication/classes/controller/welcome.phpのaction_index()が呼出された結果です。


response->body('hello, world!');
}

} // End Welcome

このように非常に簡単に使い始めることが出来ます。使わないのであればデータベースを準備する必要もありません。ただ、application、modules、system等のPHPコードが置かれる場所がApacheのDocumentRoot以下にあるのは気持ち悪く思います。これらのPATHの設定はindex.phpでされています。


/**
* The directory in which your application specific resources are located.
* The application directory must contain the bootstrap.php file.
*
* @see http://kohanaframework.org/guide/about.install#application
*/
$application = 'application';

/**
* The directory in which your modules are located.
*
* @see http://kohanaframework.org/guide/about.install#modules
*/
$modules = 'modules';

/**
* The directory in which the Kohana resources are located. The system
* directory must contain the classes/kohana.php file.
*
* @see http://kohanaframework.org/guide/about.install#system
*/
$system = 'system';


DocumentRoot以下にはindex.phpだけにして、PHPコードは別の場所に移動した方が良いでしょう。

紹介 その2 クラスロードについて - isherの日記

紹介 その2 クラスロードについて

※この記事は以下の公式ドキュメントを元に書いています。
http://kohanaframework.org/3.2/guide/kohana/conventions

クラスロード(autoload)について

Kohanaのクラスロードの仕組みはPHP5から登場したautoloadの仕組みを利用しています。コードを書くことになるapplicationの下を見てみましょう。


application/

bootstrap.php
cache/
classes/
controller/
model
config/
i18n/
logs/
messages/
views/


MVCフレームワークに触れている人であれば特に違和感を感じないと思います。ここであなたがPHPのクラスファイルを作成していくのはclasses/以下になります。controllerとmodelというディレクトリが作られていますが、それらについては次回に紹介します。
ここでsystemの下も覗いてみましょう。ほぼapplication下と同じ構成になっていて、classesの下には沢山のクラスファイルが置かれているはずです。

system/

classes/
arr.php
cli.php
controller.php
........
config/
guide/
i18n/
media/
messages/
tests/
utf8/
views/


ではsystem/classes/arr.phpを開いてみます。

<?php defined('SYSPATH') or die('No direct script access.');

class Arr extends Kohana_Arr {}

クラス名はArr。実態は無く、Kohana_Arrを継承しているだけです。これは意味のあることで、なぜこうしているかは後で解説します。
application、system、modules問わず、PHPクラスはclassesの下に作ります。そしてautoloadを利用する都合上、ファイル名やクラス名にはルールがあり、それに従う必要があります。

  1. サブディレクトリの下に配置する場合、ディレクトリとクラス名は_(アンダーバー)で区切る
  2. ファイル名、ディレクトリ名は全て小文字にする
  3. クラスは全てclassesの下に配置する

公式ドキュメントの例が分かりやすいので紹介します。

Class Name File Path
Controller_Template classes/controller/template.php
Model_User classes/model/user.php
Database classes/database.php
Database_Query classes/database/query.php
Form classes/form.php

先ほどのsystem/classes/arr.phpはファイル名がarr.phpなのでクラス名はArrになっています。継承しているKohana_Arrはsystem/classes/kohana/arr.phpPHPファイルがあります。このようにルールに従っていればrequireやincludeする必要はありません。

これはapplication/classesの下も同じです。application/classes/controller/welcome.phpがデフォルトコントローラとして作成されていますが、それのクラス名はController_Welcomeになっています。もしapplication/classes/arr.phpを作成したらどうなるかと言うと、その環境下ではどこのクラスから参照したArrであっても、systemではなくapplicationの物が参照されることになります。Kohanaのautoloadには優先順位があり、application > modules > systemとなっています。applicationに置いたクラスが最も優先度が高く、他のmodulesやsystemにArrクラスがあったとしてもapplicationの物がロードされます。

ここで、なぜsystemのArrに実態が無く、Kohana_Arrを継承しているだけなのかの解説をします。もしArrの機能に不満があり、ちょっと拡張したくなったとします。例えば1つのメソッドを増やしたいのであれば、Arrを継承してMyArrを作成すれば良いと思いますが、既にArrを多くの場所で利用してしまっている場合クラス名を変えるのは難しいです。
そこで、applicationのclassesにArrクラスを作成し、Kohana_Arrを継承するようにします。そうすればautoloadはsystemのArrをロードせずにapplicationのArrをロードするようになります。そしてメソッドが定義されているKohana_Arrはsystemの物がロードされるので、applicationのArrから呼び出せるしメソッドをオーバーライドする事も可能です。
このような拡張を容易にするために、systemに容易されているクラスのほとんどはこのような形になっています。

クラスはrequireやincludeでロードするよりも、必要な時点で読み込むautoloadを利用した方が一般的に高速に動作します。色々なクラスを読み込むWebフレームワークであればそれなりの差になると思います。Kohanaのルールに従ったクラスを用意するようにしましょう。

紹介 その3 モジュールについて - isherの日記

紹介 その0 Kohanaについて

PHPフレームワークで最近の流行と言ったらSymfonyCakePHP、Codeigniter当たりでしょうか。どれもそれぞれ素晴らしいのですが、その中で特に注目度が高いのは軽量フレームワークであるCodeigniterでしょうか。
自分も使った事あるのですが、シンプルで高速、コアな部分も含めてカスタマイズが簡単ということで気に入ってます。ただし、最近重要なライセンス変更が行われたのと、PHP4時代からの遺産を引きずっていてモダンではないというのが気になってました。
そこで、この軽量PHPフレームワークの分野で他に使えそうなものが無いか探していたところ、Kohanaと呼ばれるCodeigniterからforkして出来たフレームワークを発見し、なかなか良さそうだったのでCodeigniterで作っていたプロジェクトを全て置き換えてみました。どんなフレームワークも使っていると不満は出てくるのですが、とりあえずCodeigniterで出ていた不満がほぼ解消され、以前よりメンテしやすくなりました。
Kohanaは素晴らしいフレームワークなのですが、まだ日本での使用例がほとんど無いという状況ですので少しでも紹介出来ればと思います。

紹介 その1 インストール方法 - isherの日記
紹介 その2 クラスロードについて - isherの日記
紹介 その3 モジュールについて - isherの日記
紹介 その4 MVCのC - isherの日記