犬ターネット

PHP の preg 系関数に使える正規表現のサイズ

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),
  pregtest$base10 ),
  pregtest$base100 ),
  pregtest$base150 )
);

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 もあれば充分か。


んべむいお

mgng2(森ボーイ)の神ポストまとめ