Advanced detection

Advanced detection – parse data source with additional options

Detection of a single file

If your file implement code condition that is optional and don't break main goal, such as, for example : if function_exists then I do something, else I do something else.

Solution is very easy: You have to specify what function required should be considered as optional.

Suppose we have to detect which PHP version we need to run this chunk of script named "errorHandler.php". With standard behavior, PCI returns PHP 4.3.0 (because debug_backtrace came with version 4.3.0). So, if we ignore function debug_backtrace to find out the minimum version, we will get the real and true result.

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

We will use another very simple detection script. Have a look on options array given as second parameter (here is the magic).

<?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);
// you may also use unified method:  $info->parseData($source, $options);
?>

And displayed results are :

array (
  'ignored_files' =>
  array (
  ),
  'ignored_functions' =>
  array (
    0 => 'debug_backtrace',
  ),
  'ignored_extensions' =>
  array (
  ),
  'ignored_constants' =>
  array (
  ),
  'max_version' => '',
  'version' => '4.0.0',
  'classes' =>
  array (
  ),
  'extensions' =>
  array (
  ),
  'constants' =>
  array (
    0 => 'FALSE',
  ),
  'tokens' =>
  array (
  ),
  'cond_code' =>
  array (
    0 => 1,
  ),
)
    

that means chunk of script named "errorHandler.php" need PHP 4.0.0, have condition code (function condition : cond_code = 1), and php debug_backtrace function was excluded from scope.

Since version 1.7.0, you may catch this situation (more easily), and exclude from scope all functions that are conditionned by a function_exists. See example that follow.

Other alternative is to use ignore_functions_match option.

<?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);
// you may also use unified method:  $info->parseData($source, $options);;
?>

The string function_exists as first parameter, tell to ignore function(s) that match only conditionnal code with php function_exists().

The other possibility is string preg_match, that give more freedom, and catch function that match the pattern (without condition).

While array as second parameter, gave a list of pattern (function name) that must be catch and ignored.

Detection of a directory

Parsing a full directory, recursively or not, is no more difficult than detect PHP version of a single file.

This new example is based on auto detection of HTML_CSS 1.5.1 distribution. As we will see, basic detection is not accurate as it should be. But with an option we can get the real result (PHP minimum = 4.3.0).

First begin to download the archive from http://pear.php.net/package/HTML_CSS/download/1.5.1 and extract the full contents to a temporary directory (in our example its '/tmp')

We will focus on two important files: CSS.php and CSS/Error.php. So we will indicate to PCI to ignore examples/, and tests/ directories.

Here is the detection script:

<?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);
?>

And displayed results are :

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',
  'classes' =>
  array (
    0 => 'Services_W3C_CSSValidator',
  ),
  'extensions' =>
  array (
    0 => 'date',
    1 => 'pcre',
  ),
  'constants' =>
  array (
    0 => 'E_USER_ERROR',
    1 => 'E_USER_NOTICE',
    2 => 'E_USER_WARNING',
    3 => 'FALSE',
    4 => 'NULL',
    5 => 'PHP_OS',
    6 => 'PREG_SET_ORDER',
    7 => 'PREG_SPLIT_DELIM_CAPTURE',
    8 => 'TRUE',
  ),
  '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',
    'classes' =>
    array (
      0 => 'Services_W3C_CSSValidator',
    ),
    'extensions' =>
    array (
      0 => 'date',
      1 => 'pcre',
    ),
    'constants' =>
    array (
      0 => 'FALSE',
      1 => 'NULL',
      2 => 'PHP_OS',
      3 => 'PREG_SET_ORDER',
      4 => 'PREG_SPLIT_DELIM_CAPTURE',
      5 => 'TRUE',
    ),
    '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',
    'classes' =>
    array (
    ),
    'extensions' =>
    array (
      0 => 'date',
    ),
    'constants' =>
    array (
      0 => 'E_USER_ERROR',
      1 => 'E_USER_NOTICE',
      2 => 'E_USER_WARNING',
      3 => 'FALSE',
      4 => 'NULL',
      5 => 'TRUE',
    ),
    'tokens' =>
    array (
    ),
    'cond_code' =>
    array (
      0 => 0,
    ),
  ),
)
    

As we can read, PHP 5.0.0 is required to run the package.

Yes, but its the wrong result. HTML_CSS 1.5.1 require only PHP 4.3.0 to run.

If you have cond_code offset with a value different than zero, you are almost sure that the version given is wrong.

So why we get such result ?

Package PEAR::HTML_CSS 1.5.1 as many application/extension use conditional code to emulate function that are unavailable for previous PHP versions. Its means that HTML_CSS use the php function function_exists() to implement such alternative.

To illustrate our purpose, we can find in source code (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 function file_put_contents() came with version 5.0.0; That is the reason of wrong parsing result. But we can catch such conditional code.

Let's see now how to set the good accuracy with conditional code analysis.