「PHP」カテゴリーアーカイブ

NginxのPHP(FastCGI)をphp-fpmではなくspawn-fcgiで動かす

php-spawn-fcgi

NginxでPHPだけを動かすならphp-fpmを使えば設定も楽なんだけどMuninやらPHP以外のスクリプトをFastCGIで動かしている場合、もうspawn-fcgiで全部やっちゃえばいいのではというわけです。

$ spawn-fcgi -h
Usage: spawn-fcgi [options] [-- <fgiapp> [fcgi app arguments]]

options:
-f <path>      /usr/bin/php-cgi でいいけど非推奨なので -- <fcgiapp> を使う。
-a <address>   127.0.0.1 (Unix domain socketを使わない場合)
-p <port>      9000 (Unix domain socketを使わない場合)
-s <path>      /var/run/spawn-fcgi/php-fcgi.sock (-a, -p を使わない場合)
-C <children>  (PHP only) PHP_FCGI_CHILDRENの値が入るけどデフォルトが0なので数値指定
-F <children>  -C 使ってたらいいか (デフォルト 1)
-u <user>      nginx
-g <group>     nginx (nginx:nginx で実行してる)
-U <user>      Unix domain socketのユーザ
-G <group>     Unix domain socketのグループ

CentOS 6なら /etc/init.d/php-spawn-fcgi みたいな分かりやすい起動スクリプトを作って
start)
daemon /usr/bin/spawn-fcgi -s /var/run/spawn-fcgi/php-fcgi.sock -U nginx -u nginx -g nginx -P /var/run/spawn-fcgi/php-fcgi.pid -C 5 -- /usr/bin/php-cgi

あとは自動起動など
chkconfig php-spawn-fcgi on
/etc/init.d/php-spawn-fcgi start

Nginx側のfastcgi_passは↑で指定したやつにする
fastcgi_pass unix:/var/run/spawn-fcgi/php-fcgi.sock
これで、PHPに関してはServer API : CGI/FastCGIとして動く。

と、ここまで書いたけどphpfpm_status のようなこともできないし、宗教上の理由でもないかぎりphp-fpmを使った方がよいのではという感想。

/etc/init.d/php-spawn-fcgi の中身

#!/bin/sh
#
# php-spawn-fcgi
#
# chkconfig: 2345 86 16
# description: php spawn-fcgi service process
# pidfile: /var/run/spawn-fcgi/php-fcgi.pid

# Source function library.
. /etc/rc.d/init.d/functions

RETVAL=0

spawn_fcgi=/usr/bin/spawn-fcgi
php_cgi=/usr/bin/php-cgi
prog=`basename ${php_cgi}`
pidfile=/var/run/spawn-fcgi/php-fcgi.pid
lockfile=/var/lock/subsys/php-fcgi
sockfile=/var/run/spawn-fcgi/php-fcgi.sock

user=nginx
group=nginx

# PHP_FCGI_CHILDREN
fcgi_children=5

# See how we were called.
case "$1" in
    start)
        echo $"Starting $prog: "
        dir=$(dirname ${pidfile})
        [ -d $dir ] || mkdir $dir
        chown ${user}:${group} $dir
        # spawn-fcgi
        daemon ${spawn_fcgi} -s ${sockfile} -U nginx -u ${user} -g ${group} -P ${pidfile} -C ${fcgi_children} -- ${php_cgi} spawn-fcgi
        RETVAL=$?
        if [ $RETVAL = 0 ]; then
            echo_success
            touch ${lockfile}
        else
            echo_failure
        fi
        echo
        ;;
    stop)
        echo -n $"Stopping $prog: "
        killproc -p ${pidfile} ${prog}
        RETVAL=$?
        echo
        [ $RETVAL = 0 ] && rm -f ${lockfile} ${pidfile}
        ;;
    status)
        status -p ${pidfile} ${prog}
        RETVAL=$?
        ;;
    restart|reload)
        $0 stop
        $0 start
        RETVAL=$?
        ;;
    *)
        echo $"Usage: $prog {start|stop|status|restart|reload}"
        RETVAL=2

esac
exit $RETVAL

PHPでInverse FizzBuzz(逆FizzBuzz)問題を問題を考えた

Fizz,Buzzとかのリストから最小で最短のFizzBuzzを出力できる連続数列を返すみたいなやつ。
強引に解いた感じでスマートじゃないけど関数型脳みそだから仕方ない。

中身

<?php
header('Content-Type: text/plain');
// Inverse FizzBuzz
inverseFizzBuzz(array('fizz')); // 3
inverseFizzBuzz(array('buzz')); // 5
inverseFizzBuzz(array('fizz', 'buzz')); // 9,10
inverseFizzBuzz(array('buzz', 'fizz')); // 5,6
inverseFizzBuzz(array('fizz', 'buzz', 'fizz')); // 3,4,5,6
inverseFizzBuzz(array('fizz', 'fizz')); // 6,7,8,9
inverseFizzBuzz(array('fizz', 'fizz', 'buzz')); // 6,7,8,9,10
inverseFizzBuzz(array('fizzbuzz', 'fizz')); // 15,16,17,18
// 解なし
inverseFizzBuzz(array('buzz', 'buzz'));
inverseFizzBuzz(array('buzz', 'fizz', 'buzz'));
inverseFizzBuzz(array('fizz', 'hoge'));
exit;
function inverseFizzBuzz($fb) {
    $values = array();
    for ($n = 1; $n < 101; $n++) {
        if ($fb[0] != n2fb($n)) continue;
        if ($arr = fb2array($fb, $n)) $values[] = $arr;
    }
    print implode(', ', $fb) . ' -> ' . implode(', ', fblen($values)) . "n";
}
function fblen($arr) {
    // 最短のを選ぶ
    if (!count($arr)) return $arr;
    $n = count($arr[0]);
    $ifb = $arr[0];
    foreach ($arr as $value) {
        if (count($value) < $n) {
            $ifb = $value;
            $n = count($value);
        }
    }
    return $ifb;
}
function fb2array($fb, $n) {
    // fizz,buzz,fizzbuzzから配列を得る
    $values = array($n);
    $length = count($fb);
    $fb = array_slice($fb, 1);
    for ($i = 1; $i < $length; $i++) {
        if (!count($fb)) break;
        // 15ぐらい先までで十分
        for ($j = $n + 1, $end = $n + 15; $j <= $end; $j++) {
            if ($j > 100) break;
            $values[] = $j;
            // fizz,buzzの順番が違ったらやめる
            if (n2fb($j) && $fb[0] != n2fb($j)) break;
            if ($fb[0] == n2fb($j)) {
                // fizz,buzz,fizzbuzzのどれかの場合は$fb[0]取り出してまた15先まで見る
                $fb = array_slice($fb, 1);
                $end += 15;
                if (!count($fb)) {
                    // 最小で終わらせる
                    break;
                }
            }
        }
    }
    if (count($fb) > 0) {
        return null;
    } else {
        return $values;
    }
}
function n2fb($n) {
    // 数値からfizzbuzzを得る
    return $n % 15 ? $n % 5 ? $n % 3 ? '' : 'fizz' : 'buzz' : 'fizzbuzz';
}
?>

実行結果

fizz -> 3
buzz -> 5
fizz, buzz -> 9, 10
buzz, fizz -> 5, 6
fizz, buzz, fizz -> 3, 4, 5, 6
fizz, fizz -> 6, 7, 8, 9
fizz, fizz, buzz -> 6, 7, 8, 9, 10
fizzbuzz, fizz -> 15, 16, 17, 18
buzz, buzz -> 
buzz, fizz, buzz -> 
fizz, hoge -> 

PHP5のfgetcsvで文字化けするときの対応策 fgetcsv_reg を少し変えてみた

PHP5でfgetcsvを使用すると文字化けしたり正常に読み込めない場合の対応策として fgetcsv_reg というユーザー定義関数 ( PHP5でfgetcsvが正常に動作しない : ::yossy.blog:: )
がありますが、大量にcsvファイルを読み込んでると結構遅くなるので若干変えてみました。

殆ど同じですが$lengthの初期値1024にしてtrim(), empty()使わなかったりfeof()でファイル終端か判断してるぐらいで、気持ち早いかなって程度です。

※いい加減なので場合によっては遅くなるかもしれません。

<?php
function fgetcsv_reg( &$handle, $length = 1024, $delimiter = ',', $enclosure = '"' ) {

    $delimiter = preg_quote( $delimiter );
    $enclosure = preg_quote( $enclosure );

    $buffer = '';

    while ( !feof( $handle ) ) {
        $buffer .= fgets( $handle, $length );
        if ( preg_match_all( '/' . $enclosure . '/', $buffer, $m ) % 2 == 0 ) {
            break;
        }
    }

    $pattern = '/(' . $enclosure . '[^' . $enclosure . ']*(?:' . $enclosure . $enclosure . '[^' . $enclosure . ']*)*' . $enclosure . '|[^' . $delimiter . ']*)' . $delimiter . '/';

    $values = array();
    $len = preg_match_all( $pattern, preg_replace( '/[rn]+$/', '', $buffer ) . $delimiter, $matches );
    for ( $i = 0; $i < $len; $i++ ) {
        $values[] = str_replace( $enclosure . $enclosure, $enclosure, preg_replace( '/^' . $enclosure . '(.*)' . $enclosure . '$/s', "$1", $matches[1][$i] ) );
    }
    return $buffer ? $values : false;

}
?>

PHPのマルチバイト文字列関数で”ISO-2022-JP”, “JIS”, “ISO-2022-JP-MS”の違い

PHP5.2.1ぐらいから”ISO-2022-JP-MS”というのも使えるそうなので”ISO-2022-JP”,”JIS”とどう違うか試してみました。

<?php
/* UTF-8からISO-2022-JP, JIS, ISO-2022-JP-MSに変換してみる */
setlocale( LC_ALL, 'ja_JP.UTF-8' );

/* ダメ元で☎ ☃なども */
$str = 'アイウエオ ㈱ ㈲ ① ② Ⅰ Ⅱ ℡ ㎏ ㎎ 﨑 髙 神 ☎ ☃';

header( 'Content-Type: text/plain; charset=ISO-2022-JP' );

printf( "%sn", 'PHP' . PHP_VERSION );
$encodings = array( 'ISO-2022-JP', 'JIS', 'ISO-2022-JP-MS' );
foreach ( $encodings as $encoding ) {
    printf( "%sn", $encoding . ' : ' . mb_convert_encoding( $str, $encoding, 'UTF-8' ) );
}
?>

これを実行すると以下のようになります。

PHP5.2.17
ISO-2022-JP : ????? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
JIS : アイウエオ ? ? ? ? ? ? ? ? ? ? ? ? ? ?
ISO-2022-JP-MS : アイウエオ ㈱ ㈲ ① ② Ⅰ Ⅱ ℡ ㎏ ㎎ 﨑 髙 神 ? ?

ISO-2022-JPとJISの違いは半角カナが使えるかどうか。ISO-2022-JP-MSはそれに加えて機種依存文字や外字まで使えてしまう。ってことですかね。

メール送信で使えそうな気がしますが、”-MS”と付いてるだけあってWindows以外では化けると思います。
Macは試してませんが携帯宛だど髙などが絵文字か空白か「?」に化けてました。

ということで、半角カナを使われそうな携帯などで”JIS”、それ以外では”ISO-2022-JP”を使って”ISO-2022-JP-MS”は使わない方が無難。

PHPでFizzBuzz問題を考えてみた

まず、FizzBuzz問題とは。

1~100までの数字で3で割り切れる時は「Fizz」、5で割り切れる時は「Buzz」、両者で割り切れる時は「FizzBuzz」と出力する。

たったこれだけです。
せんだ!みつお!なはなは!とか3の倍数と3が付くときのアレみたいな感じです。

「2分以内で書け」や「1行で書け」や「○○バイト以内で書け」や「%を使うな」などの条件を付ける場合もありますが、これをPHPで書いたらどうなるかと思いましてやってみました。

<?php
for ( $i = 1; $i <= 100; $i++ ) {
    if ( $i % 15 == 0 ) {
        print 'FizzBuzz';
    } else if ( $i % 3 == 0 ) {
        print 'Fizz';
    } else if ( $i % 5 == 0 ) {
        print 'Buzz';
    } else {
        print $i;
    }
    print "n";
}
// <プークスクス
?>

実行結果

ハッ 嘲笑なんか気にしなければこれで良いんですよ!!

では「1行で書く」ってのを考えると、これを改行入れなければいいじゃん?ってなる訳で「どれだけ短く書けるか」ってのに挑戦しました。

まず、 if { } else if { } … この辺が長いので三項演算子を使うことに。
あまりひねらない書き方ですが、それぞれを三項演算子に置き換え、1行で書いた方が短いので for文の {} や余計なスペース、最後のセミコロンも省略します。

<?php for($i=1;$i<=100;$i++)print($i%15?($i%3?($i%5?$i:'Buzz'):'Fizz'):'FizzBuzz')."n"?>

何かそれっぽくなってきた!!
この時点で89バイト。

更に短くするにはと思いついたのが
for ( $i = 1; $i <= 100; $i++ ) の部分です。ここを for ( $i = 1; $i < 101; $i++ ) にしたら1文字減りますね。 何かで見た事あった次の書き方でも動きます。 for ( $i = 0; ++$i < 101; ) これでいい気もしますが101ってのがどうも気に入らないので1減らして for ( $i = 0; $i++ < 100; ) この書き方の方が見やすいかもです。文字数変わりませんし。 [php title="1行でFizzBuzz (87バイト)"]<?php for($i=0;$i++<100;)print($i%15?($i%5?($i%3?$i:"Fizz"):"Buzz"):"FizzBuzz")."n";?>[/php] これをTwitterに書いた直後に print を echo に変えるって気付きました。 echo は print と違って ,(コンマ) で区切って出力するって方法があります。ついでに最初の <?php を <? でもいいっての思い出して [php title="79bytes"]<?for($i=0;$i++<100;)echo$i%15?($i%3?($i%5?$i:'Buzz'):'Fizz'):'FizzBuzz',"n"?>[/php] ギリギリ80バイトの壁を越えました。 もうこの辺からparse error覚悟で動けばいいや!ってスペースどこ削って大丈夫か体当たり的になってます。 echo$i なんか続けて書いていいのかよ!とツッコミ入れたい。 ただ、この書き方 15の時がどうにか出来そうな気がします。 「3で割り切れる」=「FizzもしくはFizzBuzzなのでFizzだけ出力」、「5で割り切れない」=「数字だけ」ということを頭に入れて15という数字を消すと [php gutter="0" classname="inline"]( $i % 3 ( $ % 5 ? $i : '' ) : 'Fizz' ), ( !$i % 5 ? 'Buzz' : '' )[/php] こう書くこともできます。 いや ( !$i%5 ? 'Buzz' : '' ) は逆に出来るじゃないか… ということで ( $i%5 ? '' : 'Buzz' ) それでPHPの定数で思い出した事が1つ。 定義してない定数(主にtypo)はそのまま定数名が出力されたことありませんか? このPHPのいい加減な何でも文字列を利用して 「Fizz, Buzzという定数のつもりだけど実は定義してなかったからそのまま出力してね!」作戦。 [php title="FizzBuzz 67bytes "]<?for($i=0;$i++<100;)echo$i%3?($i%5?$i:''):Fizz,$i%5?'':Buzz,"n"?>[/php] ここで67バイトまで縮めることが出来ました。 ギブアップ。 翌日、forじゃなくても良いんじゃね?て思いwhileに変えると結構短く出来ました。 [php title="FizzBuzz 63bytes"]<?while($i++<100)echo$i%3?($i%5?$i:''):Fizz,$i%5?'':Buzz,"n"?>[/php] ここで、Twitterにpostしたところ救世主 @daira4000 さんに「三項演算子の()を外して、nを直接LFにする」と教えて頂きましてさらに短くなり、最後の ?> は ; に変えても大丈夫ということでついに60バイトの壁突破。

<?while($i++<100)echo$i%3?$i%5?$i:'':Fizz,$i%5?'':Buzz,"
";

実行結果

ΩΩΩ< ()は消しても大丈夫なのか-!!!
さらに考えたけど順番が変わった程度でした。

<?while($i++<100)echo$i%3?'':Fizz,$i%5?$i%3?$i:'':Buzz,"
";

PHPでは最短56バイトで書けるらしいんですがそんな脳みそは持ち合わせておりません。おわり。