PHP のブログ記事

ライセンス関連でごにゃごにゃもめとるCodeigniter(以下CIと略す)ですけど、
まぁ基本商用においては問題なさそうなので、
仕事では引き続き使っている案件もあります。

なんだかんだ、CIは高速で便利なPHPフレームワークです。
2.0以降CLIからの実行も考慮されていて
http://codeigniter.jp/user_guide_ja/general/cli.html
ローカルアプリケーションを作るのにおいても、結構活躍してくれます。

PHPでFileMakerへODBC接続して、データを取得してこなければならない仕事があるので、
CIでどうにかならんかなと思ってやってみた次第。

結論

$db['default']['hostname'] = 'Driver=FileMaker ODBC;Host=localhost;PRT=2399;database=データベース名;UID=ユーザーID;PWD=パスワード;';
$db['default']['username'] = '';
$db['default']['password'] = '';
$db['default']['database'] = '';
$db['default']['dbdriver'] = 'odbc';
$db['default']['dbprefix'] = '';
$db['default']['pconnect'] = TRUE;
$db['default']['db_debug'] = TRUE;
$db['default']['cache_on'] = FALSE;
$db['default']['cachedir'] = '';
$db['default']['char_set'] = 'utf8';
$db['default']['dbcollat'] = 'utf8_general_ci';
$db['default']['swap_pre'] = '';
$db['default']['autoinit'] = TRUE;
$db['default']['stricton'] = FALSE;

これで接続できます。
で、

上記のCLIからのCIの利用を参考に

php index.php controller_name method_name

これで起動できます。

FileMakerからはpetaexecuteプラグインなどを利用すれば標準出力を取得できるので、なかなか面白いアプリケーションがつくれそうです。

なんども私のブログで出てきましたが、FileMakerで利用できるSQLは、SQL標準に従ってない部分もあります。
したがってSQLを実行する際は、ActiveRecordは使わずにSQL直書きする方が無難でしょう。

smartyで

$obj->method()

を呼べるのかと思って

{$obj->method()}

と書いたら普通に実行できた。


 
たとえばMVCなフレームワークでSmartyを実行しているとき、
コントローラーに

public function test()
{
    return 'テスト';
}

というメソッドを作っておく。
で、Smartyにコントローラーオブジェクトを渡しておいて、
($controllerにその参照が渡っているものとする)

{$controller->test()}

と記述すればコントローラーのメソッド「test」が実行され、
「テスト」と出力される。

返り値がHTMLとして出力されるようだ。

引数も問題なく渡せた。


 
が引数には落とし穴があった。

{$obj->method( 'test', 'test2' )}

などとすると、エラーが起きる。
引数中にかっこがあるとパースエラーが起きるらしい。
可読性は低くなるが、

{$obj->method('test','test2')}

と記述する必要がある。
 
まぁ、そもそもViewにそんなもん書いていいもんかという話だが、
便利なので、覚書。

レコメンデーション

ECサイトなどでは、レコメンデーションを実装するのが、普通になってます。
個人的には、サイト回遊率が上がるけど、
コンバージョンのための終着ポイントを明示するのが少し難しくなるので、
商材などにより入れていい場合とそうでない場合があるなと思ってますが、
使いどころは多いので調べてみました。


よくあるのは、ASP提供型のサービスですね。
有名なのはこのへんですかね▼
http://recommend.submit.ne.jp/
 
あとこの会社の社長を知っているのですが、なかなか熱い方で、いいですよ!
http://www.otegaru-recommend.com/


とはいえ、このへんは月額数千円かかります。
売上100万円程度のお店に導入するには、5000円でも結構な額です。
(個人的には売上200万円程度いかないと、総合的には赤字路線だとは思いますが)
 
ということで、オープンソースで自社導入出来る仕組みがないかしらべたところ、
実用レベルで2つありました。

cicindela http://labs.edge.jp/cicindela/
vogoo http://sourceforge.net/projects/vogoo/
 
前者はライブドアが開発したレコメンドエンジンで、
汎用エンジンとしてはかなり強力なエンジンですが、
それだけ導入するには、ハードルは高くなります。

後者は、アプリケーション自体がPHPのライブラリとして動いてくれるものですが、
解析パターンがひとつしかありません。

導入障壁が低いほうがいいので、今回は、後者を利用しました。
「Vogoo」←なんて読むかわかりません(汗


残念なことに開発はすでに終了しているようで、メンテなども行われず、
インストールはMySQL4前提です。でも、普通にMySQL5でも動いています。

調べるとすでに使われている方がいますね。
http://blog.y-110.net/log/eid130.html
http://www.multiburst.net/sometime-php/2009/02/recommendation-engine-vogoo-with-php/
http://labs.unoh.net/2006/04/_vogoo.html
http://www.atyks.org/blog/2011-07-06-1.html
・・・古い記事は、2006年かぁ・・・


 
インストールはインストーラーを走らせて、
vogooのインストールディレクトリをinclude_pathに追加、
あとは、
http://www.atyks.org/blog/2011-07-06-1.html
http://blog.y-110.net/log/eid130.html
この2つの記事の内容を参考にすれば、実装できます。
(レコメンデーションの仕組みをだいたいわかって人ならという前提ですが)

いただけないことにグローバル変数を利用してるのがアレですが…。

あと、サイト内回遊をポイント評価することで、
似た回遊パターンや購入パターンの人を探して、
おすすめするという仕組みでうごいていますが、
デフォルトで0.66ポイント以上のものだけを結果として返すようになっています。
これは、実装の仕方によっては、
まったく結果が得られない可能性もあります。
ポイント評価の方法や、取得評点の閾値は
サイトにあわせて自分で考えたほうが良いと思います。


実は、協調フィルタリングパターンのレコメンデーションは、
5年前にPerlで作ってたのですが、繰り返しPCを変えてるうちに、
元データがどっかにいってしまいました。。。
当時バージョン管理システムを作らずに開発していたせいですね。。。はぁ。。。

4月2日にPHPカンファレンス関西に行ってきました。
大阪ですよ。大阪。
いつも梅田駅で迷う、小名古屋人の私にとっては、
どきどきの出発でしたが、それほど道のりは遠くなくて良かったです。


なぜ、PHPerではない(と言い張っているだけ)の私がわざわざ大阪に行ったかといいますと、
前の記事に書きましたUst番組『バニトーク』に出てやるぜ!っていうのと、
Twitterで仲良くさせていただいている、
@yat8823jpさん(以下、YATさん)、
@alphabet_hさん(以下、こしあんさん)
に会えるぜ!っていうのでした。


YATさんはECに携わってらっしゃるということで、
Twitterでもマインドを同じくしているので、
開場前に早めにお会いして、色々話をしましたが、
いいですね!適度な熱さと冷静さをもっている方でした。

そして、こしあんさんは、
まっすぐにPHPやらJSやらの言語の真ん中のところを追いかけている方だということは、
Twitterで存じ上げていましたが、
こしあんさんも、また適度な熱さと冷静さをもってらっしゃり、
僕好みの人間性を持っている方でした。

やっぱり会うって大きいと思います!
仲良くさせていただいている某団体(通販の運営者の団体)の代表の方が常々、
「TwitterやFacebookは、あくまでリアル世界のソーシャルの延長」
と仰っている意味が、ものすごく分かった気がします。

YATさん、こしあんさん、今後とも仲良くしましょう。


さて、肝心のPHPカンファレンスの中身は、
「クラウド」をテーマの中心においているせいか、
PHPカンファレンスなのに、
クラウド技術のお話だったり、JSのお話だったりと、
想像していたものと違って面食らってしまいましたが、
内容としては、良いノウハウの宝庫で、それはそれで良かったのかなと思っています。

また、ライトニングトークの枠もあったのですが、
さすが、こちらはdisられてもPHPerを名乗る方々のライトニングトークです。
PHPに対する思いがひしひしと伝わってきて、面白い内容ばかりでした。

ただ、CodeIgniter使いが少なくてしょんぼり…
やっぱ、CakeとSymfonyは強いですね…


本編終了後は、懇親会がありましたが、名古屋で行われる各イベントと異なり、
懇親会の中でも、ライトニングトークがあり、
本編の補足的な内容があったり、
有用なノウハウを伝えるものがあったり、
知らなかった会合やセミナーのお知らせがあったり、
と、ぶっちゃけ本編より充実してるんじゃないかと
錯覚させるような内容で、
お酒のちからもあるのでしょうが、
しゃべって終わりではなく、
みんながみんなどのような立場の人々なのかが分かるような懇親会でした。


名古屋からは@ahomuさんも行って、
はからずも名古屋のCMSの人がそれぞれ行くという布陣で行くことになりましたが、
強豪揃いのPHPerたちの前では、あえなく散ってしまう僕らでした。。。orz


というわけで、大阪人の熱さ、PHPerの熱さを感じることができた良い旅でした。
そして、名古屋、、、まだまだだなぁと感じた旅でした。
今後も何か大阪に関われたらなぁと思います。


あれ?集合写真の真ん中にアウエーの僕が…

先日CodeIgniterが2.0にバージョンアップしたり、
はてぶ界隈で
http://h2o-space.com/blog_ver2/diary/195
こちらの記事が話題となっているようですので、
CodeIgniterを使い始めて3ヶ月の私がちょっと反応してみます。
 


 私がCIを利用しはじめた理由は、

  • コーディング規約がゆるゆる
  • インストールにコマンドがいらない
  • 拡張がらくちん
  • 比較的広いPHPのバージョンに対応
  • モデルの扱いがゆるい

というところでした。
 
モデルのあたりは、前述の話題サイトにも書かれていますね。


実際に使い始めて特にいいなと思ったのが、
ユーザーガイドのページの使いやすさ・読みやすさです。
PHPさえちゃんとできれば、
ユーザーガイドのページを見ながら開発をラクに進められます。
書籍などはいらないと言ってよいです。
学習コストが異常なほど低いです。
 


そして、コントローラーの頭の良さに驚きを隠せないです。
一度ルーティングが成功してしまえば、
どこからでも、コントローラーインスタンスを呼び出すことができるので、
値をシステム内で縦断させて、参照/変更がかなり簡単にできます。
 
また、ローダーと呼ばれる、
ライブラリや設定ビューを呼び出す機能も、頭が良いです。
 
私はCodeIgniterを使う前、自作のフレームワークを利用していたのですが、
こちらのFWに使っていたライブラリ類の移植が、
コントローラーとローダーの頭の良さで、
非常に簡単に移植できました。
 
またCI自身のコアの拡張も分かりやすく、簡単にできます。
 


で、何が一番驚きって、とにかく高速。
ぶっちゃけ、普通のサイト作ったり、
モデルパターンの少ないWEBサービスだったら、
DBのクエリの速度以外は気にしないでいいレベルです。
 


常々私はEC専業のPGだと嘘ぶいていますが、
ECはページの魅せ方を色々変える必要があったり、
決済などの問題で、色んな拡張の必要性に迫られるケースが多々あるのですが、
そのへんでCIはパワーを発揮してくれます。
 
たとえばCakePHPは、良く言えば機能満載で、
痒いところに手が届いてサイト作りをラクにしてくれますが、
悪く言えばおせっかい機能満載で、
サイトの性質や、サイト作りの方針によっては足かせになります。
 
私のECサイト構築の立場では後者の立場だったのでCIはそのへんを解決してくれました。
 


 
とは言え、CIいいところばかりでは有りません。
 
前述の通りモデルやDB周りの実装がゆるゆるで、
ぶっちゃけモデルなんかナシでどうにかできちゃいますが、
ActiveRecordの機能は有しているものの、
Cakeよりは、ずっと「DBを操作している感」のある使い方になると思います。
また、DB操作が多いサイト作りの場合、
モデルファイルが大量になるかもしれません。
 
そして、CIを使ううえでPHPerが一番戸惑うのが、セッションでしょうね。
CIのセッションは、PHPのセッションは使わず独自実装です。
cookieに全てのデータを突っ込むcookieセッションか、
DBへデータを保存するDBセッションの2パターンが用意されていますが、
やっぱりファイルを使ったセッションもほしいところ。
ファイルセッションを利用する場合は、
自作でライブラリを作るか、別に配布されているライブラリを利用擦る必要があります。
(ただ、前述のとおりライブラリづくりは比較的簡単です。)
 
デフォルトで$_GETを破棄するという豪快な仕様になっているので、
そのへんを扱うのを最初戸惑うかもしれません。
(設定さえ覚えれば、すぐに慣れますが)
私の場合は、$_REQUESTを取り回す自作ライブラリで対応しています。
@kenji_sさんから

デフォルトで$_GETを破棄するのは CodeIgniter 1.x の仕様で 2.0.0 (Reactor) からはデフォルトで$_GETが使えるように変更されています

とご指摘いただきました。ありがとうございます。ちょっと不勉強でした。
 
あと、これは良いと言う人と悪いという人がいますが、
CIのviewは基本的に生PHPです。
私の場合は、生PHPは「うーん」な人なので、Smarty拡張しました。
(ただし、Smartyも「うーん」ですwwPHPTALにしようかなとか考えています)
 


というわけで、とにかくCIは頭がイイんだぜ!って話になっちゃいましたが、
よくないところを吸収するには、それなりのPHPの知識も必要というわけです。
以前書いたPHPのフレームワーク選択の記事でも書きましたが、
CIは、良くも悪くもちゃんとPHPが使えているということが前提条件であるFWでしょう。 

CodeIgniterが2011年1月28日付で2.0にバージョンアップされました。

ちょうど今日スタートの案件で、CodeIgniterを使おうと思って、
ダウンロードしにいったら2.0になっててびっくり。
 
社内で Perl→Template::Toolkit PHP→Smarty という刷り込みを行ってきたので、
オープンソースのFWを採用してもSmartyを使いたい。
ということで、こちらのような方策をとったのですが、
果たして、2.0ではどうでしょう?
 
とりあえず、前回の記事のように処理したら、
コンパイル済みテンプレートファイルの格納ディレクトリがないと警告。
 
CIの1.x系では、システムディレクトリの中にアプリケーションディレクトリがあるという構造でしたが、2.xからこれらが分離して、すこしディレクトリ構造が変わったのが原因でした。
 
前回の記事からのリンク先の説明にある、
config/smarty_parser.phpの

$config['compile_dir'] = BASEPATH.'cache/';

$config['compile_dir'] = APPPATH.'cache/';

にすれば動きました。
 
一安心。
 
CodeIgniteゆるゆるで素晴らしいよ!

今から作るアプリケーションでSQLiteを使わなくてはならなくなりました。
 
というわけで、CodeIgniterでSQLiteを使うにはどうしたらよいか。
 
system/application/config/database.phpの接続設定を

$db['default']['hostname'] = "";
$db['default']['username'] = "";
$db['default']['password'] = "";
$db['default']['database'] =  "データベースファイル";
$db['default']['dbdriver'] = "sqlite";
$db['default']['dbprefix'] = "";
$db['default']['pconnect'] = TRUE;
$db['default']['db_debug'] = TRUE;
$db['default']['cache_on'] = FALSE;
$db['default']['cachedir'] = "";
$db['default']['char_set'] = "utf8";
$db['default']['dbcollat'] = "utf8_general_ci";

ドライバに「sqlite」にして、
dbnameはデータベースファイルへのパスを指定します。
データベースファイルへのパスは相対パスも場合、
index.phpからの相対パスです。
APPPATHとかで、絶対パスを指定したほうがいいかもです。
 


この方法で使えるSQLiteは「SQLite2」ですのでご注意ください。
SQLite3を使いたい場合は、
http://codeigniter.com/wiki/PDO_SQLite3/
こちらを参考に。
配布アプリの場合は権限とか設定とかの問題があるので、
あまりやらないほうがいいかな。。。

CodeIgniterに同梱のSQLite用ドライバは、
バグがあるようです。
http://zidane27.blog119.fc2.com/blog-entry-11.html
このへんを参考に。これ以外情報がないので、バグにぶつかったら自分で直すしかなさそう…。

まぁいろいろアレだけど、SELECTとかINSERTとか基本的なとこは問題なさそう。

案件の事情により、CodeIgniterをCLIからもWEBからも利用しなければならないという問題にぶち当たる。

CodeIgniterでは、
system/libraries/Router.php
で実行するコントローラとメソッドを指定しているので、これを拡張してやれば、行ける気がする。

今回のコマンドは、

index.php "ディレクトリ" "クラス" "実行するメソッド"

と指定することにした。

まず、Router.phpの拡張を作る。
system/application/libraries/MY_Router.php
を作成し、そのなかに以下のようなクラスを作成する。

<?php
if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class EB_Router extends CI_Router{
 
//ここに後述のメソッドを書く。
 
}
?>

「//ここに後述のメソッドを書く。」の部分に後述のメソッドを書きます。


続いて、コマンドライン引数からコントローラーなどを指定するメソッドを拡張します。
system/libraries/Router.php
の「_validate_request」メソッドを、
system/application/libraries/MY_Router.php
へコピーします。

そして、このメソッドの冒頭へ、
CLIでの起動だった場合に、コマンドラインからコントローラーを指定するコードを書きます。
 

function _validate_request($segments){

/**********************************************************/
if( PHP_SAPI == 'cli' )
{

    //ここでコントローラーなどを指定。
    $this->set_directory($_SERVER['argv'][1]);
    $this->set_class($_SERVER['argv'][2]);
    $this->set_method($_SERVER['argv'][3]);

    //カレントディレクトリを変更しておく
    chdir( FCPATH );

    return array();
}
/**********************************************************/

// Does the requested controller exist in the root folder?
if (file_exists(APPPATH.'controllers/'.$segments[0].EXT))
{
    return $segments;
}

// Is the controller in a sub-folder?
if(is_dir(APPPATH.'controllers/'.$segments[0]))
{ 

    // Set the directory and remove it from the segment array
    $this->set_directory($segments[0]);
    $segments = array_slice($segments, 1);

    if (count($segments) > 0)
    {
        // Does the requested controller exist in the sub-folder?
        if ( ! file_exists(APPPATH.'controllers/'.$this->fetch_directory().$segments[0].EXT))
        {
            show_404($this->fetch_directory().$segments[0]);
        }
    }
    else
    {
        $this->set_class($this->default_controller);
        $this->set_method('index');

        // Does the default controller exist in the sub-folder?
        if ( ! file_exists(APPPATH.'controllers/'.$this->fetch_directory().$this->default_controller.EXT))
        {
            $this->directory = '';
            return array();
        }
    }
    return $segments;
}

// Can't find the requested controller...
show_404($segments[0]);
}

/**********************************************************/
で囲まれた部分が追加した部分です。

これで、コマンドからコントローラーやメソッドを指定できます。

定義済み定数の「PHP_SAPI」に「cli」が指定されたとき以外は、
通常のルーティングが行われるので、
CLIとWEBでハイブリッドな使い方ができそうです。


注意点としては、
上記のコードは、コマンドから渡された、
ディレクトリやらコントローラーやらメソッドが、
確実に存在するという前提で書いています。
実際に使う場合は、このへんのエラートラップもしておいたほうがいいでしょう。

拡張のヒントとしては、上記のコードでは、第4引数以降はと特になにもしていないですが、
ここも拾って、実行メソッドの引数として渡せると楽しいかもです。

何かの参考になればー。

こちらでSeezooCMS用のWiki記法ブロックを公開しているわけですが、
これを作った理由が、おそらくみなさんの役に立つのではということで、
Tipsを書いておきます。
 
SeezooにはWYSIWYGなHTMLブロックなど必要な機能はひと通り揃っていて、
企業の担当者レベルで非常に更新のしやすいシステムですが、
一個だけ不満が。
それがテーブル作成でした。
 
WYSIWYGなHTMLエディタには、テーブル機能が搭載されておらず、
HTMLコードを直で書かないといけない。
そして、一般担当者レベルでは、表組みはテーブルが一番分かりやすい。
 
ということで、何か簡単にテーブルを書ける方法がないかということで、
作ったのがwiki記法ブロックです。
 
wiki記法ブロックに

||セル1||セル2||
||セル3||セル4||

と書けば、

<table>
<tr><td>セル1</td><td>セル2</td></tr>
<tr><td>セル3</td><td>セル3</td></tr>
</table>

と自動で出力してくれる。
 

||~セル1||セル2||
||~セル3||セル4||

と書けば

<table>
<tr><th>セル1</th><td>セル2</td></tr>
<tr><th>セル3</th><td>セル3</td></tr>
</table>

と自動で出力してくれる。
 
という仕組みです。
 
どうですか?これならHTMLわからない人でもテーブルを簡単に書けます。
 


ただ、ここまでの説明だと、飾りっけのないテーブルになってしまいますので、
本日のバージョンアップで任意のidとclassを振れるようにしていますので、
例えば、テンプレート用CSSやカスタムCSSに

.wikiclass table {
 margin: 0px;
 padding: 0px;
 background-color: #FFFFFF;
    border-spacing:0px;
    border-collapse:collapse;
    empty-cells:show;
    width:100%;
    border-top:1px solid #663300;
    border-left:1px solid #663300;
    background-color: #FFFFFF;
}
.wikiclass table td {
    border-right:1px solid #663300;
    border-bottom:1px solid #663300;
    padding:3px;
    margin:0px;
}
.wikiclass table th {
    background-color: #F6F6F6;
    border-right:1px solid #663300;
    border-bottom:1px solid #663300;
    padding:3px;
    width:30%;
    margin:0px;
}

と書いておき、classに「wikiclass」を設定すれば、装飾のされたテーブルを表示できるようになります。
 
どうですか?地味だけど便利でしょ?
 
ご活用ください。

先日作ったSeezooCMS用のWikiコードブロックをバージョンアップしました。
 
・作成したブロックを<div>で囲み、そこに任意のidとclassを付与できるようにしています。
 これにより、CSSやカスタムCSSで、そのブロックに任意のCSSを適用できます。
 
・初版では、wikiブロックにHTMLコードを入れた場合、自動サニタイズされる仕様になっていましたが、
 それを回避しています。<script>などもそのまま出力されてしまいますので、
 セキュリティホールを作らないように注意が必要です。
 
以上が今回のバージョンアップです。
ダウンロードはこちらから