page_adsence

ラベル php の投稿を表示しています。 すべての投稿を表示
ラベル php の投稿を表示しています。 すべての投稿を表示

2016年5月25日水曜日

PHPのバージョンアップに伴うPHP関連の調査メモ

PHPを5.2系から5.6系にバージョンアップするにあたって、諸々調査してみました。
  • php.ini関連(主にAPC- > APCu + OPCacheへの移行に関して)
  • php-fpmの設定値に関して
  • mysqlndの利用可・不可

php.ini関連(主にAPC- > APCu + OPCacheへの移行に関して)

APCはPHP 5.5以降では利用できなくなってしまったため、APCu + OPCacheに乗り換える必要があります。
乗り換えるにあったって、諸々調査したのでその結果を残しておこうと思います。

APC(Alternative PHP Cache)

まずAPCとは「Alternative PHP Cache (APC) は、PHP の実行コードをキャッシュする仕組みで、 フリーかつオープンに使用できます。PHP の中間コードのキャッシュ・最適化を行うための、フリーでオープンかつ堅牢なフレームワークを提供するということを目標としています。」(PHPの公式から抜粋)
と書かれていますが、実際は中間コードのキャッシュだけではなく、インメモリ型のKVSとして利用することも可能です。
※但しネットワーク越しのアクセスは不可

APCu

APCu is APC stripped of opcode caching.
→ APCuはAPCのオペコードキャッシュを除いたものです。

The first APCu codebase was versioned 4.0.0, it was forked from the head of the APC master branch at the time.
→ APCuの最初のコードはバージョン4.0.0で、APCのマスターブランチをフォークして作成されています。

PHP 7 support is available as of APCu 5.0.0.
→ PHP 7はAPCu5.0.0が利用出来る様になっています。

APCu can provide a compatibility mode, such that it can provide a drop in replacement for the applicable parts of APC.
→ APCuは互換性モードを提供しており、APCの一部の機能の下位互換を持っています。

公式から抜粋して訳してみたのですが、合っている保証はありません。
要するにインメモリ型のKVSの機能のみを提供するもので、APCからフォークされて出来ているみたいです。

OPCache(Zend OPCache)

OPCacheは「OPcache はコンパイル済みのバイトコードを共有メモリに保存し、PHP がリクエストのたびにスクリプトを読み込み、パースする手間を省くことでパフォーマンスを向上させます。」
と記載されている通りの機能です。
1点問題になるのは、OPCacheはシンボリックリンクを解決した状態でキャッシュされるため、
シンボリックリンクを変更しても、キャッシュは更新されないという問題がある。

http://kohkimakimoto.hatenablog.com/entry/2014/09/13/154342

また、opcache_resetもCLIから実行するとCLI側のオペコードキャッシュしか消えないようです。
そしてWebから実行されたopcache_resetはWeb側もCLI側も消えるという・・・。
この辺は自分でも検証してみようと思います。
結果は後日追記する予定です。

http://tech.gmo-media.jp/post/136590171029/php-53-to-php-56

php-fpm(FastCGI Process Manager)の設定値に関して

「PHP の FastCGI 実装のひとつで、 主に高負荷のサイトで有用な追加機能を用意しています。」 各サーバのスペックに応じてチューニングする必要のある部分は下記の所になります。(下記はデフォルト値です)
項目名設定値
pm.max_children50
pm.start_servers5
pm.min_spare_servers5
pm.max_spare_servers35
pm.max_requestsコメントアウト

この辺の値を変更する必要があるのですが、一律で変更出来るものではないので、
各自負荷テストを行った結果を反映させる必要があります。

mysqlndの利用可・不可

mysqlnd「MySQL Native Driver」とは「MySQL Client Library」に変わる新たなライブラリです。
mysqlndにしかない機能も多数あるので、移行出来るものならしたいものですが、そうはいかない世の中でした。
php5.5以降からmysqlndがデフォルトで利用される様になったのですが、mysqlndで16バイト長のパスワードが設定されているMySQL Serverに接続しにいくと、エラーが出てしまい接続出来ないという現象が発生します。
現状稼働しているMySQLに接続しにいかないといけないとかがあると、一手間必要になってしまいます。
どういった事が必要になるかというと、mysqlndでは41バイト長のパスワードハッシュでないと接続出来ないので、
バージョンの古いMySQLのバージョンだと、16バイト長のパスワードハッシュになっているので、それを41バイトハッシュに書き換えてやる必要があります。
影響範囲の洗い出しが簡単で、DB停止とかが出来るのであれば、変更可能だとは思いますが、そうでない場合は簡単には踏み切れないですね・・・。
PHP5.2(libmysql)とPHP5.6(mysqlnd)で、それぞれ16バイトと41バイトのパスワードが設定されている時にどういった挙動になるかを表にしてみました。
PHP5.2.17(libmysql)PHP5.6.18(mysqlnd)
MySQL5.1 [16byte Password]OKNG(mysqlnd cannot connect)
MySQL5.6 [16byte Password]OKNG(パスワード不一致)
MySQL5.1 [41byte Password]OKOK
MySQL5.6 [41byte Password]OKOK

基本的にはパスワードさえ41バイト長のものに変えてしまえばログイン出来るのですが、
注意点としては、MySQL4.0以前のクライアントが残っていると41バイト長のパスワードは受け付けられないので、ログインできなくなります。(下記の様なエラーが出るみたいです)
> mysql -h localhost -u root
Client does not support authentication protocol requested
by server; consider upgrading MySQL client
とはいえ、MySQL5.7では16バイト長のパスワード(OLD_PASSWORD)のサポートがなくなるみたいですので、早めに移行するに越したことはないですね。
既存のデータベースに引っ張られない様なシステムの構築する場合は、積極的に使っていきたいと思います。

現状利用しているphpのモジュール一覧を取得する。

phpのモジュール一覧を確認しようと思ったのですが、オプション忘れたので調べた。

言わずと知れたphpinfoのコマンド版
php -i
今回知りたかったロードしているモジュール一覧
php -m

phpの公式にコメント付きで書いてあるので、詳細はそちらで。
http://php.net/manual/ja/features.commandline.options.php

2016年2月20日土曜日

composer updateでClass not found

Laravel4.2を利用している環境で、composer updateを行うと、composer updateの後続処理として、
php artisan clear-compiled
php artisan optimize

が実行されるのですが、上記の処理を実行すると、app/config/app.phpに登録している自分で作ったServiceProviderを読み込む時にClass not foundでエラーになるという状態になっていた。
$ composer update
Loading composer repositories with package information                                                                     Updating dependencies (including require-dev)

Writing lock file
Generating autoload files
> php artisan clear-compiled
PHP Fatal error:  Class 'MyAPP\Services\App\AppServiceProvider' not found in /home/user/site/myapp/vendor/laravel/framework/src/Illuminate/Foundation/ProviderRepository.php on line 157
{"error":{"type":"Symfony\\Component\\Debug\\Exception\\FatalErrorException","message":"Class 'MyAPP\\Services\\App\\AppServiceProvider' not found","file":"\/home\/user\/site\/myapp\/vendor\/laravel\/framework\/src\/Illuminate\/Foundation\/ProviderRepository.php","line":157}}Script php artisan clear-compiled handling the post-update-cmd event returned with an error



  [RuntimeException]
  Error Output: PHP Fatal error:  Class 'MyAPP\Services\App\AppServiceProvider' not found in /home/user/site/myapp/vendor/laravel/framework/src/Illuminate
  /Foundation/ProviderRepository.php on line 157



update [--prefer-source] [--prefer-dist] [--dry-run] [--dev] [--no-dev] [--lock] [--no-plugins] [--no-custom-installers] [--no-autoloader] [--no-scripts] [--no-progress] [--with-dependencies] [-v|vv|vvv|--verbose] [-o|--optimize-autoloader] [-a|--classmap-authoritative] [--ignore-platform-reqs] [--prefer-stable] [--prefer-lowest] [--] []...
一度、自分で作ったServiceProviderをコメントアウトして、php artisan dump-autoloadし、コメントアウトを外して再度php artisan dump-autoloadすると問題なく使用出来る様になる。
使用出来る様になる理由は、vendor/composer/autoload_classmap.phpに自分で作ったServiceProviderのパスが記入されるためで、
再度composer updateするとそのファイルを含めたvendor/composer配下は再生成されるため、再びClass not foundになってしまう。

原因はcomposer.jsonに書いていたパスが間違っていたというなんとも言えないミスでした。

ディレクトリ構成は、下記の通りになっています。
/vendor/vendor_name/package_name/src/MyAPP/Service/XXXX/XXXXServiceProvider.php

誤ったcomposer.jsonの記述
"autoload": {
    "psr-4": {
        "MyAPP\\": "src/"
    }
}
正しいcomposer.jsonの記述
"autoload": {
    "psr-4": {
        "MyAPP\\": "src/MyAPP/"
    }
}
このパスが間違っているせいでautoload_psr4.phpの中の処理で引っかからずに、
autoload_classmap.phpにない場合にエラーになってしまっていました。
ちなみにcomposer内のファイル読み込み処理は、下記のファイルで行われている。
PSR-4 lookupと書かれている所の$fileに読み込むファイルのパスがセットされ、ファイルの存在確認をしている。
$ vi vendor/composer/ClassLoader.php

                            〜略〜

private function findFileWithExtension($class, $ext)
{
    // PSR-4 lookup
    $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;

    $first = $class[0];
    if (isset($this->prefixLengthsPsr4[$first])) {
        foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) {
            if (0 === strpos($class, $prefix)) {
                foreach ($this->prefixDirsPsr4[$prefix] as $dir) {
                    if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
                        return $file;
                    }
                }
            }
        }
    }

    // PSR-4 fallback dirs
    foreach ($this->fallbackDirsPsr4 as $dir) {
        if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
            return $file;
        }
    }

    // PSR-0 lookup
    if (false !== $pos = strrpos($class, '\\')) {
        // namespaced class name
        $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
            . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
    } else {
        // PEAR-like class name
        $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
    }

    if (isset($this->prefixesPsr0[$first])) {
        foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
            if (0 === strpos($class, $prefix)) {
                foreach ($dirs as $dir) {
                    if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
                        return $file;
                    }
                }
            }
        }
    }

    // PSR-0 fallback dirs
    foreach ($this->fallbackDirsPsr0 as $dir) {
        if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
            return $file;
        }
    }

    // PSR-0 include paths.
    if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
        return $file;
    }
}
composer.jsonファイルを修正して、composer updateコマンドを打つ。 これでエラーが出なくなりました。

2016年1月29日金曜日

Laravel4.2で入力->確認->完了の画面遷移を実装してみる。(CSRF対策もおまけで)

Laravelでフォームを作るとどんな感じになるのか試してみた。
CSRF対策と2重送信の防止をするという前提で作ってみました。

app/routes.phpを編集し、入力、確認、完了画面で実行するコントローラーとアクションを定義する。
入力画面に関してはCSRF対策用のフィルタは通さずに、確認、完了画面に関してはCSRF対策用のフィルタを通す様に記述しました。
CSRF対策用のフィルタ処理はapp/filters.phpに書いてあります。(Laravelが元々用意している機能)
$ vi app/routes.php
Route::get('/input', 'HomeController@input');
Route::group(array('before' => 'csrf'), function()
{
    Route::post('/confirm',  'HomeController@confirm');
    Route::post('/complete', 'HomeController@complete');
});

続いてアクション。
下記の3つのメソッドを追加。
$ vi app/controllers/HomeController.php
public function input()
{
    return View::make('input');
}

public function confirm()
{
    return View::make('confirm');
}

public function complete()
{
    Session::regenerateToken();

    // 登録処理

    return View::make('complete');
}
完了画面で遷移してきた段階で、Laravelが作っているCSRF対策用のトークンを再生成しています。
これは、このトークンを2重送信防止用にも使用しているため、こういった形をとっています。
CSRF対策だけならこういった対応は必要ないと思います。
結局、「Session::regenerateToken()」は_tokenという名前でトークンをセッションに保存しているので、1セッションで複数フォームを開いた場合、
先に送信した方は登録されるが、後で送信された方はトークンが一致せずにエラーになる。
こういう挙動を許すかどうかは、そのシステムの要件次第なので、使用時に判断してもらえればと思います。

最後にテンプレート。
入力画面
$ vi app/views/input.blade.php
{{ Form::open(array('url' => 'confirm')) }}
<table>
<tr>
<td>タイトル</td><td>{{ Form::text('title') }}</td>
</tr>
<tr>
<td>メッセージ</td><td>{{ Form::textarea('message') }}</td>
</tr>
<tr>
<td colspan="2">{{ Form::submit('内容を確認') }}</td>
</tr>
</table>
{{ Form::close() }}

確認画面
$ vi app/views/confirm.blade.php
{{ Form::open(array('url' => 'complete')) }}
<table>
<tr>
<td>タイトル</td><td>{{ Input::get('title') }}{{ Form::hidden('title', Input::get('title')) }}</td>
</tr>
<tr>
<td>メッセージ</td><td>{{ Input::get('message') }}{{ Form::hidden('message', Input::get('message')) }}</td>
</tr>
<tr>
<td colspan="2">{{ Form::submit('内容を登録') }}</td>
</tr>
</table>
{{ Form::close() }}
完了画面
$ vi app/views/complete.blade.php
complete!

2015年12月28日月曜日

Laravel4系のパッケージ作成における設定ファイルの読み込み

Laravelでcomoserで管理する自作ライブラリを作成していたのですが、どうしても設定ファイルが読み込めないという事態があった。

下記の様な形で設定ファイルを置いてみたのですが、いくらやってもNULL。
app/config/packages/vendor_name/package_name/config.php
app/config/packages/vendor_name/package_name/test.php

色々調べて見ると、原因はパッケージ側に設定ファイルがない事が原因でした。
vendor/vendor_name/package_name/src/vendor_namespace/config/config.php
vendor/vendor_name/package_name/src/vendor_namespace/config/test.php

どうやらパッケージ側の設定ファイルの値を、アプリ側の設定ファイルの値で上書きするような挙動になっているようです。 使用する場合は、
Config::get('package_name::filename.key')


例えば、下記の様な設定ファイルがあるとする。
$ vi vendor/vendor_name/package_name/src/vendor_namespace/config/test.php
<?php
return array(

    'key' => 'testtest'

);
$ vi app/config/packages/vendor_name/package_name/test.php
<?php
return array(

    'key' => 'testtesttest'

);


このファイルの内容を取得する場合は、
Config::get('package_name::test.key'); → 結果:testtesttest
設定ファイル名がconfig.phpで、アプリ側に設定ファイルがなかった場合
$ vi vendor/vendor_name/package_name/src/vendor_namespace/config/config.php
<?php
return array(

    'key' => 'testtest'

);
app/config/packages/vendor_name/package_name/config.php ← このファイルはない
このファイルの内容を取得する場合は、
Config::get('package_name::key'); → 結果:testtest

Laravel4系のカスタムバリデートの作り方

基本的な作り方に関しては公式サイトに書いてあるのですが、日本語で残しておきたかったので書いておきます。

https://laravel.com/docs/4.2/validation#custom-validation-rules

カスタムバリデータクラスの設置場所は自由で、今回はcomposerで管理する用のライブラリを作成したので、下記のディレクトリに配置しました。
/vendor/vendor_name/package_name/src/vendor_namespace/Validator/CustomValidator.php
<?php namespace TEST\Validator;

class CustomValidator extends \Illuminate\Validation\Validator {

    public function validateTest($attribute, $value, $parameters)
    {
        // バリデート処理
        // OKの場合はtrueを返す
        
        // NGの場合はfalseを返す
    }

    public function replaceTest($message, $attribute, $rule, $parameters)
    {
        // バリデートNGの時に表示するメッセージを編集する
        return $message;
    }

}

元になるルールを作成
$ vi /vendor/vendor_name/package_name/src/vendor_namespace/config/rules.php
<?php
return array(

    // 入力チェック
    'input_rules' => array(
        'name'    => 'required|test',
        'email'   => 'required',
        'message' => 'max:2000',
    ),
);

元になるメッセージを作成
$ vi /vendor/vendor_name/package_name/src/vendor_namespace/config/messages.php
<?php
return array(

    'test' => 'エラーです。',

);

利用する側は下記の様に記述する。
$rules = Config::get('package_name::rules.input_rules');
$messages = Config::get('package_name::messages.test');
$validator = \Validator::make(Input::all(), $rules, $messages);
if ($validator->fails()) {
    var_dump($validator->messages());
}
さらに下記ファイルの最後に追記する。
vi app/start/global.php

/**
 * カスタムバリデータの登録
 */
Validator::resolver(function($translator, $data, $rules, $messages)
{
    return new Test\Validator\CustomValidator(
        $translator, $data, $rules, $messages);
});
以上で使える様になるはず。

2015年7月7日火曜日

久しぶりのOAuth1.0にハマった

ものすごく久々にOAuth周りを触っていたのですが、色々とハマってしまったのでメモを残す。
以前のメモはあまり役に立ちませんでした・・・。
OAuthのライブラリはGoogleのOAuth.php
http://oauth.googlecode.com/svn/code/php/OAuth.php

今回ハマった内容としてはものすごく単純でした。
既存のソースでCURLOPT_POSTFIELDSに対してリクエストパラメータの配列が配列の状態のまま渡っていたため、
BaseStringとかがすごい事になっていたのですが、そうなっている原因がすぐに分からず修正に時間がかかってしまった・・・。

例)HMAC_SHA1の形式でGETリクエストの場合

<?php
include_once 'oauth.php';

$consumerKey    = 'XXXXXXXXXXXX';
$consumerSecret = 'XXXXXXXXXXXX';
$accessToken    = null;
$method         = 'GET';
$url            = 'http://XXXX/XXX.php';
$request_params = array(
    'a' => 1,
    'b' => 2
);

$OAuthConsumer        = new OAuthConsumer($consumerKey, $consumerSecret);
$OAuthSignatureMethod = new OAuthSignatureMethod_HMAC_SHA1();

$OAuthRequest = OAuthRequest::from_consumer_and_token($OAuthConsumer, $accessToken, $method, $url, $request_params);
$OAuthRequest->sign_request($OAuthSignatureMethod , $OAuthConsumer, $accessToken);

list($buf, $OAuthAuthorization) = explode(':', $OAuthRequest->to_header(''), 2);

$OAuthAuthorizationHeader  = array(
    'Authorization:'.$OAuthAuthorization,
);

$ch   = curl_init();

curl_setopt($ch, CURLOPT_URL,            $url.'?'.http_build_query($request_params));
curl_setopt($ch, CURLOPT_HEADER,         true);
curl_setopt($ch, CURLOPT_HTTPGET,        true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_HTTPHEADER,     $OAuthAuthorizationHeader);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
curl_setopt($ch, CURLOPT_TIMEOUT,        30);

$res = curl_exec($ch);

例)HMAC_SHA1の形式でPOSTリクエストの場合

<?php
$consumerKey    = 'XXXXXXXXXXXX';
$consumerSecret = 'XXXXXXXXXXXX';
$accessToken    = null;
$method         = 'POST';
$url            = 'http://XXXX/XXX.php';
$request_params = array(
    'a' => 1,
    'b' => 2
);

$OAuthConsumer        = new OAuthConsumer($consumerKey, $consumerSecret);
$OAuthSignatureMethod = new OAuthSignatureMethod_HMAC_SHA1();

$OAuthRequest = OAuthRequest::from_consumer_and_token($OAuthConsumer, $accessToken, $method, $url, $request_params);
$OAuthRequest->sign_request($OAuthSignatureMethod , $OAuthConsumer, $accessToken);

list($buf, $OAuthAuthorization) = explode(':', $OAuthRequest->to_header(''), 2);

$OAuthAuthorizationHeader  = array(
    'Authorization:'.$OAuthAuthorization,
    'Content-Type: application/x-www-form-urlencoded',
);

$ch   = curl_init();

curl_setopt($ch, CURLOPT_URL,            $url);
curl_setopt($ch, CURLOPT_HEADER,         true);
curl_setopt($ch, CURLOPT_POST,           true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_HTTPHEADER,     $OAuthAuthorizationHeader);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
curl_setopt($ch, CURLOPT_TIMEOUT,        30);
curl_setopt($ch, CURLOPT_POSTFIELDS,     http_build_query($request_params));

$res = curl_exec($ch);

署名をチェック

<?php
include_once 'oauth.php';

$OAuthConsumerKey    = 'XXXXXXXXXXXX';
$OAuthConsumerSecret = 'XXXXXXXXXXXX';

$request              = OAuthRequest::from_request();
$OAuthSignatureMethod = new OAuthSignatureMethod_HMAC_SHA1();
$consumer             = new OAuthConsumer($OAuthConsumerKey, $OAuthConsumerSecret);

if ($OAuthSignatureMethod->check_signature($request, $consumer, null, $request->get_parameter('oauth_signature'))) {
        echo 'This signature is OK.';
} else {
        echo 'This signature is NG.';
}

2014年11月6日木曜日

Laravel用のライブラリをインストール

Sentryというユーザ管理用のライブラリがあり、そのライブラリの出来がいいらしいので、
試しにインストールしてみる。

Sentryのインストール方法
追加するライブラリ名とバージョンを記載する
$ vi プロジェクト名/composer.json
/* 省略 */
"require": {
    "laravel/framework": "4.1.*", //カンマを忘れずに
    "cartalyst/sentry": "2.1.*" //この行を追加
},
/* 省略 */

編集が終わったらインストール

$ composer update
Loading composer repositories with package information
Updating dependencies (including require-dev)
  - Installing cartalyst/sentry (v2.1.4)
    Downloading: 100%

cartalyst/sentry suggests installing happydemon/txt (Required Text helpers when using the Kohana implementation)
Writing lock file
Generating autoload files
{"error":{"type":"ErrorException","message":"date_default_timezone_set(): Timezone ID 'JST' is invalid","file":"\/home\/kusagaya\/sites\/n.jp\/test\/vendor\/laravel\/framework\/src\/Illuminate\/Foundation\/start.php","line":167}}{"error":{"type":"ErrorException","message":"date_default_timezone_set(): Timezone ID 'JST' is invalid","file":"\/home\/kusagaya\/sites\/n.jp\/test\/vendor\/laravel\/framework\/src\/Illuminate\/Foundation\/start.php","line":167}}

タイムゾーンをJSTに変えていたらエラーが出たので、UTCに戻した。
もっかいアップデート

$ composer update
Loading composer repositories with package information
Updating dependencies (including require-dev)
Nothing to install or update
Generating autoload files
Generating optimized class loader
Compiling common classes
Compiling views

出来たっぽい。
とりあえずインストール自体は完了。
使い始めたらまた記事にしようと思います。

Laravelのインストール

比較的あたらしめのフレームワークであるLaravelを開発環境にインストールしてみた。

システム要件としては以下の通り。
PHP5.4.0以上
Mcrypt
PDO

さっそくインストールしてみる。
まずはcomposerのインストールから。

インストールコマンドは以下の通り。
$ curl -sS https://getcomposer.org/installer | php
#!/usr/bin/env php
All settings correct for using Composer
Downloading...

Composer successfully installed to: /Users/phpuser/tmp/composer.phar
Use it: php composer.phar

あとはパスが通っているディレクトリへ移動させる。
$ sudo mv composer.phar /usr/local/bin/composer

これでcomposerのインストールは完了。

続いてLaravelのインストール、というかプロジェクトの初期化をする。

$ composer create-project laravel-ja/laravel プロジェクト名 --prefer-dist
Installing laravel-ja/laravel (v4.2.8)
  - Installing laravel-ja/laravel (v4.2.8)
    Downloading: 100%

Created project in test
Loading composer repositories with package information
Installing dependencies (including require-dev)
  - Installing symfony/translation (v2.5.5)
    Downloading: 100%

  - Installing symfony/security-core (v2.5.5)
    Downloading: 100%

  - Installing symfony/routing (v2.5.5)
    Downloading: 100%

  - Installing symfony/process (v2.5.5)
    Downloading: 100%

  - Installing psr/log (1.0.0)
    Downloading: 100%

  - Installing symfony/debug (v2.5.5)
    Downloading: 100%

  - Installing symfony/http-foundation (v2.5.5)
    Downloading: 100%

  - Installing symfony/event-dispatcher (v2.5.5)
    Downloading: 100%

  - Installing symfony/http-kernel (v2.5.5)
    Downloading: 100%

  - Installing symfony/finder (v2.5.5)
    Downloading: 100%

  - Installing symfony/dom-crawler (v2.5.5)
    Downloading: 100%

  - Installing symfony/css-selector (v2.5.5)
    Downloading: 100%

  - Installing symfony/console (v2.5.5)
    Downloading: 100%

  - Installing symfony/browser-kit (v2.5.5)
    Downloading: 100%

  - Installing swiftmailer/swiftmailer (v5.3.0)
    Downloading: 100%

  - Installing stack/builder (v1.0.2)
    Downloading: 100%

  - Installing predis/predis (v0.8.7)
    Downloading: 100%

  - Installing phpseclib/phpseclib (0.3.8)
    Downloading: 100%

  - Installing patchwork/utf8 (v1.1.25)
    Downloading: 100%

  - Installing nesbot/carbon (1.13.0)
    Downloading: 100%

  - Installing monolog/monolog (1.11.0)
    Downloading: 100%

  - Installing nikic/php-parser (v0.9.5)
    Downloading: 100%

  - Installing jeremeamia/superclosure (1.0.1)
    Downloading: connection...
Could not fetch https://api.github.com/repos/jeremeamia/super_closure/zipball/d05400085f7d4ae6f20ba30d36550836c0d061e8, enter your GitHub credentials to go over the API rate limit
The credentials will be swapped for an OAuth token stored in /home/username/.composer/auth.json, your password will not be stored
To revoke access to this token you can visit https://github.com/settings/applications
Username: git_username
Password:
Token successfully created
Failed to download jeremeamia/SuperClosure from dist: The "https://api.github.com/authorizations" file could not be downloaded (HTTP/1.1 404 Not Found)
Now trying to download from source
  - Installing jeremeamia/superclosure (1.0.1)
    Cloning d05400085f7d4ae6f20ba30d36550836c0d061e8

  - Installing filp/whoops (1.1.2)
    Downloading: 100%

  - Installing ircmaxell/password-compat (1.0.3)
    Downloading: 100%

  - Installing d11wtq/boris (v1.0.8)
    Downloading: 100%

  - Installing symfony/filesystem (v2.5.5)
    Downloading: 100%

  - Installing classpreloader/classpreloader (1.0.2)
    Downloading: 100%

  - Installing laravel/framework (v4.2.11)
    Downloading: 100%

symfony/translation suggests installing symfony/config ()
symfony/translation suggests installing symfony/yaml ()
symfony/security-core suggests installing symfony/validator (For using the user password constraint)
symfony/security-core suggests installing symfony/expression-language (For using the expression voter)
symfony/routing suggests installing symfony/config (For using the all-in-one router or any loader)
symfony/routing suggests installing symfony/yaml (For using the YAML loader)
symfony/routing suggests installing symfony/expression-language (For using expression matching)
symfony/routing suggests installing doctrine/annotations (For using the annotation loader)
symfony/event-dispatcher suggests installing symfony/dependency-injection ()
symfony/http-kernel suggests installing symfony/class-loader ()
symfony/http-kernel suggests installing symfony/config ()
symfony/http-kernel suggests installing symfony/dependency-injection ()
predis/predis suggests installing ext-phpiredis (Allows faster serialization and deserialization of the Redis protocol)
phpseclib/phpseclib suggests installing ext-mcrypt (Install the Mcrypt extension in order to speed up a wide variety of cryptographic operations.)
phpseclib/phpseclib suggests installing pear-pear/PHP_Compat (Install PHP_Compat to get phpseclib working on PHP < 4.3.3.)
patchwork/utf8 suggests installing ext-intl (Use Intl for best performance)
patchwork/utf8 suggests installing ext-mbstring (Use Mbstring for best performance)
monolog/monolog suggests installing graylog2/gelf-php (Allow sending log messages to a GrayLog2 server)
monolog/monolog suggests installing raven/raven (Allow sending log messages to a Sentry server)
monolog/monolog suggests installing doctrine/couchdb (Allow sending log messages to a CouchDB server)
monolog/monolog suggests installing ruflin/elastica (Allow sending log messages to an Elastic Search server)
monolog/monolog suggests installing videlalvaro/php-amqplib (Allow sending log messages to an AMQP server using php-amqplib)
monolog/monolog suggests installing ext-amqp (Allow sending log messages to an AMQP server (1.0+ required))
monolog/monolog suggests installing ext-mongo (Allow sending log messages to a MongoDB server)
monolog/monolog suggests installing aws/aws-sdk-php (Allow sending log messages to AWS services like DynamoDB)
monolog/monolog suggests installing rollbar/rollbar (Allow sending log messages to Rollbar)
d11wtq/boris suggests installing ext-posix (*)
laravel/framework suggests installing doctrine/dbal (Allow renaming columns and dropping SQLite columns.)
Writing lock file
Generating autoload files
Mcrypt PHP extension required.
Script php artisan clear-compiled handling the post-install-cmd event returned with an error



  [RuntimeException]
  Error Output:



create-project [-s|--stability="..."] [--prefer-source] [--prefer-dist] [--repository-url="..."] [--dev] [--no-dev] [--no-plugins] [--no-custom-installers] [--no-scripts] [--no-progress] [--keep-vcs] [--no-install] [package] [directory] [version]

mcryptがないのでエラーになった。
mcryptをインストール。
$ sudo yum install php-mcrypt
もう一回試す。
$ composer create-project laravel-ja/laravel test --prefer-dist
Installing laravel-ja/laravel (v4.2.8)
  - Installing laravel-ja/laravel (v4.2.8)
    Loading from cache

Created project in test2
Loading composer repositories with package information
Installing dependencies (including require-dev)
  - Installing symfony/translation (v2.5.5)
    Loading from cache

  - Installing symfony/security-core (v2.5.5)
    Loading from cache

  - Installing symfony/routing (v2.5.5)
    Loading from cache

  - Installing symfony/process (v2.5.5)
    Loading from cache

  - Installing psr/log (1.0.0)
    Loading from cache

  - Installing symfony/debug (v2.5.5)
    Loading from cache

  - Installing symfony/http-foundation (v2.5.5)
    Loading from cache

  - Installing symfony/event-dispatcher (v2.5.5)
    Loading from cache

  - Installing symfony/http-kernel (v2.5.5)
    Loading from cache

  - Installing symfony/finder (v2.5.5)
    Loading from cache

  - Installing symfony/dom-crawler (v2.5.5)
    Loading from cache

  - Installing symfony/css-selector (v2.5.5)
    Loading from cache

  - Installing symfony/console (v2.5.5)
    Loading from cache

  - Installing symfony/browser-kit (v2.5.5)
    Loading from cache

  - Installing swiftmailer/swiftmailer (v5.3.0)
    Loading from cache

  - Installing stack/builder (v1.0.2)
    Loading from cache

  - Installing predis/predis (v0.8.7)
    Loading from cache

  - Installing phpseclib/phpseclib (0.3.8)
    Loading from cache

  - Installing patchwork/utf8 (v1.1.25)
    Loading from cache

  - Installing nesbot/carbon (1.13.0)
    Loading from cache

  - Installing monolog/monolog (1.11.0)
    Loading from cache

  - Installing nikic/php-parser (v0.9.5)
    Loading from cache

  - Installing jeremeamia/superclosure (1.0.1)
    Downloading: 100%

  - Installing filp/whoops (1.1.2)
    Loading from cache

  - Installing ircmaxell/password-compat (1.0.3)
    Loading from cache

  - Installing d11wtq/boris (v1.0.8)
    Loading from cache

  - Installing symfony/filesystem (v2.5.5)
    Loading from cache

  - Installing classpreloader/classpreloader (1.0.2)
    Loading from cache

  - Installing laravel/framework (v4.2.11)
    Loading from cache

symfony/translation suggests installing symfony/config ()
symfony/translation suggests installing symfony/yaml ()
symfony/security-core suggests installing symfony/validator (For using the user password constraint)
symfony/security-core suggests installing symfony/expression-language (For using the expression voter)
symfony/routing suggests installing symfony/config (For using the all-in-one router or any loader)
symfony/routing suggests installing symfony/yaml (For using the YAML loader)
symfony/routing suggests installing symfony/expression-language (For using expression matching)
symfony/routing suggests installing doctrine/annotations (For using the annotation loader)
symfony/event-dispatcher suggests installing symfony/dependency-injection ()
symfony/http-kernel suggests installing symfony/class-loader ()
symfony/http-kernel suggests installing symfony/config ()
symfony/http-kernel suggests installing symfony/dependency-injection ()
predis/predis suggests installing ext-phpiredis (Allows faster serialization and deserialization of the Redis protocol)
phpseclib/phpseclib suggests installing pear-pear/PHP_Compat (Install PHP_Compat to get phpseclib working on PHP < 4.3.3.)
patchwork/utf8 suggests installing ext-intl (Use Intl for best performance)
patchwork/utf8 suggests installing ext-mbstring (Use Mbstring for best performance)
monolog/monolog suggests installing graylog2/gelf-php (Allow sending log messages to a GrayLog2 server)
monolog/monolog suggests installing raven/raven (Allow sending log messages to a Sentry server)
monolog/monolog suggests installing doctrine/couchdb (Allow sending log messages to a CouchDB server)
monolog/monolog suggests installing ruflin/elastica (Allow sending log messages to an Elastic Search server)
monolog/monolog suggests installing videlalvaro/php-amqplib (Allow sending log messages to an AMQP server using php-amqplib)
monolog/monolog suggests installing ext-amqp (Allow sending log messages to an AMQP server (1.0+ required))
monolog/monolog suggests installing ext-mongo (Allow sending log messages to a MongoDB server)
monolog/monolog suggests installing aws/aws-sdk-php (Allow sending log messages to AWS services like DynamoDB)
monolog/monolog suggests installing rollbar/rollbar (Allow sending log messages to Rollbar)
d11wtq/boris suggests installing ext-posix (*)
laravel/framework suggests installing doctrine/dbal (Allow renaming columns and dropping SQLite columns.)
Writing lock file
Generating autoload files
PHP Fatal error:  Class 'PDO' not found in /home/username/sites/n.jp/test2/app/config/database.php on line 16
{"error":{"type":"Symfony\\Component\\Debug\\Exception\\FatalErrorException","message":"Class 'PDO' not found","file":"\/home\/username\/sites\/n.jp\/test2\/app\/config\/database.php","line":16}}Script php artisan clear-compiled handling the post-install-cmd event returned with an error



  [RuntimeException]
  Error Output: PHP Fatal error:  Class 'PDO' not found in /home/username/sites/n.jp/test2/app/config/database.php on line 16




create-project [-s|--stability="..."] [--prefer-source] [--prefer-dist] [--repository-url="..."] [--dev] [--no-dev] [--no-plugins] [--no-custom-installers] [--no-scripts] [--no-progress] [--keep-vcs] [--no-install] [package] [directory] [version]

今度はPDOがないって怒られた
PDOをインストール
$ sudo yum install php-pdo

PDOをインストール後にもう一回
$ composer create-project laravel-ja/laravel test --prefer-dist
Installing laravel-ja/laravel (v4.2.8)
  - Installing laravel-ja/laravel (v4.2.8)
    Loading from cache

Created project in test2
Loading composer repositories with package information
Installing dependencies (including require-dev)
  - Installing symfony/translation (v2.5.5)
    Loading from cache

  - Installing symfony/security-core (v2.5.5)
    Loading from cache

  - Installing symfony/routing (v2.5.5)
    Loading from cache

  - Installing symfony/process (v2.5.5)
    Loading from cache

  - Installing psr/log (1.0.0)
    Loading from cache

  - Installing symfony/debug (v2.5.5)
    Loading from cache

  - Installing symfony/http-foundation (v2.5.5)
    Loading from cache

  - Installing symfony/event-dispatcher (v2.5.5)
    Loading from cache

  - Installing symfony/http-kernel (v2.5.5)
    Loading from cache

  - Installing symfony/finder (v2.5.5)
    Loading from cache

  - Installing symfony/dom-crawler (v2.5.5)
    Loading from cache

  - Installing symfony/css-selector (v2.5.5)
    Loading from cache

  - Installing symfony/console (v2.5.5)
    Loading from cache

  - Installing symfony/browser-kit (v2.5.5)
    Loading from cache

  - Installing swiftmailer/swiftmailer (v5.3.0)
    Loading from cache

  - Installing stack/builder (v1.0.2)
    Loading from cache

  - Installing predis/predis (v0.8.7)
    Loading from cache

  - Installing phpseclib/phpseclib (0.3.8)
    Loading from cache

  - Installing patchwork/utf8 (v1.1.25)
    Loading from cache

  - Installing nesbot/carbon (1.13.0)
    Loading from cache

  - Installing monolog/monolog (1.11.0)
    Loading from cache

  - Installing nikic/php-parser (v0.9.5)
    Loading from cache

  - Installing jeremeamia/superclosure (1.0.1)
    Loading from cache

  - Installing filp/whoops (1.1.2)
    Loading from cache

  - Installing ircmaxell/password-compat (1.0.3)
    Loading from cache

  - Installing d11wtq/boris (v1.0.8)
    Loading from cache

  - Installing symfony/filesystem (v2.5.5)
    Loading from cache

  - Installing classpreloader/classpreloader (1.0.2)
    Loading from cache

  - Installing laravel/framework (v4.2.11)
    Loading from cache

symfony/translation suggests installing symfony/config ()
symfony/translation suggests installing symfony/yaml ()
symfony/security-core suggests installing symfony/validator (For using the user password constraint)
symfony/security-core suggests installing symfony/expression-language (For using the expression voter)
symfony/routing suggests installing symfony/config (For using the all-in-one router or any loader)
symfony/routing suggests installing symfony/yaml (For using the YAML loader)
symfony/routing suggests installing symfony/expression-language (For using expression matching)
symfony/routing suggests installing doctrine/annotations (For using the annotation loader)
symfony/event-dispatcher suggests installing symfony/dependency-injection ()
symfony/http-kernel suggests installing symfony/class-loader ()
symfony/http-kernel suggests installing symfony/config ()
symfony/http-kernel suggests installing symfony/dependency-injection ()
predis/predis suggests installing ext-phpiredis (Allows faster serialization and deserialization of the Redis protocol)
phpseclib/phpseclib suggests installing pear-pear/PHP_Compat (Install PHP_Compat to get phpseclib working on PHP < 4.3.3.)
patchwork/utf8 suggests installing ext-intl (Use Intl for best performance)
patchwork/utf8 suggests installing ext-mbstring (Use Mbstring for best performance)
monolog/monolog suggests installing graylog2/gelf-php (Allow sending log messages to a GrayLog2 server)
monolog/monolog suggests installing raven/raven (Allow sending log messages to a Sentry server)
monolog/monolog suggests installing doctrine/couchdb (Allow sending log messages to a CouchDB server)
monolog/monolog suggests installing ruflin/elastica (Allow sending log messages to an Elastic Search server)
monolog/monolog suggests installing videlalvaro/php-amqplib (Allow sending log messages to an AMQP server using php-amqplib)
monolog/monolog suggests installing ext-amqp (Allow sending log messages to an AMQP server (1.0+ required))
monolog/monolog suggests installing ext-mongo (Allow sending log messages to a MongoDB server)
monolog/monolog suggests installing aws/aws-sdk-php (Allow sending log messages to AWS services like DynamoDB)
monolog/monolog suggests installing rollbar/rollbar (Allow sending log messages to Rollbar)
d11wtq/boris suggests installing ext-posix (*)
laravel/framework suggests installing doctrine/dbal (Allow renaming columns and dropping SQLite columns.)
Writing lock file
Generating autoload files
Generating optimized class loader
Compiling common classes
Compiling views
Application key [XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX] set successfully.

出来た。

インストール完了後は
プロジェクトルート/app/storage
のパーミッションを777に変更する

$ chmod -R 777 app/storage

app/storageの権限を変えていない場合、「Error in exception handler.」と表示される。

これらの作業が完了後にサイトを確認してみると、
「You have arrived.」というページが表示されている

Laravelは2回目以降のインストールはキャッシュから読み込むようになっているので、
一回サーバ内でインストールしておけば、毎回プロジェクトを作るたびにダウンロードする必要はない。
キャッシュはプロジェクトとは関係ない場所に保存されているので、Laravelのプロジェクトを全部消したとしても、
インストールに影響はない。

一番最初に行うのは、アプリケーションとデータベースの設定。
app/config/app.phpに書かれているArtisan(アルチザン)はコマンドラインインターフェイス。
app/config/database.phpには、SQL別に設定が書けるようになっている。とりあえず自分の使うタイプであるMySQL部分の設定だけ変更。

2012年3月16日金曜日

mkdirは実は再帰的にディレクトリを作ることができたらしい

今まで知らなかったけど、実はmkdirはphp5から再帰的にディレクトリを作ることができるみたい。

なので早速やってみた。

使い方は以下の通り。
mkdir(ディレクトリのパス, パーミッション, 再帰処理フラグ);

mkdir('/path/to/dir', 0775, true);

こうすると「/path/to/dir」へのディレクトリが一気に作成される。
ちなみに、「/path/to」までのディレクトリが存在していても、エラーになるわけではないみたいです。

もうphp4で開発する機会も大分減ってきたし、がんがん使っていこうと思います。

2012年3月14日水曜日

jqueryを使って配列をPOST

一番簡単な方法は対象のフォームを要素をセレクタで選択してシリアライズしてPOST。
あとはサーバサイドでよしなにすればOK。

Jquery
var postData = $('form').serializeArray();

$.post(
    'index.php',
    postData,
    function(data){
        console.log(data);
    }
);

php
var_dump($_POST);

但し、通常のフォームでPOSTされてきたように送りたい場合はJS側で色々と手を入れないといけない。
ソースに関してはその部分が出来次第公開予定。

2012年3月13日火曜日

mysqlに接続できない

会社で用意してもらったテスト用のサーバでローカル上のmysqlを使おうと思ったら
以下のようなエラーで出て接続ができなかった。

$ mysql -uroot
Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock'

とりあえずググってみたら、mysqldが起動してない可能性があると。
で、psコマンドで調べてみた。

$ ps aux | grep mysqld

いない・・・。

じゃあmysqlを起動させようと思って、
$ /etc/init.d/mysqld start

へ移動しようと思ったらmysqldが存在してない!

mysqlコマンド自体は使えるから、インストールとかに失敗してるのかと思って、
何度かアンインストールしてから、再インストールしてみたりとかしたのですが、状況は変わらず・・・。

なぜかわからず、ググり続けてみたらありました。
mysql-serverがインストールされていないんじゃないかという・・・。

というか、よくよく見てみたら結構色々とインストールされていなかった・・・。
mysql-serverだけでなく、php-mysqlとかも入ってなかった。
勝手に使える状態だと思っていたのですが、確認は大事ですね。

2012年2月29日水曜日

SimpleXMLElementを配列に変換(簡易版)

SimpleXMLを配列に変換するのがどうにもこうにも面倒くさいなーと思っていたのでググってみた。

$array = json_decode(json_encode($SimpleXMLElement), true);

この1行のソースで対応可能らしい。
但し属性等がある場合には使えないので、そういう属性がつかないことが保障されている場合には使える。
1行でいけるのは結構便利だなー。

2012年1月10日火曜日

mysql_real_escape_stringはMySQLの接続を確立していないと使えない

CSVを取り込む際にmysql_real_escape_stringを使ってエスケープする処理を入れていたのだが、
処理の順番を変更した際に、なぜかWarningが出てくるようになってしまった。
何かと思って原因を調べてみたら、MySQLと接続していなかったのが原因だった。

確かに、よく考えてみたらmysqlとの接続を確立できてないのに、
phpからmysqlの関数呼ぶことなんてできないよなーと納得の回答。

mysql_real_escape_stringを使う前にmysql_connectするように修正したんですが、
いまいち綺麗に書けてる気がしない・・・。
もうちょっと考えてソースを書こう。

2011年12月27日火曜日

mysql_pconnectはmysql_closeで閉じても意味がない

マニュアルに書いてありました。
mysql_close() は、mysql_pconnect() により生成された持続的リンクを閉じません。

へー。

2011年10月7日金曜日

PEAR mailのsendに関して

CcやBccに送る場合にheaderにCcやBccのアドレスを含めるだけでは送れない。
なので全てsendの第一引数に配列か宛先をカンマ区切りで結合した形で入れないといけないのですが、
カンマ区切りで文字列結合した際に、Ccは問題なく送信できてるんですが、Bccがうまくいかないという現象があった。
カンマ区切りの文字列ではなく、配列で渡した場合は特に問題なく送れたので
とりあえずそっちで対応することにした。

送れないバージョン

$to      = 'hogeTo@mail.com, fugaTo@mail.com';
$cc      = 'hogeCc@mail.com, fugaCc@mail.com';
$bcc     = 'hogeBcc@mail.com, fugaBcc@mail.com';
$from    = 'from@mail.com';
$subject = 'test mail subject';
$body    = 'test mail body';

$mime    = new Mail_Mime("\r\n");
$body    = mb_convert_encoding($body, 'ISO-2022-JP');
$subject = mb_encode_mimeheader($subject, 'ISO-2022-JP', 'B', "\r\n");

$headers = array(
  'To'      => $to,
  'Cc'      => $cc,
  'Bcc'     => $bcc,
  'From'    => $from,
  'Subject' => $subject,
);

$to = implode(',', array($to, $cc, $bcc));

$mail = Mail::factory($mailSystem, $params);
$mail->send($to, $headers, $body);

送れるバージョン

$to  = array('hogeTo@mail.com', 'fugaTo@mail.com');
$cc  = array('hogeCc@mail.com', 'fugaCc@mail.com');
$bcc = array('hogeBcc@mail.com', 'fugaBcc@mail.com');

$from    = 'from@mail.com';
$subject = 'test mail subject';
$body    = 'test mail body';

$mime    = new Mail_Mime("\r\n");
$body    = mb_convert_encoding($body, 'ISO-2022-JP');
$subject = mb_encode_mimeheader($subject, 'ISO-2022-JP', 'B', "\r\n");

$headers = array(
  'To'      => implode(",", $to),
  'Cc'      => implode(",", $cc),
  'Bcc'     => implode(",", $bcc),
  'From'    => $from,
  'Subject' => $subject,
);

$to = array_merge($to, $cc, $bcc);

$mail = Mail::factory($mailSystem, $params);
$mail->send($to, $headers, $body);

2011年9月28日水曜日

DoctrineのSELECT文に関して

DoctrineでSELECT文を上書きする際にハマったのでメモ。

対象テーブルは2テーブル
・account(アカウント一覧)
・friends(友達一覧)
・check(チェックしたユーザー一覧)

以下のようなSQL文をDoctrineから生成しようとしたがうまくいかなかった。
SELECT *, account_id IN (1,2,3,4) AS is_friend FROM friends ORDER BY is_friend DESC;

やりたいことは以下の通り。
checkテーブルのデータを各種条件を指定して取得し、
そのデータを友達かどうかをキーにしてソートする。

最初に書いていたのはこんな感じ。

$query = Doctrine_Core::getTable('Check')
           ->select('*, account_id IN (1,2,3,4) as is_friend')
           ->where('delete_flg = ?', 0)
           ->limit(10)
           ->offset(0)
           ->orderBy('is_friend DESC');

SELECT内に書かれている「account_id IN (1,2,3,4)」の部分がうまく解釈されずに、
本来であればいる「1」かいないか「0」の2種類の結果がis_friendに格納されて返ってくるはずなのだが、
is_friendにはcheckテーブルに含まれるaccount_idが入って返ってきてしまっていた。

原因はどうやらparseSelectの中でやっている処理っぽい。
さすがにこの部分を書き換えるわけにはいかないので、
どうにかできないかと色々と試してはみたのだが、結局わからず終い。

どうしたものかと思っていたのだが、
だったら別のSQLの書き方で同じ結果を取得できるようにすればいいんじゃないかと思った。

JOINとかしてやる方法はパフォーマンス的に悪すぎるので却下。
なんとかSQL一発で取れる方法を探していたらありましたよ。

「CASE」

これ使えばいけるかもと思って、早速MySQLでコマンド叩いてみた。
SELECT *, CASE WHEN account_id IN (1,2,3,4) THEN 1 END AS is_friend FROM friends ORDER BY is_friend DESC;

いけた!
全く同じ結果ではないですが、is_friendに1かNULLが入る感じの結果が返ってきました。
CASEもう一個書けばNULLのとこを0返すようにできますが、必要ないと思ったのでやっていません。

で、今度はこれをちゃんとDoctrineが作ってくれるのかってとこです。

$query = Doctrine_Core::getTable('Check')
           ->select('*, CASE WHEN account_id IN (1,2,3,4) THEN 1 END AS is_friend')
           ->where('delete_flg = ?', 0)
           ->limit(10)
           ->offset(0)
           ->orderBy('is_friend DESC');

これを実行してみると、SQLのSyntaxエラーに…。
ググってみたら、意外とさっくり出てきた。
どうやらCASE~ENDの前後に括弧をつけるといけるらしい。
早速試してみた。

$query = Doctrine_Core::getTable('Check')
           ->select('*, (CASE WHEN account_id IN (1,2,3,4) THEN 1 END) AS is_friend')
           ->where('delete_flg = ?', 0)
           ->limit(10)
           ->offset(0)
           ->orderBy('is_friend DESC');

出来た!!!

エラーもなく、希望通りのレスポンスが返ってきました。
やはり複雑なSQLになるとDoctrineを使うのも一苦労ですね…。
Doctrineのキャッシュを使いたいがためにDoctrineで頑張ってやってましたけど、
それに見合うかどうかも判断する必要がありますね。
毎回こんな感じでハマってると時間がいくらあっても足りない…。

フレームワークでデバッグする際に使える関数

PHPでデバッグする際によく使う関数にvar_dumpやprint_r等がありますが、
symfony等のフレームワークを使用している場合、こういったダンプ関数を使ってブラウザ上に情報を出力しようとすると、出力する情報量が多すぎてブラウザがクラッシュしてしまうことが多々あります。
こういった時に使える便利な関数がいくつかあったので、メモしておくことにします。
※但しバックトレースに関してはsymfonyで試しにやってみましたが、駄目でした・・・。


debug_backtrace
バックトレースを生成する
debug_print_backtrace
バックトレースを生成する
get_included_files
include または require で読み込まれたファイルの名前を配列として返す
get_defined_constants
すべての定数の名前とその値を連想配列として返す
get_class
オブジェクトのクラス名を返す
get_class_method
クラスメソッドの名前を取得する
get_class_vars
クラスのデフォルトプロパティを取得する
get_object_vars
指定したオブジェクトのプロパティを取得する
get_parent_class
オブジェクトの親クラスの名前を取得する

2011年9月13日火曜日

memcacheのインストール

memcacheが標準っぽくなってきたので、自分のVMWare上にも入れてみた。
まずyumからインストールする
必要であればリポジトリを指定してやる。
yum install memcached

以下のファイルを作成し、中身を書く。
# vi /etc/php.d/memcached.ini

extension=memcache.so

で、apache再起動。

きちんと読み込まれているか確認
php -i | grep memcache

/etc/php.d/memcached.ini,
memcache
memcache support => enabled
memcache.allow_failover => 1 => 1
memcache.chunk_size => 8192 => 8192
memcache.default_port => 11211 => 11211
memcache.default_timeout_ms => 1000 => 1000
memcache.hash_function => crc32 => crc32
memcache.hash_strategy => standard => standard
memcache.max_failover_attempts => 20 => 20
Registered save handlers => files user memcache

で、起動させる。
memcached -d -m 128 -l 127.0.0.1 -p 11211

ちなみにオプションはこんな感じ
オプション 説明 デフォルト値
-l <ip_addr> memcachedがListenするIPアドレス。 セキュリティを考慮するときちんと指定したほうがよい。 INDRR_ANY
-d デーモンとして起動
-s <file> Unixソケットへのパス
-P <filename> PIDファイルの指定。デーモンとして動作した場合のみ有効。
-u <username> memcachedを起動するユーザーを指定。root権限で実行した場合のみ有効。
-p <num> ListenするTCPポート番号。 11211
-U <num> ListenするUDPポート番号。 0, Off
-m <num> キャッシュとして利用するメモリの最大容量。単位はMB。 64
-c <num> 最大同時接続数。 1024
-M メモリを使い果たしたとき勝手にキャッシュを削除しないでエラーを返す
-n <bytes> キャッシュ1件(key+value+flags)に割り当てる最小サイズ 単位はバイト。 48
-v errorとwarningを出力
-vv -vに加えてクライアントコマンドとレスポンスを出力
-i ライセンス表示
-h バージョンとヘルプの表示
-r コアファイルのサイズ制限を最大化する。
-k 巨大なキャッシュを扱うときにキケンなオプション。
-f <factor> チャンクサイズの増加係数 1.25
-b 管理されたインスタンスの起動

起動できているか確認してみる。
$ telnet localhost 11211
Trying 127.0.0.1...
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.
set foo 0 0 3     (保存コマンド)
bar               (データ)
STORED            (結果)
get foo           (取得コマンド)
VALUE foo 0 3     (データ)
bar               (データ)

とりあえずこんな感じです。

2011年9月12日月曜日

myPageStateとmemcacheの相性の悪さに関して

前の会社の人が作ったライブラリでmyPageState.class.phpを今回の案件で使用してみたのですが、
memcacheとの相性が悪いのか、symfony1.4との相性が悪いのかよくわかりませんが、セッションから値が取れないという不具合が発生しました。

前の会社でもsymfony(ver.1.2ですが)を使ってフォームを作る時には大体使っていたのですが、
こういったことは起こったことがありませんでした。

なので消去法でいくと一番怪しいのはmemcacheかなと。

最初はmemcacheにオブジェクトを突っ込んでた(memcacheが勝手にserializeする)んですが、
その場合には3割位の確率でmyPageState経由で保存したセッション情報の取得に失敗してしまうという現象が起きました。
仕方ないのでオブジェクトを配列にして保存するように修正してみた。
一応発生しにくくはなったものの、根本的な原因がわかってないので、
発生しないという状態までは持っていけませんでした。
しかし一体何が原因なんだろう…。
不思議です。