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

2011-11-13

mb_convert_kana.js


ダウンローはこちらから。
https://docs.google.com/open?id=0BwK7sPpG0c5ZYWRiNDcyZWYtODY2Ni00MmRkLWIwMjMtYTcwNWExZTgwMzZm

説明。
http://php.net/manual/ja/function.mb-convert-kana.php

2011-05-09

PHP SCREWのインストール

http://www.pm9.com/newpm9/itbiz/php/phpscrew/
http://slashdot.jp/~bero/journal/508118
http://xcache.lighttpd.net/changeset/548
http://phpspot.org/blog/archives/2006/05/php_40.html

# ダウンロード後、解凍してphpizeを実行する。
$ tar xvzf php_screw-1.5.tar.gz
$ cd ./php_screw-1.5/
$ lv README.jp
$ phpize

# makeが失敗する。
$ ./configure
$ make

# LT_INITのエラーが出る場合、修正する。
# もしくはlibtool-1.5の環境でphpizeを実行した結果をディレクトリごとコピーする。
$ libtool --version|head -n1
libtool (GNU libtool) 2.2.10
$ rm -i aclocal.m4
$ autoreconf -ifv

# makeが失敗する。
$ ./configure
$ make

# php5.3の場合、修正する。
$ cp -i php_screw.c php_screw.c.org
$ emacs php_screw.c
$ diff php_screw.c.org php_screw.c
124c124,128
<       CG(extended_info) = 1;
---
> #ifdef ZEND_COMPILE_EXTENDED_INFO
>         CG(compiler_options) |= ZEND_COMPILE_EXTENDED_INFO;
> #else
>         CG(extended_info) = 1;
> #endif
133c137,141
<       CG(extended_info) = 1;
---
> #ifdef ZEND_COMPILE_EXTENDED_INFO
>         CG(compiler_options) |= ZEND_COMPILE_EXTENDED_INFO;
> #else
>         CG(extended_info) = 1;
> #endif

# 暗号化SEEDキーを修正する。
$ cp -i my_screw.h my_screw.h.org
$ emacs my_screw.h-make.php
$ cat my_screw.h-make.php
short pm9screw_mycryptkey[] = {
<?php
for ($i = 1; $i < pow(2, 16); $i++)
{
    if ($i != 1)
    {
        print ", ";
    }
    print mt_rand(-32768, 32767);
}
print "\n";
?>
};
$ php my_screw.h-make.php > my_screw.h
$ diff my_screw.h.org my_screw.h|lv

# 再度実行する。
$ make clean
$ ./configure
$ make
$ cd ./tools/
$ make

# インストールする。
$ cd ../modules/
$ strip php_screw.so
$ sudo cp -i php_screw.so /usr/lib64/php5.3/lib/extensions/no-debug-non-zts-20090626/php_screw.so
$ sudo chmod 0644 /usr/lib64/php5.3/lib/extensions/no-debug-non-zts-20090626/php_screw.so

# phpの設定ファイルを修正する。
$ sudo su -
$ echo extension=php_screw.so >> /xxx/php.ini
$ exit

# 再起動後、成功の場合、phpinfo()にphp_screwの項目が出現する。
$ sudo /etc/init.d/apache2 restart

# コマンドをインストールする。
$ cd ../tools/
$ make
$ strip screw
$ sudo cp -i screw /xxx/bin/

# テストする。
$ cd /var/www/xxx/
$ emacs sample.php
$ cat sample.php
<?php
print date("Y-m-d H:i:s");
$ screw sample.php
$ rm -i sample.php.screw

# http://example.com/sample.phpにアクセス出来ればOK

バイナリデータの解析に詳しい場合php_screw.soから暗号化SEEDキーを取り出すのは簡単らしいです。
暗号化したxxx.phpの中身を閲覧出来る状況であればphp_screw.soの中身も閲覧できるかもしれないので、問題ないかどうか検討するべきでしょう。
例えば/var/www/html/以下のxxx.phpファイルを通常ファイルに設定してapacheを再起動してしまったという場合、http経由でphpの中身をダウンロードできますが、php_screw.soが見れない場合が多いので力技で解凍することになり、暗号化SEEDキーを長めにしておけば元のファイルを復元するまでに、ある程度の時間を稼げるでしょう。
apacheのプロセスの寿命を寿命無し、プロセス数固定にして、apache起動後にphp_screw.soを消すとどうでしょう?
apache起動サーバーが別にあるとして、php_screw.so送信=>apache起動コマンド送信=>送ったphp_screw.so削除、でどうでしょう?
メモリのデータを解析できる場合、意味が無いのかもしれません。
selinuxやtomoyoなどのソフトを併用するとどうでしょう?
php_screw.soと利用するすべてのxxx.phpをセットであらかじめ作成しておいてapache再起動時には入れ替えるというのはどうでしょう?
その面倒さに見合った効果が得られるでしょうか?
攻撃者がapacheの動いているOSにログイン出来ている時点で、きっとあまり意味が無いでしょうか?
有料の暗号化ソフトもあるので、それも検討するべきでしょう。

xcacheのList PHPではHitsは増えるので毎回復号化している訳ではない?

2010-10-11

pcntl_fork の例

子プロセスを作成する例です。
myDaemon.2010-10-11.tar.gz をダウンロードします。
設定の変更と子プロセスの内容の変更は myDaemon.php です。
起動方法の例です。
$ php myDaemon.php
$ tail -f /tmp/myDaemon.child_log
...
$ kill -TERM `cat /tmp/myDaemon.pid` # 止まる。
$ kill -USR1 `cat /tmp/myDaemon.pid` # 子プロセスの終了を待って止まる。
$ kill -HUP -`cat /tmp/myDaemon.pid` # ログファイルを再度開く。子プロセスも開く。

説明です。
<?php

// シグナルを受け取るのに必要です。
declare(ticks = 1);

// クラスをロードします。
require_once("class.myDaemonBase.php");

// クラスの設定を変更します。
class myDaemon extends myDaemonBase
{
    // 親プロセスのPIDの保存先
    protected $pidFile         = "/tmp/myDaemon.pid";
    // 親プロセスのログの保存先
    protected $mainLogFile     = "/tmp/myDaemon.log";
    // 子プロセスのログの保存先
    protected $childLogFile    = "/tmp/myDaemon.child_log";
    // phpエラーをフックする前に出るphpエラーの保存先
    protected $phpErrorLogFile = "/tmp/myDaemon.php_error_log";
    // 子プロセスの数
    protected $maxChildNum     = 10;
    // 無限にループする部分で1回ごとに休憩する秒数
    protected $sleepTime       = 1;
    // ログを増やすかどうか?
    protected $debugFlag       = false;
}

// 子プロセスが実行する関数名
function childProcess($class)
{
    $class->Logger("Start child process");
    $time = rand(1, 60);
    $class->Logger("Sleep for {$time} seconds.");
    for ($i = 0; $i < $time; $i++)
    {
        sleep(1);
    }
    $class->Logger("End child process");
}

// 常駐プロセスの起動
myDaemon::start();
exit(0);

メモリの設定が足りていないと子プロセスの生成と終了を繰り返す例です。
$ cat /etc/php/cli-php5/php.ini | grep -v ^\; | grep -v ^$ > myDaemon.ini
$ emacs myDaemon.ini
$ cat myDaemon.ini
short_open_tag                 = Off
allow_call_time_pass_reference = Off
memory_limit                   = 8M
error_reporting                = E_ALL
display_errors                 = STDERR
log_errors                     = On
error_log                      = /tmp/myDaemon.php_error_log
log_errors_max_len             = 4096
register_long_arrays           = Off
include_path                   = ".:/usr/share/php5:/usr/share/php"
enable_dl                      = Off
allow_url_fopen                = Off

extension = pcntl.so
extension = mbstring.so

mbstring.language             = Japanese
mbstring.internal_encoding    = UTF8
mbstring.http_input           = auto
mbstring.http_output          = pass
mbstring.encoding_translation = On
mbstring.detect_order         = auto
mbstring.substitute_character = 0x3013
mbstring.func_overload        = 0
$ php -n -c myDaemon.ini myDaemon.php
$ sleep 3
$ kill `cat /tmp/myDaemon.pid`
$ tail -n1 /tmp/myDaemon.php_error_log
[17-Oct-2010 01:51:37] PHP Fatal error:  Allowed memory size of 8388608 bytes exhausted (tried to allocate 10485761 bytes) in /var/www/localhost/php/System_Daemon/myDaemon.php on line 26
狙いは動的にロードするモジュールを最低限にすること、
設定の変更を、変更したい部分だけに絞ってわかりやすくすること、です。

例えば、変更したことによってプログラムが memory_limit=8M 以上ないと動かない、
などのハードウェィアへの依存度がわかったり、
phpのバグだと認識していたものが、ただの設定の違いであることが判明したり、
するかもしれません。

よって例えば centos5 の php5.1.6 のデフォルトがあなたの利用する全てのphpである場合、
このような設定の変更は、混乱するだけです。

php -n は /etc/php.ini などを全て読みません。
php -c ファイル名は、設定ファイルを追加で読みます。
php5.2より新しくないと -n と -c は同時に使えないかもしれません。

error_reporting = E_ALL はお勧めです。
バグが必ずあるとして E_NOTICE を発生させるタイプは、すぐに対応できる、というのが主な理由です。
E_NOTICE が邪魔というのは、すぐになれる可能性が高いです。(正常な場合に出ないように書くのは容易という意味です。)
対して E_NOTICE が発生するミスというのはログに取れないと特定が大変です。
デフォルトが E_ALL & ~E_NOTICE であると仮定した場合の話です。
例えば perl で use strict; use warnings; があった場合に少し安心しますが、それに似ています。
log_errors=On, error_log=filename, display_errors=0|1|stderr もセットで有効です。

2010-04-11

php で標準入力を取得する例

function getStdin()
{
    $ret = "";
    $fp = fopen("php://stdin", "r");
    if (is_resource($fp))
    {
        $cnt = 0;
        stream_set_blocking($fp, 0);
        do
        {
            $buf = fread($fp, 512);
            if ($buf === "")
            {
                $cnt++;
                if ($cnt > 15)
                {
                    break;
                }
            }
            else
            {
                $cnt = 0;
                $ret .= $buf;
            }
        }
        while (1);
        fclose($fp);
    }
    return $ret;
}

手前のコマンドがゆっくり出力される場合、失敗します。
$ for i in $(seq 1 3); do echo $i; sleep 1; done
1
2
3
$ for i in $(seq 1 3); do echo $i; sleep 1; done | php wagnek.php
  2| Basic Latin    |   1| 1
  1| Control Code...|   1| \n

必ず標準入力が有る場合 stream_set_blocking($fp, 0); をコメントアウトすると良いです。

2010-04-10

array.patterns.Blocks.UTF-8.php part2

array.patterns.Blocks.UTF-8.php は http://unicode.org/Public/UNIDATA/Blocks.txt を UTF-8 と php と pcre で表現した場合の結果です。

前回と比べて変化は無いです。
利用するためのサンプルが、一度にマッチする量が多い場合に、セグメンテーション違反になっていたので修正しました。
wagnek.2010-04-10.tar.gz

(例 1) UTF-8 のファイルの、ファイル名を渡す。
$ date | nkf -w > date.txt
$ php wagnek.php date.txt
  2| Basic Latin    |   4| 2010
113| CJK Unified ...|   3| 年
  2| Basic Latin    |   3|   4
113| CJK Unified ...|   3| 月
  2| Basic Latin    |   3|  10
113| CJK Unified ...|   3| 日
  2| Basic Latin    |   1|
113| CJK Unified ...|   9| 土曜日
  2| Basic Latin    |  13|  19:48:55 JST
  1| Control Code...|   1| \n

(例 2) UTF-8 ではないテキストの内容を UTF-8 にして渡す。
$ cat ../wagahaiwa_nekodearu.txt | nkf -w | php wagnek.php | head -n10
113| CJK Unified ...|   6| 吾輩
101| Hiragana       |   3| は
113| CJK Unified ...|   3| 猫
101| Hiragana       |   9| である
  1| Control Code...|   2| \r\n
113| CJK Unified ...|  12| 夏目漱石
  1| Control Code...|   4| \r\n\r\n
  2| Basic Latin    |  55| -------------------------------------------------------
  1| Control Code...|   2| \r\n
100| CJK Symbols ...|   3| 【

文字を分割したい場合は chasen, kakasi, mecab がお勧めです。
"199| unknown" という文字が表示される場合、違う文字コードを渡している可能性が高いです。

(例 3) EUC-JP の文字を渡すと "199| unknown" が発生する。
$ date | nkf -e | php wagnek.php
  2| Basic Latin    |   4| 2010
  5| Latin Extend...|   2| ǯ
  2| Basic Latin    |   3|   4
199| unknown        |   2| \xB7\xEE
  2| Basic Latin    |   3|  10
199| unknown        |   2| \xC6\xFC
  2| Basic Latin    |   1|
199| unknown        |   6| \xC5\xDA\xCD\xCB\xC6\xFC
  2| Basic Latin    |  13|  19:55:51 JST
  1| Control Code...|   1| \n

unknown の場合1バイトずつ 16進数 で表現しています。
$ php -r 'var_export(mb_convert_encoding("\xB7\xEE", "UTF-8", "EUC-JP")); print "\n";';
'月'
$ php -r 'var_export(mb_convert_encoding("\xC6\xFC", "UTF-8", "EUC-JP")); print "\n";';
'日'
$ php -r 'var_export(mb_convert_encoding("\xC5\xDA\xCD\xCB\xC6\xFC", "UTF-8", "EUC-JP")); print "\n";';
'土曜日'

ǯ (&#x1ef;) の部分は UTF-8 の別の文字コードにマッチしています。
$ php -r 'var_export(unpack("N", mb_convert_encoding(mb_convert_encoding("年", "EUC-JP", "UTF-8"), "UCS-4", "UTF-8"))); print "\n";';
array (
1 => 495,
)
$ php -r 'print dechex(495) . "\n";'
1ef
http://ja.wikipedia.org/wiki/Unicode%E4%B8%80%E8%A6%A7_0000-0FFF の 横 01E0 - 縦 F の部分です。

例えば、データベースに binary(blob)型で UTF-8 に似ているデータが入っている場合のサルベージや、サルベージするかどうかの検討に利用します。

php の json_decode のエラー

json_decode でエラーが出るので調べたところ、下記のように記述すると解決しました。
バックスペース(\x08): \b
水平タブ      (\x09): \t
改ページ      (\x0C): \f

以下は詳細です。

json_last_error() は php-5.3 で使えるので、無い場合、適当な場所にインストールします。
$ ./configure --prefix=/usr/local/stow/php-5.3.2 --disable-cgi --disable-all --enable-json
$ make
$ sudo make install

エラーの出る(json_decode() が NULL を返す)プログラムに json_last_error() を追加します。
$ lv /tmp/test.php
...
var_dump(json_decode($buf));
var_dump(json_last_error());
$arr = get_defined_constants(true);
var_dump($arr["json"]);
...

json_last_error() が JSON_ERROR_CTRL_CHAR でした。
$ /usr/local/stow/php-5.3.2/bin/php /tmp/test.php | lv
...
NULL
int(3)
array(10) {
  ["JSON_HEX_TAG"]=>
  int(1)
  ["JSON_HEX_AMP"]=>
  int(2)
  ["JSON_HEX_APOS"]=>
  int(4)
  ["JSON_HEX_QUOT"]=>
  int(8)
  ["JSON_FORCE_OBJECT"]=>
  int(16)
  ["JSON_ERROR_NONE"]=>
  int(0)
  ["JSON_ERROR_DEPTH"]=>
  int(1)
  ["JSON_ERROR_STATE_MISMATCH"]=>
  int(2)
  ["JSON_ERROR_CTRL_CHAR"]=>
  int(3)
  ["JSON_ERROR_SYNTAX"]=>
  int(4)
}

$buf を最小限にしてテストしたところ、バックスペース、水平タブ、改ページ、が、そのまま書かれていることが原因でした。

2010-04-03

php の ob_start の利用例 (preg_replace の正規表現の例)

html を出力する前にスペースを取り除く例です。
<?php
function callback($buffer)
{
    $buf = $buffer;
    $pattern = array();
    $replace = array();

    // 改行を \n に統一する。
    $pattern[] = "/(?:\r\n)|[\r\n]/";
    $replace[] = "\n";

    // \n を除く制御文字を削除する。
    $pattern[] = "/[\\x00-\\x09\\x0b-\\x1f]/";
    $replace[] = " ";

    // 反映させる。
    $buf = preg_replace($pattern , $replace, $buf);

    // 修正しない部分を保管して代替文字に入れ替える。
    $tmpName = "__TMP__";
    $i = 0;
    do
    {
        if (!isset($GLOBALS[$tmpName . $i]))
        {
            $tmpName .= $i;
            break;
        }
        if ($i > 10)
        {
            $tmpName .= md5(mt_rand()) . md5(mt_rand());
            break;
        }
        $i++;
    }
    while (true);
    $GLOBALS[$tmpName] = array();
    $buf = preg_replace_callback(
        "/<(pre|s(?:cript|tyle)|xmp)(?:\s*|(?:\s+[^>]+))>(.*?)<\/\\1\s*>/is",
        create_function('$matches',
                        '$tmp =& $GLOBALS["' . $tmpName . '"];' .
                        'if ($matches[2] === "") { return $matches[0]; }' .
                        '$tmp[] = $matches[0];' .
                        'return "<\\x00," . count($tmp) . ",\\x01>";'),
        $buf);

    $pattern = array();
    $replace = array();

    // 最初と最後の空白を削除する。
    $pattern[] = "/^\s+|\s+$/";
    $replace[] = "";

    // コメントを削除する。
    $pattern[] = "/<\!\-\-.*?(?<=\-\-)>/s";
    $replace[] = "";

    // 行ごとの最初と最後の半角スペースを削除する。
    $pattern[] = "/^ +| +$/m";
    $replace[] = "";

    // 改行を減らす。
    $pattern[] = "/\\n{3,}/";
    $replace[] = "\n\n";

    // 半角スペースを減らす。
    $pattern[] = "/ {2,}/";
    $replace[] = " ";

    // <, > に接触する空白を削除する。
    $pattern[] = "/\s*([<>])\s*/";
    $replace[] = "$1";

    // </head>までのタグに改行を追加する。
    $pattern[] = "/^(.*?)(<\/head>)/eis";
    $replace[] = "rtrim(preg_replace(\"/><(?!\\/)/\", \">\\n<\", \"$1\")) . \"\\n$2\\n\"";

    // ブロック要素の終わりに改行を追加する。
    $pattern[] = "/<\/(?:address|blockquote|c(?:aption|ol(?:group)?)|d(?:iv|[dlt])|" .
        "f(?:ieldset|orm)|h[1-6r]|l(?:egend|i)|noscript|ol|p(?:re)?|" .
        "t(?:able|body|foot|head|[dhr])|ul)>/i";
    $replace[] = "$0\n";

    // brタグの終わりに改行を追加する。
    $pattern[] = "/<br((?:\s*)|(?:\s+[^>]+?))\/?" . ">/ei";
    $replace[] = "\"<br\" . preg_replace(\"/^\\s+|\\s+$/\", \" \", \" $1 \") . \"/>\\n\"";

    // 反映させる。
    $buf = preg_replace($pattern , $replace, $buf);

    // 代替文字を元に戻す。
    $buf = preg_replace_callback(
        "/<\\x00,(\d+),\\x01>/",
        create_function(
            '$matches',
            '$tmp =& $GLOBALS["' . $tmpName . '"];' .
            'return $tmp[$matches[1] - 1];'
            ),
        $buf);

    return $buf;
}
ob_start("callback");

主な用途はタグに隣接するスペースの削除です。
<tag>\n
  <tag>\n
      This is an example.\n
  </tag>\n
</tag>\n

htmlソースの見易さのために、ブロック要素の終わりに改行を追加するため、スタイルシートなどでブロック要素を display: inline; にしている場合、余分なスペースが発生します。

以下は詳細です。
http://jp.php.net/manual/ja/reference.pcre.pattern.syntax.php

    // 改行を \n に統一する。
    $pattern[] = "/(?:\r\n)|[\r\n]/";
    $replace[] = "\n";
\r\n を優先して検索して、あれば \n に変更し、無ければ \r を \n に変更します。(\n を \n に変換する意味の無い処理も含まれます)

    // \n を除く制御文字を削除する。
    $pattern[] = "/[\\x00-\\x09\\x0b-\\x1f]/";
    $replace[] = " ";
\n(10番)を除く 0 ~ 31番の文字を 32番(半角スペース)へ変更します。
http://ja.wikipedia.org/wiki/ASCII#ASCII.E5.88.B6.E5.BE.A1.E6.96.87.E5.AD.97

UCS-4 などの該当部分が1バイトではない文字コードは破壊されます。
表現は16進数です。 (\x0A → \10, \x1F → \31, \x20 → \32)
[ ... ] で括った場合で 文字-文字 となっている場合、範囲を指定しています。
例えば半角スペースを \x20 と表現した場合の利点は、 \x00-\x20 と書いた場合見やすいことや、スペースを無視する正規表現 ( /pattern/x ) の際に半角スペースを指定する、などです。

    // 修正しない部分を保管して代替文字に入れ替える。
    $tmpName = "__TMP__";
    ...
    $GLOBALS[$tmpName] = array();
    $buf = preg_replace_callback(
        "/<(pre|s(?:cript|tyle)|xmp)(?:\s*|(?:\s+[^>]+))>(.*?)<\/\\1\s*>/is",
        create_function('$matches',
                        '$tmp =& $GLOBALS["' . $tmpName . '"];' .
                        'if ($matches[2] === "") { return $matches[0]; }' .
                        '$tmp[] = $matches[0];' .
                        'return "<\\x00," . count($tmp) . ",\\x01>";'),
        $buf);
pre, script, style, xmp タグを保管場所に移動します。

利用していないグローバル変数を検索して空の配列とし、文字列の保管場所にします。
    $tmpName = "__TMP__";
    ...
    $GLOBALS[$tmpName] = array();

タグの中身が空の場合には移動をしません。
http://jp.php.net/manual/ja/function.preg-replace-callback.php
'if ($matches[2] === "") { return $matches[0]; }' .

始まりのタグ
<(pre|s(?:cript|tyle)|xmp)(?:\s*|(?:\s+[^>]+))>

大文字小文字を無視します。( /pattern/i )

中身
(.*?)

改行を含む任意の文字にマッチします。( /pattern/s )
中身に </script> などがあれば失敗し、期待した動作になりません。
? をつけない場合、最後の </script> にマッチします。
(量指定子の後に疑問符を付けると、貪欲さは消え、できるだけ少ない回数だけマッチします付近の説明)

終わりのタグ
<\/\\1\s*>

\\1 は後方参照で始まりのタグで指定した pre, script, style, xmp の、どれかが入ります。(括弧の始まりの後ろに ?: と付かない (...) で囲まれた部分です)
phpの正規表現は文字列に正規表現を入れるので \\1 は 動作する際には \1 になっています。
例えば \\n, \n は \n という文字か、改行(line feed)自体の文字かの違いになりますので変化は無いです。
スペースを無視する正規表現( /pattern/x ) では、その違いは重要です。

    // 最初と最後の空白を削除する。
    $pattern[] = "/^\s+|\s+$/";
    $replace[] = "";
^ は文字列の始まりです。
$ は文字列の終わりです。
| は、or、もしくは、の意味です。
\s は改行を含む空白です。

    // コメントを削除する。
    $pattern[] = "/<\!\-\-.*?(?<=\-\-)>/s";
    $replace[] = "";
<!-- という文字を探し、あった場合、後ろに続く処理を行います。
.*? は改行を含む任意の文字にマッチしますが、常に最短の短さでマッチしようとしますので > の有る無しを常に探します。
> があった場合 > の手前の文字が -- であるかどうかをチェックします。
> の手前の文字が -- であった場合(<!--> など)マッチしたことになります。

    // 行ごとの最初と最後の半角スペースを削除する。
    $pattern[] = "/^ +| +$/m";
    $replace[] = "";
行ごとの最初と最後にマッチします。( /pattern/m )

// "/^ +| +$/m" の場合
  aaa  
  bbb  
  ccc  

// "/^ +| +$/" の場合
  aaa  
  bbb  
  ccc  

    // 改行を減らす。
    $pattern[] = "/\\n{3,}/";
    $replace[] = "\n\n";
\n が 3回以上続く場合 \n\n に変換します。

    // 半角スペースを減らす。
    $pattern[] = "/ {2,}/";
    $replace[] = " ";
半角スペースが2個以上続く場合、半角スペース1個に変換します。

繰り返しの例です。
a が0~1の時にマッチします。
a?
a{0,1}

a が0文字以上の時にマッチします。
a*
a{0,}

a が1文字以上の時にマッチします。
a+
a{1,}

文字の指定の後の ? と、文字の連続する回数の指定の後の ? が意味が違います。
後者は最小限の長さでマッチしてください、という意味です。

aab? に bが含まれます。
$ php -r '$buf = "aabbcc"; if (preg_match("/(aab?)(b*cc)/", $buf, $m)) { var_dump($m); }'
array(3) {
  [0]=>
  string(6) "aabbcc"
  [1]=>
  string(3) "aab"
  [2]=>
  string(3) "bcc"
}

aab?? に bが含まれません。
$ php -r '$buf = "aabbcc"; if (preg_match("/(aab??)(b*cc)/", $buf, $m)) { var_dump($m); }'
array(3) {
  [0]=>
  string(6) "aabbcc"
  [1]=>
  string(2) "aa"
  [2]=>
  string(4) "bbcc"
}

aab?? に bが含まれます。bcc の部分の b の長さが固定なためです。
$ php -r '$buf = "aabbcc"; if (preg_match("/(aab??)(bcc)/", $buf, $m)) { var_dump($m); }'
array(3) {
  [0]=>
  string(6) "aabbcc"
  [1]=>
  string(3) "aab"
  [2]=>
  string(3) "bcc"
}

    // <, > に接触する空白を削除する。
    $pattern[] = "/\s*([<>])\s*/";
    $replace[] = "$1";
$1 は ( ... ) で指定した <, > のどちらかが入ります。

    // </head>までのタグに改行を追加する。
    $pattern[] = "/^(.*?)(<\/head>)/eis";
    $replace[] = "rtrim(preg_replace(\"/><(?!\\/)/\", \">\\n<\", \"$1\")) . \"\\n$2\\n\"";
head の部分の大文字小文字を無視します。 ( /pattern/i )
.* の部分の任意の文字に改行を含めます。 ( /pattern/s )
( /pattern/e ) は $replace[] の部分を phpのコードとして解釈します。

php コード自体で書いた場合下記のような意味です。($matches に配列でマッチした文字が入っている場合)
rtrim(preg_replace("/><(?!\/)/", ">\n<", "{$matches[1]}")) . "\n{$matches[2]}\n";

"/><(?!\/)/"
>< にマッチしますが < の後ろに / が有る場合、マッチしません。(マッチする内容に / を含みません)

>< を >(改行)< に変換後に、文字列の後ろのスペースを取り除き(rtrim) (改行)</head>(改行) を追加します。

    // ブロック要素の終わりに改行を追加する。
    $pattern[] = "/<\/(?:address|blockquote|c(?:aption|ol(?:group)?)|d(?:iv|[dlt])|" .
        "f(?:ieldset|orm)|h[1-6r]|l(?:egend|i)|noscript|ol|p(?:re)?|" .
        "t(?:able|body|foot|head|[dhr])|ul)>/i";
    $replace[] = "$0\n";
</tagname> を </tagname>(改行) へ変更します。
指定しているタグ名は、ブロック要素です。
http://ja.wikipedia.org/wiki/HTML%E8%A6%81%E7%B4%A0#.E3.83.96.E3.83.AD.E3.83.83.E3.82.AF.E3.83.AC.E3.83.99.E3.83.AB.E8.A6.81.E7.B4.A0

括弧が多いのは、マッチにかかる時間を減らすためです。
aa|ab|ac → aa か ab か ac を探す。
a[abc]   → a を探してから a, b, c を探す。

p(?:re)?      → p, pre を探す。
d(?:iv|[dlt]) → div, dd, dl, dt を探す。

    // brタグの終わりに改行を追加する。
    $pattern[] = "/<br((?:\s*)|(?:\s+[^>]+?))\/?" . ">/ei";
    $replace[] = "\"<br\" . preg_replace(\"/^\\s+|\\s+$/\", \" \", \" $1 \") . \"/>\\n\"";
<br で始まり
スペースが0文字以上、もしくはスペース1文字以上で > では無い文字が1文字以上あり
/ が0~1文字あり
> で終わる
場合の文字の後ろに改行を追加します。

[^ ... ] の場合 ... を含まないという意味になります。
1文字(1バイト)にしか効果が有りません。

文字列の場合(preg_xxx で利用されている pcreライブラリは perl 互換です)
http://www.din.or.jp/~ohzaki/regex.htm#Without

スペースが0文字以上 の場合
<br />

スペースが1文字以上で > では無い文字が1文字以上 の場合
<br class="className" />
例えば<br class="<className>" />となっていた場合、失敗します。(htmlとしてはおかしいですが " の数が合っていればブラウザは正常に解釈する場合が多いです)

    // 代替文字を元に戻す。
    $buf = preg_replace_callback(
        "/<\\x00,(\d+),\\x01>/",
        create_function(
            '$matches',
            '$tmp =& $GLOBALS["' . $tmpName . '"];' .
            'return $tmp[$matches[1] - 1];'
            ),
        $buf);
<\\x00,(\d+),\\x01> の \x00, \x01 の部分は実際にはブラウザで見ると文字化けを起こすような文字が指定されています。
<\\x00,(\d+),\\x01> を埋め込む前に// \n を除く制御文字を削除する。で 元々ある \x00, \x01 は削除されています。

<\\x00,(\d+),\\x01> の 数字の部分は配列の個数で入っているので
'return $tmp[$matches[1] - 1];' で -1 で番号を指定します。
$tmp[] = $matches[0]; のように空の配列に追加した場合、配列のキーは 0, 1, 2 ... の順番で追加されます。
配列が1個 → $tmp = array(0 => "xxx");
配列が2個 → $tmp = array(0 => "xxx", 1 => "yyy");

この処理(function callback($buffer))は、htmlのタグのオプションの値の <, > が &lt;, &gt; のようにエスケープされていることや
key="a   b   c" が key="a b c" のようにスペースが短くなっても問題ないことや
(例えば <a onclick="javascript: ... code ...">click</a> などで問題が出る場合がある)
<script> ... </script> の中に var a="</script>" のような指定が無いこと
などの前提が必要です。
よって html の処理には tidy などがお勧めです。

2010-03-30

UTF-8 と UCS-4 の変換を php で行う場合の例

この記事は文字コードの変換に関する例ではありません。

phpでは文字コードの変換は用意されています。
http://jp2.php.net/manual/ja/function.mb-convert-encoding.php
http://jp2.php.net/manual/ja/mbstring.encodings.php

(例1) unicode番号にして戻す。
$ php -r 'var_export(unpack("N", mb_convert_encoding("〓", "UCS-4", "UTF-8"))); print "\n";';
array (
  1 => 12307,
)
$ php -r 'var_export(mb_convert_encoding(pack("N", 12307), "UTF-8", "UCS-4")); print "\n";';
'〓'

(例2) htmlの数値文字参照に使う。
utf8ucs4.php.2010-03-30.gz

文字参照 - Wikipedia: 数値文字参照(文字参照)
HTML Document Representation: 5.3.1 Numeric character references

UTF-8の正規表現

array.patterns.Blocks.UTF-8.php part2 が新しい記事です。

この記事は array.patterns.Blocks.UTF-8.php の紹介です。
wagahaiwa_nekodearu.2010-03-30.tar.gz をダウンロードします。
青空文庫 から 789_ruby_5639.zip をダウンロードして wagahaiwa_nekodearu.txt を同じディレクトリにコピーします。
$ tar xvzf wagahaiwa_nekodearu.2010-03-30.tar.gz
$ cd wagahaiwa_nekodearu
$ cp -i /tmp/wagahaiwa_nekodearu.txt .
$ php wagahaiwa_nekodearu.txt.php > wagahaiwa_nekodearu.txt.php.txt
$ head -n20 wagahaiwa_nekodearu.txt.php.txt
112: CJK Unif: 吾輩
100: Hiragana: は
112: CJK Unif: 猫
100: Hiragana: である
  1: Basic La: \r\n
112: CJK Unif: 夏目漱石
  1: Basic La: \r\n\r\n-------------------------------------------------------\r\n
 99: CJK Symb: 【
101: Katakana: テキスト
112: CJK Unif: 中
100: Hiragana: に
112: CJK Unif: 現
100: Hiragana: れる
112: CJK Unif: 記号
100: Hiragana: について
 99: CJK Symb: 】
  1: Basic La: \r\n\r\n
 99: CJK Symb: 《》
149: Halfwidt: :
101: Katakana: ルビ
これは使い方のサンプルなので、文字を分割したい場合は chasen, kakasi, mecab がお勧めです。
array.patterns.Blocks.UTF-8.php の元となるデータはhttp://unicode.org/Public/UNIDATA/Blocks.txtです。

2010-03-29

phpの配列関数の多次元配列対応

array_ksort.2010-04-01.tar.gz
このファイルは ksort() の例です。
グローバル関数に登録すると、ややこしいので extra::array_ksort() などにすると邪魔にならないかもしれません。

2010-03-23

php の 正規表現 (Perl 互換) 入れ子 サンプル

$buf = "...";
$pattern = "/"
    . "{";
$bracketsNum = 8;
for ($i = 0; $i < $bracketsNum; $i++)
{
    $pattern .= ""
        . "(?:(?:"
        . "(?:[^\{\}]*)"
        . "|"
        . "(?:(?:"
        . "{";
}
$pattern .= ""
    . "(?:[^\{\}]*)";
for ($i = 0; $i < $bracketsNum; $i++)
{
    $pattern .= ""
        . "}"
        . ")*)"
        . ")*)";
}
$pattern .= ""
    . "}"
    . "/";
if (preg_match_all($pattern, $buf, $matches))
{
    var_export($matches);
}
大外の{...}ごとに分割します。
"...", '...', /.../ などに {} があると失敗します。
その場合、そういったパターンを用意します。
$p1 = "(?:[^\{\}]*)";

$p2_1 = "[^\{\}\"\\\\]";
$p2_2 = "(?:\"(?:(?:[^\"\\\\]|(?:\\\\.))*)\")";

$p2 = "(?:(?:{$p2_1}|{$p2_2})*)";

$p3_1 = "[^\{\}\'\"\\\\]";
$p3_2 = "(?:\'(?:(?:[^\'\\\\]|(?:\\\\.))*)\')";

$p3 = "(?:(?:{$p3_1}|{$p3_2}|{$p2_2})*)";

$p4_1 = "[^\{\}\\/\'\"\\\\]";
$p4_2 = "(?:\\/(?:(?:[^\\/\\\\]|(?:\\\\.))*)\\/)";

$p4 = "(?:(?:{$p4_1}|{$p4_2}|{$p3_2}|{$p2_2})*)";
一部をコメント付きにした場合の例
$p2 = "
# 内部オプションにxを指定した場合に使えます。
# (?x:pattern) は無理です。
# / ... (?:pattern) ... /x とする場合。
(?:           # 括弧始まり
(?:           #     括弧始まり
[^\{\}\"\\\\] #         {}”¥ を含まない文字
|             #         もしくは
(?:           #         括弧始まり
\"            #             ” で始まり
(?:           #             括弧始まり
(?:           #                 括弧始まり
[^\"\\\\]     #                     ”¥ を含まない文字
|             #                     もしくは
(?:           #                     括弧始まり
\\\\.         #                         ¥ で始まる任意の(¥を含めて)2文字
)             #                     括弧終わり
)             #                 括弧終わり
*             #                 を0回以上
)             #             括弧終わり
\"            #             ” で終わる
)             #         括弧終わり
)             #     括弧終わり
*             #     を0回以上
)             # 括弧終わり
";

2010-02-13

PHP: socket_select - Manual

この記事は php の socket_select に関する参考です。

PHP: socket_select - Manual: "vardhan ( at ) rogers ( dot ) com
28-Aug-2005 02:46"

上記のサンプルは、(B)と(C)の入力(文字+改行)が(B)と(C)の両方に出力されました。
(A) phpを起動(終わらない)
(B) telnet (A)のIPアドレス 9050
(C) telnet (A)のIPアドレス 9050

この例だと(B),(C)のtelnetのプロセスを落とすと(A)でエラーが大量に出ます。
pear 使うと良いようです。
Manual :: Net_Server の利用: "例 – Net_Server の利用
シンプルなデーモンを生成する"

echo "test" | nc 127.0.0.1 -q10 9090

1つのプロセスがずっと動いて複数のなにかを監視する場合は stream_select, socket_select などを使うらしいです。
pcntl_signal で kill xxx に対応すると本格的になります。

2010-02-12

fifo

この記事は php で fifo を扱う例です。

1つのfifoを複数のプロセスで監視します。
<?php
$log_file = "/tmp/abc.txt";
$log_fp = fopen($log_file, "a");
$read_file = "./fifo_read";
posix_mkfifo($read_file, 0600);
$read_fp = fopen($read_file, "r");
$pid = posix_getpid();
while (true)
{
    $buf = stream_get_contents($read_fp);
    if (is_string($buf) && $buf != "")
    {
        foreach (preg_split("/\r\n|[\r\n]/", $buf, -1, PREG_SPLIT_NO_EMPTY) as $line)
        {
            fwrite($log_fp, $pid . ":" . date("Y-m-d H:i:s") . ":" . $line . "\n");
        }
    }
    fopen($read_file, "r");
}
?>

確認します。
for i in $(seq 1 10); do (php test.php &); done
echo -n "" > /tmp/abc.txt
for xx in $(seq 1 100); do ((for i in $(seq 1 1000); do echo "cat /proc/meminfo" > fifo_read; done) &); done
wc -l /tmp/abc.txt
grep /proc/meminfo /tmp/abc.txt |wc -l
cat /tmp/abc.txt |awk -F\: '{print $1}'|sort|uniq -c
killall php

1つプロセスで複数の fifo を監視するには inotify, dio (pecl)などを使うらしいです。
open(file, O_RDONLY)でノンブロックではないので止まります。strace
ログの書き込みが遅れます。
openの動作で一時停止させるのは、良くないかもしれません。

2010-02-04

phpのオプションでビット演算を使う場合

この記事は php の error_reporting 関数に関する参考です。

関数のオプションの定数をビット演算で複数指定する場合の確認をします。
print "<pre>\n";
printf("%' 30s | %' 6s | %' 13s\n", "constant", "dec", "bin");
foreach (array("E_ALL", "E_NOTICE", "E_ALL^E_NOTICE", "E_ALL&~E_NOTICE", "LOCK_SH", "LOCK_SH|LOCK_NB", "LOCK_EX", "LOCK_EX|LOCK_NB") as $value)
{
    eval("\$value1 = $value;");  // 良い書き方ではないです。
    printf("%' 30s | %'06d | %'013s\n", $value, $value1, decbin($value1));
}
print "</pre>\n";

例えば、下記の設定は E_ALL に E_STRICT が含まれるかどうかで変わります。
error_reporting(E_ALL &~ E_STRICT);
error_reporting(E_ALL ^ E_STRICT);

例えば E_ALL ^ E_NOTICE は E_ALL に E_NOTICE が含まれるので E_ALL から E_NOTICE を引いた値(E_ALL &~ E_NOTICE と同じ)になり E_ALL ^ E_STRICT は E_ALL に E_STRICT が含まれないので E_ALL | E_STRICT と同じ値になります。

php6 から E_ALL が E_STRICT を含むらしいので注意が必要です。
よって error_reporting に排他的論理和を使うのをやめることをお勧めします。

排他的論理和の例
print (0^0) . "=0\n";
print (0^1) . "=1\n";
print (1^0) . "=1\n";
print (1^1) . "=0\n";

例えば E_XXX &~ E_NOTICE は E_NOTICE のビットを0にしたいことが明白で
E_XXX ^ E_NOTICE は E_XXX の E_NOTICE のビットが、0の時1にして、1の時0にします。

(例) pear を使うので E_STRICT をOFFにする。
error_reporting(E_ALL &~ E_STRICT);
//set_error_handler("exception_error_handler", E_ALL &~ E_STRICT);

また .htaccess にエラーレベルを指定する場合には、番号になるので auto_prepend_file などに php のコード(error_reporting(XXX);)として書くほうが良いです。

2010-02-02

speedfan の設定 (SpeedFan Part4 の引用)

この記事は speedfan に関する参考です。

SpeedFan Part4 の引用
SpeedFan Part4: "7 :名無しさん@お腹いっぱい。[sage]:2007/02/16(金) 19:54:14 ID:NaOnMsdl0
Divisorの設定についても、テンプレに追加した方が良くない?
…と、言いだしっぺが案を…。

Q. Fanの回転数がZeroのままです。
A. Configure→Advancedでマザーのチップセットを選んだ後、
FanN divisorの設定を2,4,8等に増やしてください。
これは、Fan回転検知の「細かさ」の指定で、値を上げると回転検知に
敏感になります。上げすぎると回転数の振れ幅が大きくなります。"

speedfanの回転数が取れません。
状態としては everest のセンサーを選んだ後に speedfan を見ると回転数が 0 => 0 以外になります。

設定します。
記録するかどうかのチェックボックスをチェックしないと再起動で元に戻ります。
変更する数字の量は使用しているマザーボードの種類によって異なります。この例は PX915G の場合です。
設定 => 詳細設定 => Winbord ... ISA =>
FAN1 divisor => 2から8
FAN2 divisor => 2から4
FAN3 divisor => 2から8

表示されるようになりました。
sexe というソフトが簡単に speedfan をサービスに登録できます。
sexe でデスクトップに関する設定をONにするとシャットダウン時に speedfan が初期化できないとエラーを出します。
ログイン前に起動して欲しい場合か、止まったら再実行して欲しい場合か、タスクバーにアイコンが出て欲しくない場合に有用と思われます。

xAPで外部からデータを取得します。
設定 => xAP => Enable

temp1~7, fan1~3 が取得できます。
取得間隔や、電圧の設定は発見できませんでした。

取得する例
http://php.net/manual/ja/function.socket-recvfrom.php
<?php
date_default_timezone_set("Asia/Tokyo");
error_reporting(E_ALL | E_STRICT);
$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
$ret = socket_bind($socket, '127.0.0.1', 3639);
if (!$ret)
{
    trigger_error(E_USER_ERROR, "socket_bind error.\n");
}
while (true)
{
    $from = "";
    $port = 0;
    $ret = socket_recvfrom($socket, $buf, 8192, 0, $from, $port);
    if ($ret > 0)
    {
        print str_repeat("=", 60) . "\n";
        print "\$from = " . $port . "\n";
        print "\$port = " . $port . "\n";
        print "\$buf = \n" . $buf . "\n";
    }
    else
    {
        trigger_error(E_USER_WARNING, "socket_recvfrom error\n");
    }
    print date("Y-m-d H:i:s\n");
}
?>

munin-node の fan の設定は無いようです。
munin-nodewin32/svnroot/munin-node/src/plugins/speedfan/SpeedFanNodePlugin.cpp

確認するコマンド
echo "config speedfan" | nc 192.168.0.90 4949 -q10
echo "fetch speedfan" | nc 192.168.0.90 4949 -q10

2009-12-07

phpのlevenshtein関数のマルチバイト化

この記事はphpのlevenshtein関数のマルチバイト化の例です。
(phpのlevenshtein関数は2バイトの文字を1バイトの文字2つとして扱います)

例えば、UTF8の「あいうえお」と「あいうZお」の距離を3から1に変更する関数です。
渡す文字コードと内部の設定が違うと使えません。
なお、かなり遅く5~50倍かかります。
さらに、長い文字を扱うために、例えば、define("__MB_LEVENSHTEIN__LEVENSHTEIN_MAX_LENGTH", 65535);などを設定してから長い文字で実行すると、さらに遅くなります。

https://docs.google.com/open?id=0BwK7sPpG0c5ZNWNjMzE5OWQtNWZmZC00ZTdlLTgzOTgtMWIwNTNlYmU4NDBi

テスト方法はダウンロードしたファイルと同じディレクトリでコマンドを実行します。
$ php test.mb_levenshtein.php

2011-11-30: JISの場合の不具合を修正。

2009-11-22

phpでswf,flvの動作に関する調査

http://www.ioncannon.net/ruby/108/flash-video-steam-ming-php-ruby/
上記のページのphpソースを動作させてみました。
日本語を表示させるのはOKでした。
ttftofft コマンドが動かないので ttf2fft のコマンドを minghsp 付属のもので利用しました。
flvを固定パスで指定して再生ボタンを押して動きました。
ming 3.0 で動作します。4以上だと 4.0.2 の場合 php では動作せず 4.2 では動作します。
また swf をxmlファイルに変換する swfmill を試したところ、動きました。
ソースで入れる際に修正しました。
swfmill-0.2.12/src/swft/swft_css.cpp
> #include  <string.h>
swfmill-0.2.12/src/swft/swft_import_mp3.cpp
> #include  <string.h>
swf=>xml=>swf といった具合に再変換したところ、違うファイルになることを確認しました。
swf=>xml=>swf=>xml といった具合に再変換したところ、2のxmlファイルは同じであったことを確認しました。

2009-11-21

phpのxdiff

2010-04-02 現在 dev-php5/pecl-xdiff のパッケージがありますので、そちらをお勧めします。
$ sudo ACCEPT_KEYWORDS="~amd64" emerge -av dev-php5/pecl-xdiff
$ eix dev-php5/pecl-xdiff
[D] dev-php5/pecl-xdiff
     Available versions:  ~1.4.1
     Installed versions:  1.4.1(00時20分03秒 2010年04月01日)
     Homepage:            http://pecl.php.net/xdiff
     Description:         PHP extension for generating diff files

phpのxdiffが使いたいため
sudo pecl install xdiff-1.5.1
を行ったところ失敗したため
/etc/portage/package.mask
に
>=sys-devel/libtool-2
を、追記してシステムを更新したところ
バージョンが1.5.26になりました。
また、更新時に依存パッケージが一部再インストールされました。
再度peclを実行したところインストールできたのですが
libtoolのバージョンを下げることはお勧めしません。

手動でファイルを追加しています。
$ find /etc/php/ -name xdiff.ini
/etc/php/cli-php5/ext/xdiff.ini
/etc/php/cli-php5/ext-active/xdiff.ini
/etc/php/apache2-php5/ext/xdiff.ini
/etc/php/apache2-php5/ext-active/xdiff.ini
$ cat /etc/php/apache2-php5/ext/xdiff.ini
extension=xdiff.so