A Day In The Boy's Life -15ページ目

A Day In The Boy's Life

とあるエンジニアのとある1日のつぶやき。

PHPのコードキャッシュとしてOPcacheがPHP5.5以降バンドルされているのですが、CentOS6系のサーバー上でPHPをソースからインストールする際に、--enable-opcacheオプションをつけてコンパイルすると下記のエラーが出てうまくインストールできずにはまりました。


$ ./configure --enable-opcache
- snip -
checking "whether flock struct is linux ordered"... "no"
checking "whether flock struct is BSD ordered"... "no"
configure: error: Don't know how to define struct flock on this system, set --enable-opcache=no

まぁ、メッセージのとおり--enable-opcache=noオプションをつければコンパイルは通るわけですけど、コードキャッシュ使ったほうがパフォーマンスも良くなりますし、APCとかコードキャッシュしてくれなくなったりしてますんでOPcacheを使いたいわけですよ。

で、解決方法は共有ライブラリのパスを定義している/etc/ld.so.confに/usr以下のライブラリのディレクトリを追加したら解決しました。


include ld.so.conf.d/*.conf
/usr/local/lib
/usr/lib64

追加した後は、ldconfigで設定を反映します。


# ldconfig

その後、もう一度PHPのconfigureをしたら通ったので、そのままmake、make installします。

インストール完了後は、OPcacheを有効にするためにphp.iniを編集。


[opcache]
zend_extension=/usr/local/lib/php/extensions/no-debug-non-zts-20131226/opcache.so
opcache.enable=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=4000
opcache.revalidate_freq=30
opcache.fast_shutdown=1
opcache.enable_cli=1

余談ですけど、Xdebugと一緒に使う場合はXdebugの設定より前に上記を書いておかないといけないらしいです(詳細はマニュアル 参照)。

最後に、Apacheを再起動してOPcacheが使えているかを確認。


# php -v
PHP 5.6.13 (cli) (built: Sep 28 2015 11:18:05) 
Copyright (c) 1997-2015 The PHP Group
Zend Engine v2.6.0, Copyright (c) 1998-2015 Zend Technologies
    with Zend OPcache v7.0.6-dev, Copyright (c) 1999-2015, by Zend Technologies

OPcacheの詳しい使い方は以前に書いた、「PHPコードキャッシュにOPcacheを使ってみる 」も参考にしてみてください。





Laravelで入力フォームのエラーチェックをしたいといった場合に、標準のバリデーションを利用すれば独自にロジックを組み込むことなく容易に実装することが可能です。

ってことでLaravel標準のバリデータの使い方のまとめです。

ここでは、Laravel4.2をベースに書いています。



Laravel標準のバリデータを利用する


どういったバリデータが標準で用意されているのかを知りたければ、エラーの際に出力されるメッセージの一覧の定義ファイルを見るのが早 いかもしれません。


<?php

return array(
    /*
    |--------------------------------------------------------------------------
    | バリデーション言語行
    |--------------------------------------------------------------------------
    |
    | 以下の言語行はバリデタークラスにより使用されるデフォルトのエラー
    | メッセージです。サイズルールのようにいくつかのバリデーションを
    | 持っているものもあります。メッセージはご自由に調整してください。
    |
    */
    "accepted"             => ":attributeを承認してください。",
    "active_url"           => ":attributeが有効なURLではありません。",
    "after"                => ":attributeには、:date以降の日付を指定してください。",
    "alpha"                => ":attributeはアルファベッドのみがご利用できます。",
    "alpha_dash"           => ":attributeは英数字とダッシュ(-)及び下線(_)がご利用できます。",
    "alpha_num"            => ":attributeは英数字がご利用できます。.",
    "array"                => ":attributeは配列でなくてはなりません。",
    "before"               => ":attributeには、:date以前の日付をご利用ください。",
    "between"              => array(
        "numeric" => ":attributeは、:minから:maxの間で指定してください。",
        "file"    => ":attributeは、:min kBから、:max kBの間で指定してください。",
        "string"  => ":attributeは、:min文字から、:max文字の間で指定してください。",
        "array"   => ":attributeは、:min個から:max個の間で指定してください。",
    ),
    "boolean"              => ":attributeには、trueかfalseを指定してください。",
    "confirmed"            => ":attributeと、確認フィールドとが、一致していません。",
    "date"                 => ":attributeには有効な日付を指定してください。",
- snip -

配列のキーがバリデーションのルールとなっており、ルールに違反した場合は配列の値に格納されているメッセージが出力されます。

例えば、入力項目がアルファベットのみであるかのチェックや指定の日付の正当性のチェックなどのルールが含まれています。

まぁ、boolean型かどうかのチェックでエラーを出すかなど、使うのかどうか疑問なルールもあるわけですが、取りあえず標準としては一般的に使いそうなバリデーションが定義されています。


また、これは日本語化された定義ファイルになりますが、laravel/app/lang/enディレクトリの中にも英語がベースとなっているメッセージ定義ファイルがあります。

この使い分けは、laravel/app/config/app.php内のロケールの定義に依存します。


    /*
    |--------------------------------------------------------------------------
    | アプリケーションローカル設定
    |--------------------------------------------------------------------------
    |
    | アプリケーションローカルは翻訳サービスプロバイダーにより使用される
    | デフォルトローカルを指定します。アプリケーションで提供するローカルを
    | 自由に設定してください。
    |
    */

    'locale' => 'ja',

    /*
    |--------------------------------------------------------------------------
    | アプリケーションフォールバック言語
    |--------------------------------------------------------------------------
    |
    | フォールバック言語は現在のローカルが使用できない場合に、
    | 代替として使われます。アプリケーション全体に対して用意されている
    | 言語フォルダーに対応するコードであればどれでも使用可能です。
    |
    */

    'fallback_locale' => 'en',

上記のように設定することで標準はja(日本語)のロケールが設定され、それが存在しない場合にen(英語)が呼び出されます。

他の言語にローカライズしたい場合は、新たなロケールとメッセージファイルを用意します。


で、具体的なバリデーションの実装方法ですが、まずはバリデーション用のファイルを作りそれを読み込むようにします。

バリデーションファイルへのパスの通し方は「Laravelで独自に作ったライブラリファイルへのパスの通し方 」を参照。


class MyValidator
{
    public function validate (array $params)
    {
        $validator = Validator::make($params, ['id' => 'required|max:10', 'passwd' => 'required_if:flg,1|alpha_num']);
        // バリデーションチェック
        if (!$validator->passes()) {
            $this->errors = $validator->messages();
            return false;
        }
        return true;
    }

    public function errors()
    {
        return $this->errors;
    }
}

バリデーションの作り方はValidator::make()で作成し、チェック対象の引数とその配列内のキーにそれぞれバリデーションのルールを渡します(複数のルールを適用したければパイプで区切ります)。

例えば、idのパラメータは必須(required)でかつ最大10文字(max:10)であるかをチェックしています。

passwdパラメータでチェックしているrequired_ifは特殊でflgが1の場合に必須かどうかをチェックするというものです。

詳細は、マニュアル のほうも確認してみてください。


このバリデーションを元に実際にチェックしてみたいと思います。

コントローラファイルから先ほど作ったバリデーションを呼び出してエラーがあるかどうかをチェックします。


public function foo()
{
    $obj = App::make('MyValidator');
    // リクエストパラメータを取得
    $params = Input::all();
    // バリデータにパラメータを渡してチェック
    if (! $obj->validate($params)) {
        // エラーがある場合は何がしかのエラー画面を出力
        return View::make('Common::Error.index')->withErrors($obj->errors());
    }
    return View::make('hello');
}

エラーがある(validate()からfalseが返ってくる)場合は専用のエラー画面を出力していますが、この辺はお好みで。



バリデーションをカスタマイズする


当然、デフォルトで用意しているバリデーションだけではアプリケーションの要件を満たさない場合がありますし、もっと複雑なバリデーシ ョンを作りたいという場合があります。

この場合、バリデーションルールを独自に作ることができます。

例えば、先ほどのvalidate()に独自のルールを追加してIDのチェック用にDBのマスタ上に存在するかといった処理を加えたルールを追加して みます。


public function validate (array $params)
{
    // バリデータのルールを定義
    Validator::extend('idCheck', function ($attribute, $value, $parameters, $validator) {
        // IDが存在するかどうか
        $db = App::make('DbMaster');
        if (!$db->checkMember($value)) {
            return false;
        }
        // 全パラメータを取得
        $data = $validator->getData();
        // flgが1の場合のみの処理を何か
        if ($data['flg'] === "1" && $db->checkSomething($value)) {
            return false;
        }
        return true;
    });
    // エラーメッセージ
    $messages = array(
        'id_check' => ':attributeが存在しません'
    );
    // 項目名
    $attributes = array(
        'id' => 'ログインID'
    );
    $validator = Validator::make($params, [
        'id' => 'required|max:10|id_check',
        'passwd' => 'required_if:flg,1|alpha_num'
        ], $messages);
    $validator->setAttributeNames($attributes);
    // バリデーションチェック
    if (!$validator->passes()) {
        $this->errors = $validator->messages();
        return false;
    }
    return true;
}

バリデーションルールの拡張はValidator::extend()で行い、第1引数にルール名(idCheck)を指定します。

第2引数は無名関数として定義しており、中身に詳細なバリデーションロジックを記述します。

単純な話、何らかの処理をして最終的に正しければtrueを、誤りがある場合はfalseを返せばよいだけです。


その他のパラメターと複合的にチェックしたい場合、無名関数の第4引数にある$validatorのgetData()メソッドでその他のパラメータを受け 取れたりします。


// 全パラメータを取得
$data = $validator->getData();

拡張したルールは、それに対応するエラーメッセージや項目名を定義する必要があります。

エラーメッセージはルール名をスネークケース(idCheckをid_checkにする)にしたキーを元に配列に格納します。


// エラーメッセージ
$messages = array(
    'id_check' => ':attributeが存在しません'
);

項目名は、エラーメッセージ内の項目名に何を入れるかを定義します。

この際の項目名は、チェックするパラメータ名をキーにしておきます。


// 項目名
$attributes = array(
    'id' => 'ログインID'
);

要は、このバリデーションに引っかかった場合は「ログインIDが存在しません」というエラーメッセージを受け取ることができます。


最後に、定義したルールを適用します。


$validator = Validator::make($params, [
    'id' => 'required|max:10|id_check',
    'passwd' => 'required_if:flg,1|alpha_num'
    ], $messages);
$validator->setAttributeNames($attributes);

最初のバリデーションとは異なり、idのパラメータに適用するバリデーションとしてid_checkが追加されています。

エラーメッセージの設定は、Validator::make()の第3引数に渡し、項目名はsetAttributeNames()によってセットします。


バリデーションのカスタマイズは標準のValidatorそのものを拡張するやり方もあったりするのですが、長くなったので別途まとめたいと思います。





コントローラーファイルにごりごりとビジネスロジックを書いていくようなやり方もありますが、コントローラー間の共通処理など別ファイルにロジックを分離したいということはよくあることです。

ってことで、Laravelでライブラリを定義する方法のまとめです。

ここでは、Laravel4.2をベースに書いています。



Laravelでライブラリを読み込む方法


基本的にLaravelはファイルやディレクトリの構成の制約をうけません。

どこに何のファイルを置いているのかを定義しておけば素直に読み込んでくれます。

ということで、まずはライブラリ用のディレクトリ(lib)を今回の例ではlaravel/app以下に作成するとします。


$ cd /path/to/laravel/app
$ mkdir lib

この下にライブラリファイルを作っていくわけですが、このディレクトリへのパスの通し方は幾つか方法があります。


まずは、composerを使う方法。

composerで読み込む場合は、laravel/composer.jsonにオートロードするように記載します。


    "autoload": {
        "classmap": [
            "app/commands",
            "app/controllers",
            "app/models",
            "app/database/migrations",
            "app/database/seeds",
            "app/tests/TestCase.php",
            "app/lib",
        ]
    },

composer.jsonを編集した後は


$ composer dump-autoload

を実行する必要があります。

composerの使い方の詳細は「[Composer] プログラムのオートロードはComposerにお任せ 」を参照。

composerを使うとそっちに依存したり、ディレクトリが増えるたびにdump-autoloadするのが面倒だったりもするというデメリットもあったりします。


次に、app/start/global.phpに対象ディレクトリを書いてしまう方法です。


/*
|--------------------------------------------------------------------------
| Laravelクラスローダーの登録
|--------------------------------------------------------------------------
|
| Composerを使用することに加え、コントローラーとモデルをロードするために
| Laravelのクラスローダーを使用することもできます。Composerを更新しなくても
| 「グローバル」な名前空間にあなたのクラスを設置しておくのに便利です。
|
*/

ClassLoader::addDirectories(array(
    app_path().'/commands',
    app_path().'/controllers',
    app_path().'/models',
    app_path().'/database/seeds',
    app_path().'/lib',
));

最後に、laravel/libへのパスを追加しています。

こちらは、ファイルを編集するとすぐに読み込んでくれるためシステムの構成に柔軟に対応させることができるので一番手っ取り早い方法かもしれません。

ただし、Laravel内の全システムで共通となるため、全てで使う共有ライブラリを作りたいといった場合はよいですが、システム(やコントローラ)ごとのライブラリを作りたいといった場合は多少構成が煩雑になる可能性があります。


最後は、サービスプロバイダを使うやり方です。

こちらは、「Laravelでサブシステム用のディレクトリを分離する 」に書いたようにシステムごとにディレクトリを分離したいといった場合に便利なやり方ですが、少し設定が面倒です。


最初に、サービスプロバイダ用のファイル(CommonServiceProvider.php )を配置します。

今回は、laravel/app/Myproviders/Servicesに置くとします(実際にディレクトリやファイル名はどこでも大丈夫です)。

サービスプロバイダ用のファイルは下記のようなものとなります。


<?php

namespace Myproviders\Services;

use Illuminate\Support\ServiceProvider;

class CommonServiceProvider extends ServiceProvider
{
    public function boot()
    {
        \ClassLoader::addDirectories(array(
            __DIR__ . '/../../lib',
        ));
    }
    /**
     * Iocコンテナの登録.
     *
     * 事前に登録したいクラスがある場合はここで設定する.
     *
     * @return void
     */
    public function register()
    {
    }
}

あとで、composerのオートロードにも登録するため名前空間を定義しています。

そして、boot()内にライブラリ用のディレクトリパスを定義しています(サービスプロバイダファイルからみたディレクトリパスを指定します) 。

次に、laravel/app/config/app.phpを編集し、先ほど作ったサービスプロバイダを登録します。


    /*
    |--------------------------------------------------------------------------
    | オートロードサービスプロバイダー
    |--------------------------------------------------------------------------
    |
    | ここにリストしたサービスプロバイダーはアプリケーションのリクエストに対し
    | 自動的にロードされます。アプリケーションの機能を拡張するため、この配列へ
    | 自由に自分のサービスを付け加えてください。
    |
    */

    'providers'=> array(

        'Illuminate\Foundation\Providers\ArtisanServiceProvider',
        'Illuminate\Auth\AuthServiceProvider',
        'Illuminate\Cache\CacheServiceProvider',
- snip -
        'Myproviders\Services\CommonServiceProvider'
    ),

指定方法は、PSR-0のオートロードに準拠した名前空間で指定します。

最後に、laravel/composer.jsonにサービスプロバイダを置いたディレクトリを登録しておきます。


    "autoload": {
        "classmap": [
            "app/commands",
            "app/controllers",
            "app/models",
            "app/database/migrations",
            "app/database/seeds",
            "app/tests/TestCase.php"
        ],
        "psr-0": {
            "Myproviders": "app/"
        }
    },

編集後は、dump-autoloadしておきます。

こちらも編集ファイルが多かったり、composerを使わないといけないといったデメリットはあるんですけど、「Laravelでサブシステム用のディレクトリを分離する 」に書いたようにシステムを分離したいといった場合に便利な方法かもしれません。



Laravelのライブラリを作成する


これでライブラリが読み込める環境となったわけですが、ライブラリファイル自体は一般的なクラスファイルとして定義すればよいだけです(モデルやバリデータを作りたいといった場合は継承するクラスがありますが今回は割愛します) 。


<?php

class Tools
{
    function foo() {
        return true;
    }
}

呼び出し方は、コントローラから


    public function bar()
    {
        $obj = App::make('Tools');
        $obj->foo();
    }

とするだけで呼び出せます。

余談ですが、サービスプロバイダを作っておくと独自のプレフィックスを定義できたりもして便利です。

先ほどのサービスプロバイダのboot()に


    public function boot()
    {
        \ClassLoader::addDirectories(array(
            __DIR__ . '/../../lib',
        ));

        \View::addNamespace('Common', __DIR__ . '/../../views');
        \Config::addNamespace('Common', __DIR__ . '/../../config');
    }

このように「Common」というプレフィックスを定義しておくことで、


// 設定ファイルの読み込み
$env = Config::get('Common::const.MY_ENV');
// Viewファイルを読み込み
return View::make('Common::top');

のようなことができて設定ファイルやViewファイルを分離、明示することができたりもします。