2011-07-11 php
PHPのpreg系関数の正規表現にあんまり大きいパターンを渡すと以下のようなエラーが出る。
Compilation failed: regular expression is too large at offset ...
PCREの場合、ディフォルトだと正規表現には 65539bytes までしか使えない、と記述されている(http://www.pcre.org/pcre.txt の LIMITATIONS を参照)
本当にそうなのか、PHP-5.4(alpha1) で最小限コンパイルして試してみる。
# cd /usr/local/src/ # wget http://snaps.php.net/php5.4-latest.tar.gz # tar -xzvf php5.4-latest.tar.gz # cd php5.4-201107110030 # ./configure # make
./sapi/cli/php に実行ファイルができるので確認する( -n オプションは php.ini 使わないよっていうオプション)
# ./sapi/cli/php -n -v PHP 5.4.0alpha2-dev (cli) (built: Jul 2 2011 07:46:52) Copyright (c) 1997-2011 The PHP Group Zend Engine v2.4.0, Copyright (c) 1998-2011 Zend Technologies
ためしに以下のようなスクリプト作って実行してみる。
<?php
$pattern = '';
for( $i=1; $i<=65539; $i++ ) {
$pattern .= 'a';
if ( preg_match( "/{$pattern}/", 'test' ) === false ) {
exit("error : {$i} bytes");
}
}
実行。32765 bytes でエラーになった。
# ./sapi/cli/php -n _test.php Warning: preg_match(): Compilation failed: regular expression is too large at offset 32765 in /usr/local/src/php5.4-201107110030/_test.php on line 5 error : 32765 bytes
さて、正規表現の実行可能バイト数を増やすには PCRE の link size を増やしてあげる必要がある (http://www.pcre.org/pcre.txt の HANDLING VERY LARGE PATTERNS を参照)
ということで PCRE を --with-link-size=3 指定してソースコンパイル(PHPのコンパイルでつかうだけなのでインストールはしない)
# cd /usr/local/src # wget ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-8.12.tar.gz # tar -xzvf pcre-8.12.tar.gz # cd pcre-8.12/ # ./configure --enable-utf8 --enable-unicode-properties --with-link-size=3 # make # cp -a pcre.h .libs/ ← PHPコンパイル時に pcre.h が必要なので .libs/ にコピーしとく
で、PHPを再コンパイル。pcre ライブラリの場所は上記の .libs を指定。
# cd /usr/local/src/php5.4-201107110030/ # make distclean # ./configure --with-pcre-regex=/usr/local/src/pcre-8.12/.libs # make
さっき作ったPHPスクリプトを実行してみる。
# ./sapi/cli/php -n _test.php
30分たっても結果返ってこなかったので途中であきらめた...
ということでスクリプト内容変えてためしてみる。
<?php
$base = 65539;
var_dump(
pregtest( $base, 1 ),
pregtest( $base, 10 ),
pregtest( $base, 100 ),
pregtest( $base, 150 )
);
function pregtest ( $base, $multiply ) {
$pattern = "/" . str_pad( '', $base * $multiply, 'a' ) . "/";
return preg_match( $pattern, 'test' ) ;
}
実行。65539 * 100 bytes までは実行できた。
# ./sapi/cli/php -n _test.php Warning: preg_match(): Compilation failed: regular expression is too large at offset 9830850 in /usr/local/src/php5.4-201107110030/_test.php on line 12 int(0) int(0) int(0) bool(false)
まあ、6M もあれば充分か。