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', 'extensions' => array ( ), 'constants' => array ( ), 'tokens' => array ( ), 'cond_code' => array ( 0 => 1, ), ) |
Note: 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.
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', '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, ), ), ) |
Yes, but its the wrong result. HTML_CSS 1.5.1 require only PHP 4.3.0 to run.
Tip: If you have cond_code offset with a value different than zero, you are almost sure that the version given is wrong.
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); } // ... ?> |
Let's see now how to set the good accuracy with conditional code analysis.