phpの最近のブログ記事

メッセージキューとは

異なる処理の間でキューを用いてメッセージ交換を行う仕組みのことです。
生産者(キューを登録する)側は単純にキューに対してメッセージを追加します。
消費者(キューを消費する)側は単純にキューからメッセージを取り出して処理を行います。

PerlだとTheShwartzやGearmanが有名。
身近なところではcodereposでTheShwartzが使われています

PHPでやるには

akkyさんがJavaのActiveMQを使う方法を紹介してくれてます。

秋元@サイボウズラボ・プログラマー・ブログ: PHPでメッセージキューを使う

pseudoQueue作った

もっと簡単にできないかなと思ってpseudoQueueというクラスを作ってみました。

pseudoQueue

pseudoQueueの特徴

  • 必要なのはsqliteのみ(ほとんどの環境で動く)
  • PHP4でもPHP5でも動く
  • デーモン不要
  • webアプリに組み込むだけですぐに使える

使い方

requireして、pseudoQueueを継承したクラスを定義する。

require_once('pseudoQueue.php');
class myPseudoQueue extends pseudoQueue
{
}

生産者側

sendメソッドを使うとキューにメッセージを登録できます。

$queue = new myPseudoQueue();
$queue->send('foo', 'bar');

消費者側

継承したクラスでsubscriberメソッドを上書きします。
キューにメッセージが登録されると順番にキューからメッセージを取り出して、subscriberが呼び出されます。

class myPseudoQueue extends pseudoQueue
{
    // overwrite subscriber method
    function subscriber($key, $message)
    {
        echo "key: {$key}, message: {$message}\n";
        flush(); @ob_flush();
    }
}

仕組み

インスタンスを生成したプロセスが終了した時点で、消費者プロセスを生成します。
なので、消費者側のプログラムを書く必要はなくて、subscriberメソッドを上書きするだけですみます。

堅牢性

消費者プロセスはsingleExecutionクラスを使っているので、複数プロセスが生成されることはありません。
また、仮にプロセスが落ちてもすぐに消費者プロセスは復活します。

消費者プロセスの生成

消費者プロセスの生成には pcntl_fork() を使っています。
なので、pcntl拡張が必要です。
pcntl拡張がない場合は、その場でキューを取り出して処理を実行しようとするので、pcntl拡張がなくてもそれなりに動作するはずです。

昨日作ったsingleExcutionですが、思いのほか好評なようでよかった。^o^

昨日作ったバージョンだと通常通りにスクリプトが終了すれば問題ないのですが、何らかの原因でプロセスが終了した場合にロックが残ってしまい、手動でロックを削除しないといけなかったのですが、これでは使いづらくてしょうがないので、ロックも自動で削除するように修正しました。

single_execution.php

シグナルハンドラを登録するようにした

次のようにしてシグナルハンドラを登録して、強制終了させられたタイミングでunlockするようにしてみました。
でもなぜか動かない...
なんでやねん!!

       if (singleExecution::loadExtension('pcntl')) {
            $this->key = $key;
            pcntl_signal(SIGTERM, array($this, 'unlockExit'));
            pcntl_signal(SIGHUP,  array($this, 'unlockExit'));
        }

ロックしたプロセスが存在するか調べるようにした

ロックディレクトリにプロセスIDを保存するようにしました。
このプロセスIDを使ってプロセスが存在するどうか調べて、存在しなければunlockするようにしました。

プロセスが存在するかどうか調べる

プロセスIDからプロセスが存在するかどうかは、シグナル番号0を送信してみれば分かります
phpの場合はposix拡張がないとシグナルが送れないので、その場合はpsコマンドを使って調べます。
このやり方だとpsコマンドの出力結果が変わると動かなくなるので、あまりよくないんですが...

    function processExists($pid)
    {
        if (!$this->loadExtension('posix')) {
            return posix_kill($pid, 0);
        } else {
            return intval(exec("ps -e|grep '^ *{$pid} '"));
        }
    }

ロックが不正な場合はunlock

これでロックが不正かどうか調べる準備が整ったので、不正な場合はunlockするようにしました。
不正なロックのunlock処理は不整合が起きないようにクリティカルセクションに入れました。

    function unlockIfInvalid($key)
    {
        $common_key = 'common';
    
        // spin lock
        $common_lock = $this->lockDirectory($common_key);
        while (!$this->mkdir($common_lock)) {
            usleep(10000);
        }
    
        // critical section
        if (!$this->isInvalidLock($this->lockDirectory($key))) {
            $this->unlock($key);
        }
    
        // unlock
        $this->unlock($common_key);
    }

かなり堅牢になった

想定されるケースはつぶしたので、かなり堅牢になったはず。
何かうまく動かない場合がありましたら、ご連絡ください。

PHPの拡張とは

PHPの機能を拡張するためにCで書かれた動的ライブラリのこと。

拡張を有効にするには

拡張を有効にする方法はphp.iniに設定する方法とdl()から動的に呼び出す方法の2つ。
動的に拡張を追加するにはdl()を使います。

dl()を使う問題点

dl('foo'); のように拡張名だけ指定して有効にできると便利なのですが、ちゃんと拡張のファイル名を指定しなければいけません。
で、ファイル名がOSによって異なるので、呼び出し方が異なります。

UNIX系OSの場合、拡張子が "so" になる。

dl('foo.so');

Windowsの場合、拡張子が "dll" になって、さらにファイル名の先頭に "php_" という文字列がつく。

dl('php_foo.dll');

LLなんだから、こういう基本的なところは簡便にできるようにして欲しいな〜。
それとも動的に拡張を読み込むのは特殊ケースなのかしら?

PHP_SHLIB_SUFFIXを使う

最近のPHPではPHP_SHLIB_SUFFIXという定数に拡張の拡張子が定義されているので、それを使えばOK。

$prefix = PHP_SHLIB_SUFFIX === 'dll' ? 'php_' : '';
dl($prefix.'foo.'.PHP_SHLIB_SUFFIX);

PHP_SHLIB_SUFFIXが定義されてない場合

古いPHPではPHP_SHLIB_SUFFIXが定義されてない。
そういう場合はPHP_OSを見て、Windowsかどうかで無理矢理処理する。

$suffix = strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' ? 'dll' : 'so';
$prefix = $suffix === 'dll' ? 'php_' : '';
dl($prefix.'foo.'.$suffix);

Mac OS Xで動かない><

これで完璧だと思ったんだけど、Mac OS Xで動かない><
PHP_SHLIB_SUFFIX が "dylib" なのに、拡張が ".so" でインストールのが原因。
どっちやねん(怒

でも、将来的に ".so" から ".dylib" に変更されるかもしれない可能性を考えると、あまりアドホックにやりたくない。
というわけでいくつか候補となるファイル名をいくつか試してdl()を呼び出すことにして回避。

loadExtension()

というわけで、最終的にできた完成系がこれ。

function loadExtension($extension)
{
    if (extension_loaded($extension)) {
        return true;
    }
    
    if (defined('PHP_SHLIB_SUFFIX')) {
        $suffix = PHP_SHLIB_SUFFIX;
    } else if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
        $suffix = 'dll';
    } else {
        $suffix = 'so';
    }
    
    $prefix = $suffix === 'dll' ? 'php_' : '';
    $libraries = array("{$prefix}{$extension}.{$suffix}");
    $libraries[] = "{$extension}.so";
    $libraries[] = "php_{$extension}.dll";
    $libraries = array_unique($libraries);
    
    foreach ($libraries as $library) {
        if (@dl($library)) {
            return true;
        }
    }
    
    return false;
}

LinuxでもWindowsでもMac OS Xでもうまくいくはず。
できるだけ汎用的に書いているので、多少環境が変わっても(Mac OS Xで拡張子がdylibに変更とか)大丈夫なようになってるはず。

どこでもsymfonyコマンド実行できるようにするのって流行ってるみたい

ぷぎがぽぎ
symfonyコマンドwrapper
symfonyコマンドラッパのあれこれ
symfonyコマンドがプロジェクトトップディレクトリでしか使えない件
symfonyコマンドをどこでも使えるようにする - ゆどうふろぐ

みんなわざわざ外部コマンド作ってるんだな〜。
コマンド呼ぶだけのシンプルな機能だったら、aliasかshell functionでrcファイルにサクっと書いた方が便利な希ガス。

ゆどうふさんのはaliasだけど、プロジェクトが増えるたびに追加しないといけないのが面倒だよね。

symfony shell function

これを .zshrc に貼付ける。
"command" を使ってるので、たぶん zsh でしか動きません。

if [ ! -z `whence symfony` ]; then
  symfony(){(
    if [ -f symfony ]; then
      ./symfony $*
    elif [ $PWD = / ]; then
      command symfony $*
    else
      cd ..; symfony $*
    fi
  )}
fi

ゆどうふさんのaliasを自動追加してみる

こんな感じでいけるんじゃないのかな(未検証)。
"$()" を使ってるので、これも zsh じゃないとダメかも。

for i in `locate "$HOME/*/symfony"`;do
  alias $(basename $(dirname $i))symfony=$i
done

cronにジョブを登録して、バックグランドで定型処理を実行することをよくやるかと思います。
その時に必ず一つのプロセスだけが実行されることを保証したい時があります。
こういう時にみなさんはどのようにやっているでしょうか。

案1: cronに時間間隔をある程度あけて実行する

毎分実行するとかじゃなくて、5分くらい間隔を開けて実行するようにする。
5分以内に処理が終われば、複数プロセスで実行されることはありません。
cronの設定をちょこっと変えるだけで簡単にできます。
でも、こういうことするとメンテナンスが面倒だし、いつの間にか複数プロセスが立ち上がってたりするんですよね ToT

案2: ロック処理をいれる

ロック処理を入れて、ちゃんと排他処理してやれば大丈夫です。
ただ個人的な感覚でしかないんですが、排他処理をまともに書ける人って想像以上に少ない気がする。
基本的なプログラミングではあるとは思うのですが。
あとスクリプトが増えてくると統一されてないいろんなやり方で実装されてたりして、だんだんメンテナンスが大変になってくる。

案3: daemontoolsのsetlockを使う

これはかなりおすすめ。
setlockを使うと簡単に実現できます。
個人的にはdjbのソフトウェアはライセンスがアレゲなので、ほとんど使いませんが。
daemontoolsはバイナリ提供できないので、インストールがちょっと面倒かもしれません。

singleExecution作った

もっと簡単に実現できたらいいなと思ってsingleExecutionなるクラスを作ってみました。
codereposに登録したので、よかったらどうぞ。

single_execution.php

使い方

プログラムの先頭で、require して new するだけです。
同じプロセスが実行されてない場合は、続きの処理を実行しますが、既に他のプロセスで実行されてた場合は直ちに終了します。

require_once('single_execution.php');
new singleExecution();

例えば

時間のかかる処理を常に実行しておきたい。

プログラムの先頭で singleExecution を呼び出す。

require_once('single_execution.php');
new singleExecution();

cron には毎分実行するように登録。

* * * * * php /path/to/command/foo.php

どんなに時間のかかる処理だろうが、実行されるプロセスは1つだけ。

[Think IT] 第1回:スクリプトはどうやって見つける? (1/3)

ThinkITに記事を書きました。
僕が書いたのは第一回だけで、残りはウノウの別のエンジニアが担当します。

5月の特集で毎週木曜更新予定です。
よろしければご覧ください!!

第32回PHP勉強会に参加してきました。

LT枠でこの前作ったARGFを紹介させてもらいました。
codereposにあげてあるので、よろしかったらどうぞ!!

ARGF

発表に使った資料を公開しておきます。

Read this doc on Scribd: argf

ちなみにデモで使ったphshもcodereposで絶賛公開中でございます。
こちらも、よろしかったらどうぞ!!

phsh

rubyのライブラリとかで、ファイルの最後に次のような感じになってるものがある。

if __FILE__ == $0
  ...
end

こういう風に書いておくと、他のファイルからインクルードした場合はライブラリの機能だけ提供するんだけど、そのライブラリ自体を直接単体のスクリプトとして実行できるようにもなる。
でもこれはスクリプトとして使われることを意図してなくて、単なるサンプルプログラムとして提供されています。
これ結構便利なんですよね。
簡単なライブラリの使い方がすぐに分かるし、ちょっとお試しで使ってみるのも簡単にできる。
ドキュメント読むよりも、プログラム見た方が早いし。
でも、PHPでやってるのはあんまりみないな。

理由: コマンドラインを活用するPHPプログラマが少ない?

偏見かもしれないけど、コマンドラインを活用しているPHPプログラマが他言語と比較して相対的に少ない気がする。
コマンドラインが必要ないことをメリットにしているプロジェクトもあるし...。
Webアプリ用の言語なのでしょうがないのかな。
コマンドライン便利だけどね。

理由: PHPではargvを取得する簡便な方法がない

PHPではargvを取得する方法が3つもある。

  • $argv
  • $_SERVER['argv']
  • $GLOBALS['HTTP_SERVER_VARS']['argv']

もともとWebアプリ用の言語だからargvなんてのは後付けなんだろうけど、せめて統一して欲しかった...
PEARのConsole_Getoptでも、argvを取得する専用のメソッドが定義されている。
拙作のARGFでも専用のメソッド作りましたよ。

PHPでもやろうよ

こんな感じで簡単にできるよ。
argvの取得は$argvだけで。面倒だから。

if (isset($argv[0]) && __FILE__ === realpath($argv[0])) {
    /* sample code */
}

PHP4,5両対応のライブラリを書こうと思った時にどうしても気になるのがE_STRICTの設定。
PHP4でも動くようにプログラムを書くと、PHP5でE_STRICTを有効にした場合に警告の嵐が orz。

プログラムを分ける

そもそもプログラムを分ければ問題なさそう。
ってこれは面倒くさいすぎるので却下。

一時的にE_STRICTを無効にする

一時的にE_STRICTを無効にすればいいんじゃないだろうか。
プログラムの最初でE_STRICTをoff。

// disable E_STRICT temporary
if (defined('E_STRICT')) {
    $_original_e_strict = ini_get('error_reporting') & E_STRICT;
    ini_set('error_reporting', ini_get('error_reporting') & ~E_STRICT);
}

プログラムの最後で元の設定に戻す。

// get E_STRICT setting back
if (defined('E_STRICT')) {
    ini_set('error_reporting', ini_get('error_reporting') | $_original_e_strict);
}

途中で他のファイルをincludeしなければこれでいけそう。

ARGFとは

rubyで使える機能で、入力を抽象化してくれます。
ARGFを使うとフィルタプログラムのようなコマンドラインプログラムが簡単に書けます。

ARGF

これ、とっても便利でrubyでプログラム書くときは(知らずに)よく使います。
ARGFを使うと入力が標準入力だろうが、ファイルが引数で複数渡されようが、プログラムを全く変更しないで動作させることができます。

% vim cat.rb    # cat と同じプログラム
ARGF.each {|line| print line}    # 本当はもっと簡単に書ける
% ruby cat.rb < cat.rb    # 標準入力から
% ruby cat.rb foo.txt bar.txt    # 引数に複数ファイル渡しても大丈夫

phpでは

コマンドラインのプログラムが書きづらい。 ><
もともとweb用の言語なので、しょうがないといえばしょうがないかもしれない。
とはいえ、phpのプロジェクトをやってるとコマンドラインのプログラムもphpで書くわけです。

php用ARGF作った

php用のARGFを作ってみました。
php4, php5両方で動きます。
codereposにコミットしました。

ARGF

サンプル

% vim cat.php    # catと同じプログラム
each()) {
    echo $line;
}
% php cat.php < cat.php    # 標準入力から
% php cat.php foo.txt bar.txt    # 引数に複数ファイル渡しても大丈夫

使い方

require_once('ARGF.php');

ファイルをインクルードすると$ARGC, $ARGV, $ARGFが定義されます。

$ARGC

コマンドラインの引数の数になります。
ARGF::ARGC(); でも取り出せます。

$ARGV

コマンドラインの引数の配列になります。
ARGF::ARGV(); でも取り出せます。

$ARGF

メインのオブジェクトです。
他の関数の中とかスコープが閉じたところで使いたい場合は、適当にインスタンスを取り出してあげてください。

$ARGF = new ARGF();
$ARGF = ARGF::getInstance();

$ARGF->each()

一行ずつ取り出します。

$ARGF->toArray()

全ての入力を一行ずつの配列に変換します。

$ARGF->toString()

全ての入力を一つの文字列に変換します。

$ARGF->line

現在読み込んでる仮想的な行番号を示します。

$ARGF->filename

現在処理しているファイル名を示します。

まとめ

ARGFを使うとコマンドラインのプログラム書くのが楽になるよ!!

実はちょっと前に飲み会から帰ってきて勢いで作ったんだけど、せっかくだからcodereposに公開。

昨日はHaskell Hackathonに参加してきました
あんまり挑戦者がいなさそうなPHPで挑んでみたのですが、蓋を空けてみると3人もいてびっくり

とりあえずやったことをつらつらと書いてく

Hugs 98のparse.yを持ってくる

Hugs 98にparse.yがあったので、とりあえずそれを持ってくる
C言語の部分をばっさり削除 parser.y

parser.phpyを作成

parse.yからphpのプログラムが埋め込まれたparser.phpyを作成
y2phpy.rbというスクリプトファイルで機械的に作った
これをkmyaccにかければ、parser.phpができる

字句解析器

字句解析は面倒なので、phpのtokenizerを使いました
phpのtokenizerを使うとphpのプログラムの字句解析を簡単に行う事ができます
もちろんこれはhaskell用ではないので、完全ではありませんが大体うまくいきました
うまく行かない部分は無理矢理処理してやるということで

サンプルプログラムを通してみる

字句解析と構文解析はできるようになったので、この時点でサンプルプログラムをいくつか通してみる
インデントのないプログラムはうまくいくんだけど、インデントのあるプログラムがパースエラーになってしまう ><
実はこれがオフサイドルールだということを知りました
構文解析と字句解析の間でなにやら処理をしないといけないみたいですね
今回はやらないってことで無視しました

いろいろクラスを追加

別にPHPパーサの処理系を書いてきたので、それを流用してクラスをいろいろ追加

簡単なプログラムを動かす

なんとかここまできて、プログラムの解析はできるようになってきたけど、Hugs 98のparser.yが複雑でどこから手をつけていいか、全然わかりません
そりゃちゃんとしたHaskellの処理系のyaccなんだから当たり前なんだけど
どうにかして、次のような簡単なプログラムの実行にこぎつけました

main = print (1 + 2 * 3 + 4 * 5 * 6)

演算子の優先順位が...

実際に実行してみた結果は「390」
あれっ?これ間違ってるじゃん、と思ってよくよく考えてみると演算子の優先順位が全然考慮されてない
確かにyaccのファイルを見ると演算子の優先順位って定義されてない
Haskellは演算子の優先順位を変更する事ができるので、構文定義では演算子の優先順位を定義してない模様
どうやって解決すればいいか、検討もつかないので放置

まとめ

Haskellも関数型言語もよく知らないのに、参加してみたけど、やっぱり周りのレベルが超たけー
いつもとは違う新鮮な感じでとても良い刺激になりました

開催してくれた、yukoba, amachang, nishio、ありがとうございます!!

全然できてないけど一応僕が作った奴は公開しておきます

php_haskell

今週末に開催されるPHPカンファレンス2007に登壇させていただくことになりました

PHPカンファレンス2007プログラム概要

ウノウのサービスを大規模などと言うのは大変おこがましいのですが、
PHPカンファレンスに参加される方はどちらかというとこれからサービスの開発を始めようと思っている方が多いと思うので、
小さなところから少しずつ大きく展開していく部分について話をしようかなと思っています。

よろしかったらご参加ください!!

ついカッとなってライトニングトークにも応募してしまいました 。
こちらもネタを仕込み中です。
くだらないネタなので、気が向いたらどうぞ。

JOBS - Open Positions
$q1 = Are you a master PHP Ninja?
$q2 = Excel at object oriented PHP, MVC pattern, Smarty template engine, caching practices and PEAR?
$q3 = Familiar with CSS, Javascript,  XHTML, MySQL?
$q4 = Experienced with Zend Studio, Subversion, wikis?
$q5 = Love open source? Aware of RSS, XML-RPC, web services, memcached and all other geeky stuff?
$q6 = Keen to learn much more?
$q7 = Ready to move to Silicon Valley?

if ( $q1 && $q2 && $q3 && $q4 && $q5 && $q6 ) {
    echo "YOU SHOULD <strong>JOIN</strong> US! drop your resume to contact@grou.ps <br />";
    echo "please include some php and javascript code snippets or refer us to an open source project you've";
    echo "already made. tell us our coding mistakes in this call and let us know what you know about";
    echo "the new javascript 1.7, mysql 5.2 and php 6. thanx,";
    exit;
}
else {
    die("maybe next time...");
}

ThinkITにてインターネットやWebの世界がまだよく分からない初心者向けのPHP開発入門講座「PHP開発はじめの一歩」の連載をさせていただくことになりました。

さきほど第1回が公開されました。

第1回:PHPの基礎とインストール

よろしければご覧ください!!

以前から PEAR のパッケージをローカルでミラーしたいなと思ってたんだけど、rsync とか ftp とかでローカルに再帰的に一発でミラーっていうのができないみたい。

ELFさんに聞いてみると 「pear list-all でごにょごにょやればいいんじゃない」ってことだったので簡単なスクリプトを作ってみた。使い捨てスクリプトのつもりで適当に作ろうかと思ってたんだけど、どうせなら公開しようと思ってちょっとまじめに作ってみました。

作ったのは pear_mirror.sh というシェルスクリプトです。名前は pear_mirror.sh ですが、PEAR だけでなく PECL のライブラリもダウンロードしてくれます。pear_mirror.sh を実行すると pear/pecl list-all の出力を元に $HOME/(pear|pecl) に PEAR/PECL のパッケージをダウンロードしてくれます。1回ダウンロードしたら2回目以降はダウンロードしません。新しいパッケージがあった場合だけダウンロードしてくれます。

pear_mirror.sh --help のように --help をつけて実行すると簡単なヘルプが表示されます。

% pear_mirror.sh --help
Usage: pear_mirror.sh
        [--delete]
        [--dest-dir=DIRECTORY]
        [--help]
        [--php-dir=PHP DIRECTORY]
        [--verbose]

--delete を指定すると存在しないパッケージ、違うバージョンのパッケージが削除されます。デフォルトでは削除されません。

--dest-dir はダウンロード先のディレクトリを指定します。デフォルトは $HOME です。ダウンロード先のディレクトリに pear/pecl というディレクトリを作って、それぞれ PEAR/PECL のパッケージがダウンロードされます。

--php-dir はインストールした php のディレクトリを指定します。普通は必要ないですが、複数のバージョンの PHP をインストールしてる場合とかに有効です。

--verbose を指定すると出力が詳細になります。と言っても今は --delete をつけた時に削除するファイル名を表示してくれるだけですが...

このスクリプトを cron に1日1回登録しておけば、オフラインの時でも常に最新の PEAR パッケージをインストールできるようになります。

ダウンロード後のサイズは100MBもいかない。これぐらいなら --delete をつけないでミラーして過去のバージョンを保存してもしばらくの間は問題なさそう。

pear_mirror.sh

最近 PHP の拡張である xdebug をインストールしてみた。
使ってみると、なかなか便利。プロファイリングとかとれて、どこが処理のボトルネックになってるかよく分かります。
なんかエラーメッセージとかも分かりやすくなってるし。これはイイ!!

xdebug をインストールしてみて気づいたのですが、xdebug は普通の extension ではなく、zend extension なんですね。
この extension と zend extension の違いはよく分かってないのですが、なぜか zend extension の場合は extension_dir の指定が効かない!!
zend extension をロードするときだけ、絶対パスで指定しないといけないのです。
(相対パスでもいいんだろうけど extension_dir のように基準ディレクトリを指定できない)

どうにもこうにも納得がいかないので、いろいろいじってみてパッチを作ってみました。
zend_extension_dir.patch

zend extension をロードする関数に渡ってきた文字列が絶対パスでない場合は、extension_dir を先頭に付加してやるという単純なパッチです。

cd php-x.y.z
patch -p0 < zend_extension_dir.patch
でパッチを当てることができます。

このパッチを当てて、php.ini に

zend_extension=xdebug.so

って書いたら、バッチリ extension_dir にある xdebug.so を読み込んでくれました。
やったね!!

PHP 4.3.11, 4.4.2, 5.1.2 で動作確認済みです。

これで本当に大丈夫なのかよく分からないんだけど。教えて > エロい人

zend extension と extension の違いなんですけど、ソースコード読む限りは zend extension は初期化時にだけロードされて、extension の方はソースコードからでも(もちろん初期化時も)ロードできるようになってるみたい。
zend extension と extension の違いってプログラム中でロードできるかどうかの違いだけなのだろうか?
教えて > エロい人

PHP のプログラムにはバージョンによって動いたり動かなかったりするものがあります。
特にバージョン4, 5の違いは大きいので、これを簡単に切り替える方法を試行錯誤しながらやってみたら結構うまくいきました。

まず前提として mod_php は使いません。さすがに Apache のモジュールで複数バージョンを共存させるのは無理なので、PHP は CGI で動作させます。これなら呼び出し先の PHP プログラムを変更するだけですむので、簡単にバージョンの切り替えができます。

複数バージョンのインストールについては前回のエントリtarballからインストールしたソフトウェアを簡単に管理するを参照してください。
インストールする時に注意するのは --with-apxs2 とかのオプションをつけて apache のモジュールをコンパイルしないことです。そうすると、勝手に CGI 用にコンパイルしてくれます。
あるいは --enable-cgi をつけるといいかも。(未確認)

インストールが終わったら PHP が CGI プログラムとして呼び出されるように Apache の設定を変更します。
Fedora の場合は

ScriptAlias /cgi-bin/ "/var/www/cgi-bin/"

と定義されていますので、/var/www/cgi-bin にファイルを追加してやるだけですみます。

sudo ln -s /opt/php/bin/php /var/www/cgi-bin/php
sudo ln -s /opt/pkgs/php-4.4.2/bin/php /var/www/cgi-bin/php-4.4.2
sudo ln -s /opt/pkgs/php-5.1.2/bin/php /var/www/cgi-bin/php-5.1.2

デフォルトの設定だと /var/www/cgi-bin でシンボリックリンクが許可されてなかったので、次の設定を apache に追加しました。

<Directory /var/www/cgi-bin>
Options FollowSymLinks
</Directory>

これで PHP を CGI プログラムとして呼び出す準備が整いました。

次に .php のファイルのアクセスに対して自動的に PHP が呼ばれるように設定します。
以下の内容で /etc/httpd/conf.d/php.conf を追加します。

Action php-script /cgi-bin/php
Action php-script-4.4.2 /cgi-bin/php-4.4.2
Action php-script-5.1.2 /cgi-bin/php-5.1.2
AddHandler php-script .php
DirectoryIndex index.php

これで .php へのアクセスに対してはデフォルトの PHP(/cgi-bin/php) が呼ばれるようになります。
php-script-4.4.2, php-script-5.1.2 は切り替え用なので、デフォルトでは使用されていません。

ようやく全ての準備が整いました。何もしなければ、デフォルトの PHP が使用されます。
違うバージョンの PHP を使用したい場合は、.htaccess に

AddHandler php-script-4.4.2 .php

と書くことで簡単に切り替えを行うことができます。
適当なところで phpinfo() の出力を見てみましょう。
ちゃんと切り替わっているはずです。

昨日のエントリYahoo でカスタマイズされた apache のまとめに引き続き、Yahoo でカスタマイズされている PHP についてのプレゼン資料をまとめてみました。

  • 2002年5月に採用
  • PHP 以前は yScript という独自のプロプリエタリな言語を使ってた
  • PHP が採択された理由
    • ハイパフォーマンス
    • 安定性
    • C/C++のようなコンパイラ言語で拡張できる
    • FreeBSD で動作する
    • i18n
    • 習得が容易
    • コミュニティの規模が大きい
    • HTML にコードが書ける
    • 充実したツール郡
  • オプションなしでコンパイル(./configure --disable-all)
  • 必要な拡張はライブラリを動的にロードする(メモリを節約)
  • 初期設定はセキュアに(register_globals=off など)
  • ユーザからの入力は全てサニタイズ
  • APCのようなシステムを入れてる
  • C/C++で書かれた独自の拡張

apache の時と比べてあまりこったことをしてない印象を受けますが、
独自拡張の部分でいろいろ手を入れているのでしょうね。
独自拡張にどのような機能があるのかは、プレゼン資料にはないので気になるところです。

第8回 PHP 勉強会に参加してきました。

今回はサイボウズのセミナールームでの勉強会。
詳しくはリンク先を参照してください、と逃げる。

僕はPHPの開発環境というお題で発表してきました。
最近導入した svk の調子がすこぶるよいので、その普及活動をしてきました。

勉強会後はいつもの通り宴会へと。
ちょっと食べ過ぎておなかいっぱいになりました。

PHPどうやって開発してる?


ということで(?)、僕の開発環境です。

・VMware PlayerのFedora
・Poderosa(sshターミナル)
・screen
・zsh
・jvim
・subversion
・svk

基本的に Linux マンセーなんだけど、Webアプリ開発者として
Windows IE での動作確認は必須なので、クライアントは Windows。
(Webアプリ開発者になる前はほとんど Linux しか使ってなかった)

ツール類は全て Linux 上のものを使用します。

screen、zsh は必須。必ず最初にこの2つのパッケージをインストールします。
これがないと生きていけない。

Poderosa は VMware Player 上の Fedora に接続するためだけに使用。
他のサーバに ssh する場合も全て Linux 上で。
複数ターミナルは screen で切り替えればよい。

ssh はほぼ例外なく鍵認証で接続。
ssh-agent を起動してるので、パスワードを入力するのは最初の1回のみ。

エディタは jvim。Emacs は使いません(使えません)。

コードは全部 subversion で管理。
最近導入した svk がかなり良い感じです。

リポジトリはサーバ上に置く。
svk でローカルにリポジトリをミラーして、ローカル上にコミット。
適当なタイミングで svk push してサーバ上のリポジトリにコミット。
サーバ上ではコミットのタイミングでフックして自動的に svn update

という風にローカルで作業した内容が自動的に更新されるようになってます。
ftp, scp, rsync のどれも使いません。

2chみたいな掲示板

この掲示板スクリプトは、ネット上で見つけたread.cgi、bbs.cgiをPerlからPHPに移植したものです。 改造、再配布、商用利用等に制限はありませんが、プログラムのバグ等でログが消えたり他の損害がおきても作者は一切の責任を負いません。

2chの掲示板のプログラムをphpに移植。
phpの方が得意でカスタマイズしたい人にはいいですね。

Five Things You Didn't Know You Could Do with PHP

結構知らないネタがありました。

switch文の使い方にはびっくりしましたが、
phpのswitchって逐次検索なんですか?

ウェブページ

Powered by Movable Type 4.21-ja

このアーカイブについて

このページには、過去に書かれたブログ記事のうちphpカテゴリに属しているものが含まれています。

前のカテゴリはOSSです。

次のカテゴリはpythonです。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。