高度な検出

高度な検出 -- 追加オプションの指定によるデータソースのパース

単一のファイルの検出

あなたのファイルの中で、オプションの条件であって もしそれを満たさなくても動作するという条件があったとしましょう。 たとえばこのようなものです。 「if function_exists { 何かをする } else { 別のことをする }」

こんな場合の対応は簡単です。 要求されている関数がオプションであることを指定すればいいのです。

この "errorHandler.php" というコードを動かすのに必要な PHP のバージョンを調べることを考えてみましょう。 標準では、PCI は PHP 4.3.0 と返します (debug_backtrace が使えるのがバージョン 4.3.0 以降だからです)。ここで、最小バージョンを調べる際に debug_backtrace を無視させるようにすれば、 本当の結果が得られます。

<?php
// ...
if (function_exists('debug_backtrace')) {
    $backtrace = debug_backtrace();
} else {
    $backtrace = false;
}
// ...
?>

ここでは次のようなシンプルな検出スクリプトを使用します。 2 番目のパラメータで渡したオプション配列に注目しましょう (これがミソです)。
<?php
require_once 'PHP/CompatInfo.php';

$source  = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'errorHandler.php';
$options = array('ignore_functions' => array('debug_backtrace'));

$info = new PHP_CompatInfo();
$info->parseFile($source, $options);
// 共通形式のメソッドをつかってもかまいません:  $info->parseData($source, $options);
?>

結果はこのようになります。
array (
  'ignored_files' =>
  array (
  ),
  'ignored_functions' =>
  array (
    0 => 'debug_backtrace',
  ),
  'ignored_extensions' =>
  array (
  ),
  'ignored_constants' =>
  array (
  ),
  'max_version' => '',
  'version' => '4.0.0',
  'extensions' =>
  array (
  ),
  'constants' =>
  array (
  ),
  'tokens' =>
  array (
  ),
  'cond_code' =>
  array (
    0 => 1,
  ),
)
この結果からわかることは、"errorHandler.php" というスクリプトを実行するには PHP 4.0.0 以降が必要で、 条件コード (関数条件 : cond_code = 1) が含まれており、 debug_backtrace 関数は調査範囲からはずしているということです。

注意 バージョン 1.7.0 以降、このような状況を (より簡単に) 処理できるようになり、function_exists で条件指定されているすべての関数を調査範囲からはずせるようになりました。 次の例を参照ください。

もうひとつの方法として ignore_functions_match オプションを使うこともできます。
<?php
require_once 'PHP/CompatInfo.php';

$source  = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'errorHandler.php';
$options = array('ignore_functions_match' => array('function_exists', array('/.*/')));

$info = new PHP_CompatInfo();
$info->parseFile($source, $options);
// 共通形式のメソッドをつかってもかまいません:  $info->parseData($source, $options);
?>

文字列 function_exists を最初のパラメータで指定することで、php の function_exists() による条件の中でだけ登場する関数は無視させるようにしています。

もうひとつ、preg_match 文字列を使用することもできます。 これはより自由な設定をすることができるもので、 そのパターンにマッチする関数は無視させることができます。

一方、2 番目のパラメータの配列では、無視させるパターン (関数名) の一覧を指定します。

ディレクトリの検出

ディレクトリ全体をパースすることは、 それが再帰的であるかどうかにかかわらず、 単一のファイルの PHP バージョンを検出するのと同じようなものです。

次の例では、HTML_CSS 1.5.1 の自動検出を行います。 ご覧のとおり、基本的な検出では正確な結果とはなりません。 しかし、オプションを指定することで正確な結果 (PHP バージョン 4.3.0 以降が必要) が得られます。

まず、アーカイブを http://pear.php.net/package/HTML_CSS/download/1.5.1 からダウンロードしてそれを一時ディレクトリ (今回の例では '/tmp') に展開します。

ここで注目するのは、2 つの重要なファイル CSS.phpCSS/Error.php です。 そこで、PCI に対して examples/ ディレクトリと tests/ ディレクトリは無視するように指示します。

検出用のスクリプトはこのようになります。
<?php
require_once 'PHP/CompatInfo.php';

$source  = '/tmp/HTML_CSS-1.5.1';
$options = array('ignore_dirs' => array('examples', 'tests'));

$info = new PHP_CompatInfo();
$info->parseDir($source, $options);
// you may also use unified method:  $info->parseData($source, $options);
?>

そして、表示される結果はこのようになります。
array (
  'ignored_files' =>
  array (
    0 => '/tmp/HTML_CSS-1.5.1/ChangeLog',
    1 => '/tmp/HTML_CSS-1.5.1/package.xml',
    2 => '/tmp/HTML_CSS-1.5.1/tests/AllTests.php',
    3 => '/tmp/HTML_CSS-1.5.1/tests/HTML_CSS_TestSuite_Bugs.php',
    4 => '/tmp/HTML_CSS-1.5.1/tests/HTML_CSS_TestSuite_Standard.php',
    5 => '/tmp/HTML_CSS-1.5.1/tests/stylesheet.css',
    6 => '/tmp/HTML_CSS-1.5.1/examples/CSS_Advanced.php',
    7 => '/tmp/HTML_CSS-1.5.1/examples/CSS_DisplayOnline.php',
    8 => '/tmp/HTML_CSS-1.5.1/examples/css_errorstack_custom.php',
    9 => '/tmp/HTML_CSS-1.5.1/examples/css_errorstack_logger.php',
    10 => '/tmp/HTML_CSS-1.5.1/examples/css_error_custom.php',
    11 => '/tmp/HTML_CSS-1.5.1/examples/css_error_ignore.php',
    12 => '/tmp/HTML_CSS-1.5.1/examples/css_error_logger.php',
    13 => '/tmp/HTML_CSS-1.5.1/examples/CSS_grepStyles.php',
    14 => '/tmp/HTML_CSS-1.5.1/examples/CSS_InHeader.php',
    15 => '/tmp/HTML_CSS-1.5.1/examples/CSS_Inline.php',
    16 => '/tmp/HTML_CSS-1.5.1/examples/CSS_Logger.php',
    17 => '/tmp/HTML_CSS-1.5.1/examples/CSS_parseData.php',
    18 => '/tmp/HTML_CSS-1.5.1/examples/CSS_req12194_atrule_api.php',
    19 => '/tmp/HTML_CSS-1.5.1/examples/CSS_req12194_atrule_parser.php',
    20 => '/tmp/HTML_CSS-1.5.1/examples/CSS_Stylesheet.php',
    21 => '/tmp/HTML_CSS-1.5.1/examples/CSS_validate.php',
  ),
  'ignored_functions' =>
  array (
  ),
  'ignored_extensions' =>
  array (
  ),
  'ignored_constants' =>
  array (
  ),
  'max_version' => '',
  'version' => '5.0.0',
  'extensions' =>
  array (
    0 => 'date',
    1 => 'pcre',
  ),
  'constants' =>
  array (
  ),
  'tokens' =>
  array (
  ),
  'cond_code' =>
  array (
    0 => 1,
  ),
  '/tmp/HTML_CSS-1.5.1/CSS.php' =>
  array (
    'ignored_functions' =>
    array (
    ),
    'ignored_extensions' =>
    array (
    ),
    'ignored_constants' =>
    array (
    ),
    'max_version' => '',
    'version' => '5.0.0',
    'extensions' =>
    array (
      0 => 'date',
      1 => 'pcre',
    ),
    'constants' =>
    array (
    ),
    'tokens' =>
    array (
    ),
    'cond_code' =>
    array (
      0 => 1,
    ),
  ),
  '/tmp/HTML_CSS-1.5.1/CSS/Error.php' =>
  array (
    'ignored_functions' =>
    array (
    ),
    'ignored_extensions' =>
    array (
    ),
    'ignored_constants' =>
    array (
    ),
    'max_version' => '',
    'version' => '4.3.0',
    'extensions' =>
    array (
      0 => 'date',
    ),
    'constants' =>
    array (
    ),
    'tokens' =>
    array (
    ),
    'cond_code' =>
    array (
      0 => 0,
    ),
  ),
)
このパッケージを実行するには PHP 5.0.0 以降が必要であるように見えます。

でも、この結果は間違っています。 HTML_CSS 1.5.1 は PHP 4.3.0 以降で動作するのです。

ティップ cond_code オフセットをゼロ以外の値にすれば、 このバージョンが間違っていることがよくわかることでしょう。

では、なぜこのような結果になったのでしょう?

パッケージ PEAR::HTML_CSS 1.5.1 は、条件コードを使用して PHP の過去のバージョンで使用できない関数のエミュレートを行っています。 HTML_CSS では、php の関数 function_exists() を用いてその代替実装を行っているのです。

実際のソースコード (CSS.php) を見てみましょう。
<?php
// ...
    if (function_exists('file_put_contents')) {
        file_put_contents($filename, $this->toString());
    } else {
        $file = fopen($filename, 'wb');
        fwrite($file, $this->toString());
        fclose($file);
    }
// ...
?>
PHP の関数 file_put_contents() はバージョン 5.0.0 以降で使用可能なものです。 これが原因で間違った結果が表示されたのです。 しかし、このような条件コードを考慮して処理を行うこともできます。

それでは、条件コードをより正確に処理できるようにする方法を見ていきましょう。