Source for file Parser.php
Documentation is available at Parser.php
* Copyright (c) 2008, Davey Shafik <davey@php.net>
* Laurent Laville <pear@laurent-laville.org>
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the authors nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
* @package PHP_CompatInfo
* @author Davey Shafik <davey@php.net>
* @author Laurent Laville <pear@laurent-laville.org>
* @license http://www.opensource.org/licenses/bsd-license.php BSD
* @version CVS: $Id: Parser.php,v 1.15 2008/11/29 10:00:46 farell Exp $
* @link http://pear.php.net/package/PHP_CompatInfo
* @since File available since Release 1.8.0b2
require_once 'Event/Dispatcher.php';
require_once 'File/Find.php';
* An array of class init versions and extension
require_once 'PHP/CompatInfo/class_array.php';
* An array of function init versions and extension
require_once 'PHP/CompatInfo/func_array.php';
* An array of constants and their init versions
require_once 'PHP/CompatInfo/const_array.php';
* An abstract base class for CompatInfo renderers
require_once 'PHP/CompatInfo/Renderer.php';
* Event name of parsing data source start process
define('PHP_COMPATINFO_EVENT_AUDITSTARTED', 'auditStarted');
* Event name of parsing data source end process
define('PHP_COMPATINFO_EVENT_AUDITFINISHED', 'auditFinished');
* Event name of parsing a file start process
define('PHP_COMPATINFO_EVENT_FILESTARTED', 'fileStarted');
* Event name of parsing a file end process
define('PHP_COMPATINFO_EVENT_FILEFINISHED', 'fileFinished');
* Event name of parsing a file start process
define('PHP_COMPATINFO_EVENT_CODESTARTED', 'codeStarted');
* Event name of parsing a file end process
define('PHP_COMPATINFO_EVENT_CODEFINISHED', 'codeFinished');
* This class is the model in the MVC design pattern of API 1.8.0 (since beta 2)
* @package PHP_CompatInfo
* @author Laurent Laville <pear@laurent-laville.org>
* @license http://www.opensource.org/licenses/bsd-license.php BSD
* @version Release: 1.9.0b1
* @link http://pear.php.net/package/PHP_CompatInfo
* @since Class available since Release 1.8.0b2
* Instance of concrete renderer used to show parse results
* Stores the event dispatcher which handles notifications
* Count the number of observer registered.
* The Event_Dispatcher will be add on first observer registration, and
* will be removed with the last observer.
* @var string Earliest version of PHP to use
* @var string Last version of PHP to use
* @var array Parsing options
* @var array Directory list found when parsing data source
* @var array List of files ignored when parsing data source
* @var array Result of the latest data source parsing
* Class constructor (ZE1) for PHP4
* @since version 1.8.0b2 (2008-06-03)
* Class constructor (ZE2) for PHP5+
* @since version 1.8.0b2 (2008-06-03)
'file_ext' => array ('php', 'php4', 'inc', 'phtml'),
'ignore_files' => array (),
* Set up driver to be used
* Set up driver to be used, dependant on specified type.
* @param string $type Name the type of driver (html, text...)
* @param array $conf A hash containing any additional configuration
* @since version 1.8.0b2 (2008-06-03)
* Registers a new listener
* Registers a new listener with the given criteria.
* @param mixed $callback A PHP callback
* @param string $nName (optional) Expected notification name
* @since version 1.8.0b2 (2008-06-03)
function addListener($callback, $nName = EVENT_DISPATCHER_GLOBAL )
$this->dispatcher = & Event_Dispatcher ::getInstance ();
// $this->dispatcher->setNotificationClass('PHP_CompatInfo_Audit');
$this->dispatcher->addObserver ($callback, $nName);
* Removes a registered listener
* Removes a registered listener that correspond to the given criteria.
* @param mixed $callback A PHP callback
* @param string $nName (optional) Expected notification name
* @return bool True if listener was removed, false otherwise.
* @since version 1.8.0b2 (2008-06-03)
$result = $this->dispatcher->removeObserver ($callback, $nName);
if ($this->_observerCount == 0 ) {
* Post a new notification to all listeners registered.
* This notification occured only if a dispatcher exists. That means if
* at least one listener was registered.
* @param string $event Name of the notification handler
* @param array $info (optional) Additional information about the notification
* @since version 1.8.0b2 (2008-06-03)
* Load components list for a PHP version or subset
* @param string $min PHP minimal version
* @param string|boolean$max (optional) PHP maximal version
* @param boolean $include_const (optional) include constants list
* @param boolean $groupby_vers (optional) give initial php version
* of function or constant
* @return array An array of php function/constant names history
* @since version 1.2.0 (2006-08-23)
$include_const = false , $groupby_vers = false )
foreach ($GLOBALS['_PHP_COMPATINFO_FUNCS'] as $func => $arr) {
if (isset ($arr['pecl']) && $arr['pecl'] === true ) {
$end = (isset ($arr['end'])) ? $arr['end'] : $vmin;
if ($groupby_vers === true ) {
if ($groupby_vers === true ) {
if ($include_const === true ) {
$keys = array ('functions' => $keys, 'constants' => array ());
foreach ($GLOBALS['_PHP_COMPATINFO_CONST'] as $const => $arr) {
$end = (isset ($arr['end'])) ? $arr['end'] : $vmin;
if ($groupby_vers === true ) {
$keys['constants'][$vmin][] = $arr['name'];
$keys['constants'][] = $arr['name'];
if ($groupby_vers === true ) {
$keys['constants'][$vmin][] = $arr['name'];
$keys['constants'][] = $arr['name'];
ksort($keys['constants']);
* Returns list of directory parsed
* Returns list of directory parsed, depending of restrictive parser options.
* @param mixed $dir The directory name
* @param array $options An array of parser options. See parseData() method.
* @return array list of directories that should be parsed
* @since version 1.8.0b2 (2008-06-03)
* Returns list of files parsed
* Returns list of files parsed, depending of restrictive parser options.
* @param mixed $dir The directory name where to look files
* @param array $options An array of parser options. See parseData() method.
* @return array list of files that should be parsed
* @since version 1.8.0b2 (2008-06-03)
$options['file_ext'] = array_map('strtolower', $options['file_ext']);
if ($dir{strlen($dir)-1 } == '/' || $dir{strlen($dir)-1 } == '\\') {
// use system directory separator rather than forward slash by default
$ff->dirsep = DIRECTORY_SEPARATOR;
// get directory list that should be ignored from scope
if (count($options['ignore_dirs']) > 0 ) {
foreach ($options['ignore_dirs'] as $cond) {
$dirs = $ff->search ('`'. $cond. '`', $dir, 'perl',
// get file list that should be ignored from scope
if (count($options['ignore_files']) > 0 ) {
foreach ($options['ignore_files'] as $cond) {
$files = $ff->search ('`'. $cond. '`', $dir, 'perl',
list ($directories, $files) = $ff->maptree ($dir);
foreach ($files as $file) {
if ($options['recurse_dir'] == false
&& $file_info['dirname'] != $dir) {
if (in_array($file_info['dirname'], $ignore_dirs)) {
} elseif (in_array($file, $ignore_files)) {
if (isset ($file_info['extension'])
* Returns list of files ignored
* Returns list of files ignored while parsing directories
* @return array or false on error
* @since version 1.8.0b2 (2008-06-03)
* Returns the latest parse data source version
* Returns the latest parse data source version, minimum and/or maximum
* @param mixed $file (optional) A specific filename or not (false)
* @param bool $max (optional) Level with or without contextual data
* @return mixed Null on error or if there were no previous data parsing
* @since version 1.9.0b1 (2008-11-30)
$key = ($max === true ) ? 'max_version' : 'version';
// no code analysis found
} elseif ($file === false ) {
* Returns the latest parse data source classes declared
* Returns the latest parse data source classes declared (internal or
* @param mixed $file (optional) A specific filename or not (false)
* @return mixed Null on error or if there were no previous data parsing
* @since version 1.9.0b1 (2008-11-30)
// no code analysis found
} elseif ($file === false ) {
* Returns the latest parse data source functions declared
* Returns the latest parse data source functions declared (internal or
* @param mixed $file (optional) A specific filename or not (false)
* @return mixed Null on error or if there were no previous data parsing
* @since version 1.9.0b1 (2008-11-30)
// no code analysis found
} elseif ($file === false ) {
* Returns the latest parse data source extensions used
* Returns the latest parse data source extensions used
* @param mixed $file (optional) A specific filename or not (false)
* @return mixed Null on error or if there were no previous data parsing
* @since version 1.9.0b1 (2008-11-30)
// no code analysis found
} elseif ($file === false ) {
* Returns the latest parse data source constants declared
* Returns the latest parse data source constants declared (internal or
* @param mixed $file (optional) A specific filename or not (false)
* @return mixed Null on error or if there were no previous data parsing
* @since version 1.9.0b1 (2008-11-30)
// no code analysis found
} elseif ($file === false ) {
* Returns the latest parse data source tokens declared
* Returns the latest parse data source PHP5+ tokens declared
* @param mixed $file (optional) A specific filename or not (false)
* @return mixed Null on error or if there were no previous data parsing
* @since version 1.9.0b1 (2008-11-30)
// no code analysis found
} elseif ($file === false ) {
* Returns the latest parse data source conditions
* Returns the latest parse data source conditions, with or without
* @param mixed $file (optional) A specific filename or not (false)
* @param bool $levelOnly (optional) Level with or without contextual data
* @return mixed Null on error or if there were no previous data parsing
* @since version 1.9.0b1 (2008-11-30)
// no code analysis found
} elseif ($file === false ) {
if (is_array($conditions) && $levelOnly === true ) {
$conditions = $conditions[0 ];
* Parse a data source with auto detect ability. This data source, may be
* one of these follows: a directory, a file, a string (chunk of code),
* an array of multiple origin.
* Each of five parsing functions support common and specifics options.
* - 'debug' Contains a boolean to control whether
* - 'ignore_functions' Contains an array of functions to ignore
* when calculating the version needed.
* - 'ignore_constants' Contains an array of constants to ignore
* when calculating the version needed.
* - 'ignore_extensions' Contains an array of php extensions to ignore
* when calculating the version needed.
* - 'ignore_versions' Contains an array of php versions to ignore
* when calculating the version needed.
* - 'ignore_functions_match' Contains an array of function patterns to ignore
* when calculating the version needed.
* - 'ignore_extensions_match' Contains an array of extension patterns to ignore
* when calculating the version needed.
* - 'ignore_constants_match' Contains an array of constant patterns to ignore
* when calculating the version needed.
* * parseArray, parseDir|parseFolder, specific options :
* - 'file_ext' Contains an array of file extensions to parse
* for PHP code. Default: php, php4, inc, phtml
* - 'ignore_files' Contains an array of files to ignore.
* File names are case insensitive.
* * parseArray specific options :
* - 'is_string' Contains a boolean which says if the array values
* are strings or file names.
* * parseDir|parseFolder specific options :
* - 'recurse_dir' Boolean on whether to recursively find files
* - 'ignore_dirs' Contains an array of directories to ignore.
* Directory names are case insensitive.
* @param mixed $dataSource The data source (may be file, dir, string, or array)
* @param array $options An array of options. See above.
* @return array or false on error
* @since version 1.8.0b2 (2008-06-03)
function parseData($dataSource, $options = array ())
// - when array source with mixed content incompatible
// - if all directories are not readable
// - if data source invalid type: other than file, directory, string
if ($dataType == 'string' || $dataType == 'array') {
} elseif (is_dir($dataSource)) {
$dataSource = array ($dataSource);
$dataSource = array ($dataSource);
} elseif (substr($dataSource, 0 , 5 ) == '<?php') {
array ('is_string' => true ));
$dataSource = array ($dataSource);
// directory or file are misspelled
$dataSource = $this->_validateDataSource ($dataSource,
$dataCount = count($dataSource);
$this->dataSource = array ('dataSource' => $dataSource,
'dataCount' => $dataCount);
array ('parseOptions' => $this->options));
// notify all observers that parsing data source begin
$parseData = $this->_parseArray ($dataSource, $this->options);
$parseData = $this->_parseString ($dataSource, $this->options);
$parseData = $this->_parseFile ($dataSource, $this->options);
$parseData = $this->_parseDir ($dataSource, $this->options);
// notify all observers that parsing data source is over
* Validate content of data source
* Validate content of data source list, before parsing each source
* @param mixed $dataSource The data source (may be file, dir, or string)
* @param array $options Parser options (see parseData() method for details)
* @return array empty array on error
* @since version 1.8.0b3 (2008-06-07)
function _validateDataSource ($dataSource, $options = array ())
* Array by default expect to contains list of files and/or directories.
* If you want a list of chunk of code (strings), 'is_string' option
foreach ($dataSource as $source) {
if ($options['is_string'] === true ) {
* One of items is not a string (chunk of code). All
* data sources parsing are stopped and considered as invalid.
* One of items is not a valid file or directory. All
* data sources parsing are stopped and considered as invalid.
* Parse an Array of Files
* You can parse an array of Files or Strings, to parse
* strings, $options['is_string'] must be set to true
* @param array $dataSource Array of file &| directory names or code strings
* @param array $options Parser options (see parseData() method for details)
* @return array or false on error
* @since version 0.7.0 (2004-03-09)
function _parseArray ($dataSource, $options = array ())
// Each data source have been checked before (see _validateDataSource() )
$parseData = $this->_parseDir ($dataSource, $options);
$parseData = $this->_parseString ($dataSource, $options);
* Parse a string for its compatibility info.
* @param array $strings PHP Code to parse
* @param array $options Parser options (see parseData() method for details)
* @return array or false on error
* @since version 0.7.0 (2004-03-09)
function _parseString ($strings, $options = array ())
$results = $this->_parseElements ($strings, $options);
* Parse a single file for its compatibility info.
* @param string $file File to parse
* @param array $options Parser options (see parseData() method for details)
* @return array or false on error
* @since version 0.7.0 (2004-03-09)
function _parseFile ($file, $options = array ())
$results = $this->_parseElements ($file, $options);
* Parse a directory recursively for its compatibility info
* @param array $files Files list of folder to parse
* @param array $options Parser options (see parseData() method for details)
* @return array or false on error
* @since version 0.8.0 (2004-04-22)
function _parseDir ($files, $options = array ())
$results = $this->_parseElements ($files, $options);
* Parse a list of elements
* Parse a list of directory|file elements, or chunk of code (strings)
* @param array $elements Array of file &| directory names or code strings
* @param array $options Parser options (see parseData() method for details)
* @since version 1.8.0b3 (2008-06-07)
* @see _parseString(), _parseDir()
function _parseElements ($elements, $options = array ())
$all_functions = array ();
$ignored_functions = array ();
$ignored_extensions = array ();
$ignored_constants = array ();
$function_exists = array ();
$extension_loaded = array ();
foreach ($elements as $p => $element) {
if (in_array($element, $options['ignore_files'])) {
= array ('filename' => $element, 'fileindex' => $index);
$tokens_list = $this->_tokenize ($element);
$files_parsed[$kfile] = $this->_parseTokens ($tokens_list, $options);
= array ('stringdata' => $element, 'stringindex' => $index);
$tokens_list = $this->_tokenize ($element, true );
$kfile = 'string_' . $index;
$files_parsed[$kfile] = $this->_parseTokens ($tokens_list, $options);
foreach ($files_parsed as $fn => $file) {
$latest_version = $file['version'];
if ($file['max_version'] != '') {
if ($earliest_version == '' || $cmp === 1 ) {
$earliest_version = $file['max_version'];
foreach ($file['classes'] as $class) {
foreach ($file['functions'] as $func) {
foreach ($file['extensions'] as $ext) {
foreach ($file['constants'] as $const) {
foreach ($file['tokens'] as $token) {
foreach ($file['ignored_functions'] as $if) {
if (!in_array($if, $ignored_functions)) {
$ignored_functions[] = $if;
foreach ($file['ignored_extensions'] as $ie) {
if (!in_array($ie, $ignored_extensions)) {
$ignored_extensions[] = $ie;
foreach ($file['ignored_constants'] as $ic) {
if (!in_array($ic, $ignored_constants)) {
$ignored_constants[] = $ic;
foreach ($file['cond_code'][1 ][0 ] as $ccf) {
if (!in_array($ccf, $function_exists)) {
$function_exists[] = $ccf;
foreach ($file['cond_code'][1 ][1 ] as $cce) {
if (!in_array($cce, $extension_loaded)) {
$extension_loaded[] = $cce;
foreach ($file['cond_code'][1 ][2 ] as $ccc) {
if ($options['debug'] === false ) {
unset ($files_parsed[$fn]['cond_code'][1 ]);
unset ($file['ignored_functions']);
unset ($file['ignored_extensions']);
unset ($file['ignored_constants']);
unset ($file['max_version']);
unset ($file['functions']);
unset ($file['extensions']);
unset ($file['constants']);
unset ($file['cond_code']);
foreach ($file as $version => $functions) {
// extra information available only when debug mode is on
if (isset ($all_functions[$version])) {
foreach ($functions as $func) {
$all_functions[$version][] = $func;
$all_functions[$version] = $functions;
if (count($files_parsed) == 0 ) {
if (count($function_exists) > 0 ) {
if (count($extension_loaded) > 0 ) {
if (count($defined) > 0 ) {
if ($options['debug'] === false ) {
$cond_code = array ($cond_code);
$cond_code = array ($cond_code, array ($function_exists,
sort($ignored_functions);
sort($ignored_extensions);
sort($ignored_constants);
'ignored_functions' => $ignored_functions,
'ignored_extensions' => $ignored_extensions,
'ignored_constants' => $ignored_constants,
'max_version' => $earliest_version,
'version' => $latest_version,
'functions' => $functions,
'extensions' => $extensions,
'constants' => $constants,
'cond_code' => $cond_code);
if (count($files_parsed) == 1 ) {
if ($options['debug'] === false ) {
$files_parsed[$kfile], $all_functions);
if ($options['debug'] === false ) {
$parseData = array_merge($main_info, $all_functions, $files_parsed);
* @param string $input Filename or PHP code
* @param boolean $is_string Whether or note the input is a string
* @param boolean $debug add token names for human read
* @since version 0.7.0 (2004-03-09)
function _tokenize ($input, $is_string = false , $debug = false )
if ($is_string === false ) {
foreach ($tokens as $token) {
* The tokens are those returned by token_get_all() which is nicely
* wrapped in PHP_CompatInfo::_tokenize
* @param array $tokens Array of PHP Tokens
* @param boolean $options Show Extra Output
* @since version 0.7.0 (2004-03-09)
function _parseTokens ($tokens, $options)
$functions_version = array ();
$constant_names = array ();
$ignore_functions = array ();
$ignored_functions = array ();
$ignore_extensions = array ();
$ignored_extensions = array ();
$ignore_constants = array ();
$ignored_constants = array ();
$function_exists = array ();
$extension_loaded = array ();
if (isset ($options['ignore_constants'])) {
$options['ignore_constants']
= array_map('strtoupper', $options['ignore_constants']);
$options['ignore_constants'] = array ();
if (isset ($options['ignore_extensions'])) {
$options['ignore_extensions']
= array_map('strtolower', $options['ignore_extensions']);
$options['ignore_extensions'] = array ();
if (isset ($options['ignore_versions'][0 ])) {
$min_ver = $options['ignore_versions'][0 ];
if (isset ($options['ignore_versions'][1 ])) {
$max_ver = $options['ignore_versions'][1 ];
if (isset ($options['ignore_functions_match'])) {
list ($ifm_compare, $ifm_patterns) = $options['ignore_functions_match'];
if (isset ($options['ignore_extensions_match'])) {
list ($iem_compare, $iem_patterns) = $options['ignore_extensions_match'];
if (isset ($options['ignore_constants_match'])) {
list ($icm_compare, $icm_patterns) = $options['ignore_constants_match'];
$token_count = sizeof($tokens);
while ($i < $token_count) {
if ($this->_isToken ($tokens[$i], 'T_FUNCTION')) {
while ($found_func == false ) {
if ($this->_isToken ($tokens[$i], 'T_STRING')) {
if ($found_class === false
// Try to detect PHP method chaining implementation
if ($this->_isToken ($tokens[$i], 'T_VARIABLE')
&& $this->_isToken ($tokens[$i+1 ], 'T_OBJECT_OPERATOR')
&& $this->_isToken ($tokens[$i+2 ], 'T_STRING')
&& $this->_isToken ($tokens[$i+3 ], '(')) {
$php5_method_chaining = false;
while (((!is_array($tokens[$i]) && $tokens[$i] == ';') === false )
&& (!$this->_isToken ($tokens[$i], 'T_CLOSE_TAG'))
if ((($this->_isToken ($tokens[$i], ')'))
|| ($this->_isToken ($tokens[$i], 'T_WHITESPACE')))
&& $this->_isToken ($tokens[$i+1 ], 'T_OBJECT_OPERATOR')) {
$php5_method_chaining = true;
// Compare "ignore_functions_match" pre-condition
if (strcasecmp('preg_match', $ifm_compare) != 0 ) {
// Try to catch function_exists() condition
if ($this->_isToken ($tokens[$i], 'T_STRING')
&& (strcasecmp($tokens[$i][1 ], $ifm_compare) == 0 )) {
while ((!$this->_isToken ($tokens[$i],
'T_CONSTANT_ENCAPSED_STRING'))) {
$func = trim($tokens[$i][1 ], "'");
* try if function_exists()
* match one or more pattern condition
foreach ($ifm_patterns as $pattern) {
$ignore_functions[] = $func;
// Compare "ignore_extensions_match" pre-condition
if (strcasecmp('preg_match', $iem_compare) != 0 ) {
// Try to catch extension_loaded() condition
if ($this->_isToken ($tokens[$i], 'T_STRING')
&& (strcasecmp($tokens[$i][1 ], $iem_compare) == 0 )) {
while ((!$this->_isToken ($tokens[$i],
'T_CONSTANT_ENCAPSED_STRING'))) {
$ext = trim($tokens[$i][1 ], "'");
* try if extension_loaded()
* match one or more pattern condition
foreach ($iem_patterns as $pattern) {
$ignore_extensions[] = $ext;
// Compare "ignore_constants_match" pre-condition
if (strcasecmp('preg_match', $icm_compare) != 0 ) {
// Try to catch defined() condition
if ($this->_isToken ($tokens[$i], 'T_STRING')
&& (strcasecmp($tokens[$i][1 ], $icm_compare) == 0 )) {
while ((!$this->_isToken ($tokens[$i],
'T_CONSTANT_ENCAPSED_STRING'))) {
$cst = trim($tokens[$i][1 ], "'");
* match one or more pattern condition
foreach ($icm_patterns as $pattern) {
$ignore_constants[] = $cst;
// try to detect class instantiation
if ($this->_isToken ($tokens[$i], 'T_STRING')
&& (isset ($tokens[$i-2 ]))
&& $this->_isToken ($tokens[$i-2 ], 'T_NEW')) {
$classes[] = $tokens[$i][1 ];
if ($this->_isToken ($tokens[$i], 'T_STRING')
&& (isset ($tokens[$i+1 ]))
&& $this->_isToken ($tokens[$i+1 ], '(')) {
&& !$this->_isToken ($tokens[$i-1 ], 'T_DOUBLE_COLON')
&& !$this->_isToken ($tokens[$i-1 ], 'T_OBJECT_OPERATOR')) {
&& $this->_isToken ($tokens[$i-2 ], 'T_FUNCTION')) {
// its a function declaration
if ($is_function == true || !is_array($tokens[$i-1 ])) {
// try to detect condition function_exists()
if ($this->_isToken ($tokens[$i], 'T_STRING')
&& (strcasecmp($tokens[$i][1 ], 'function_exists') == 0 )) {
while ((!$this->_isToken ($tokens[$j], ')'))) {
if ($this->_isToken ($tokens[$j], 'T_CONSTANT_ENCAPSED_STRING')) {
$t_string = $tokens[$j][1 ];
$t_string = trim($t_string, "'");
$t_string = trim($t_string, '"');
$function_exists[] = $t_string;
// try to detect condition extension_loaded()
if ($this->_isToken ($tokens[$i], 'T_STRING')
&& (strcasecmp($tokens[$i][1 ], 'extension_loaded') == 0 )) {
while ((!$this->_isToken ($tokens[$j], ')'))) {
if ($this->_isToken ($tokens[$j], 'T_CONSTANT_ENCAPSED_STRING')) {
$t_string = $tokens[$j][1 ];
$t_string = trim($t_string, "'");
$t_string = trim($t_string, '"');
$extension_loaded[] = $t_string;
// try to detect condition defined()
if ($this->_isToken ($tokens[$i], 'T_STRING')
&& (strcasecmp($tokens[$i][1 ], 'defined') == 0 )) {
while ((!$this->_isToken ($tokens[$j], ')'))) {
if ($this->_isToken ($tokens[$j], 'T_CONSTANT_ENCAPSED_STRING')) {
$t_string = $tokens[$j][1 ];
$t_string = trim($t_string, "'");
$t_string = trim($t_string, '"');
// try to detect beginning of a class
if ($this->_isToken ($tokens[$i], 'T_CLASS')) {
// build contents one time only (static variable)
$akeys = array_keys($GLOBALS['_PHP_COMPATINFO_CONST']);
if ($this->_isToken ($tokens[$i], 'T_ENCAPSED_AND_WHITESPACE')) {
// PHP 5 constant tokens found into a string
// Compare "ignore_constants_match" free condition
if (strcasecmp('preg_match', $icm_compare) == 0 ) {
* match one or more pattern condition
foreach ($icm_patterns as $pattern) {
$init = $GLOBALS['_PHP_COMPATINFO_CONST'][$const]['init'];
|| in_array($const, $options['ignore_constants'])
$ignored_constants[] = $const;
if (isset ($options['ignore_functions'])) {
$options['ignore_functions']
= array_map('strtolower', $options['ignore_functions']);
$options['ignore_functions'] = array ();
if (count($ignore_functions) > 0 ) {
$ignore_functions = array_map('strtolower', $ignore_functions);
$options['ignore_functions']
= array_merge($options['ignore_functions'], $ignore_functions);
$options['ignore_functions']
if (count($ignore_extensions) > 0 ) {
$ignore_extensions = array_map('strtolower', $ignore_extensions);
$options['ignore_extensions']
= array_merge($options['ignore_extensions'], $ignore_extensions);
$options['ignore_extensions']
foreach ($classes as $name) {
if (!isset ($GLOBALS['_PHP_COMPATINFO_CLASS'][$name])) {
continue; // skip this unknown class
$class = $GLOBALS['_PHP_COMPATINFO_CLASS'][$name];
continue; // skip this class version
$latest_version = $class['init'];
if ($earliest_version == '' || $cmp === 1 ) {
$earliest_version = $class['end'];
// this class depends of an extension
$extensions[] = $class['ext'];
foreach ($functions as $name) {
if (!isset ($GLOBALS['_PHP_COMPATINFO_FUNCS'][$name])) {
continue; // skip this unknown function
$func = $GLOBALS['_PHP_COMPATINFO_FUNCS'][$name];
// retrieve if available the extension name
if ((isset ($func['ext']))
&& ($func['ext'] != 'ext_standard')
&& ($func['ext'] != 'zend')) {
if ($func['pecl'] === false ) {
$extension = substr($func['ext'], 4 );
if ($extension{0 } == '_') {
$extension = $func['ext'];
$extension = $func['ext'];
// Compare "ignore_functions_match" free condition
if (strcasecmp('preg_match', $ifm_compare) == 0 ) {
* match one or more pattern condition
foreach ($ifm_patterns as $pattern) {
&& (!in_array($name, $options['ignore_functions']))
&& ($ifm_preg_match === false )) {
if ($extension && !in_array($extension, $extensions)) {
$extensions[] = substr($func['ext'], 0 , 4 ) == 'ext_'
? $extension : $func['ext'];
// Compare "ignore_extensions_match" free condition
if (strcasecmp('preg_match', $iem_compare) == 0 ) {
* match one or more pattern condition
foreach ($iem_patterns as $pattern) {
&& (in_array($extension, $options['ignore_extensions'])
if (!in_array($extension, $ignored_extensions)) {
// extension is ignored (only once)
$ignored_extensions[] = $extension;
// all extension functions are also ignored
$ignored_functions[] = $name;
continue; // skip this extension function
continue; // skip this function version
if ($options['debug'] == true ) {
$functions_version[$func['init']][] = array (
'extension' => substr($func['ext'], 0 , 4 ) == 'ext_'
? $extension : $func['ext'],
|| (isset ($func['pecl']) && $func['pecl'] === false ) ) {
$latest_version = $func['init'];
if ($earliest_version == '' || $cmp === 1 ) {
$earliest_version = $func['end'];
$ignored_functions[] = $name;
foreach ($constants as $constant) {
$const = $GLOBALS['_PHP_COMPATINFO_CONST'][$constant];
continue; // skip this constant version
if (!in_array($constant, $ignored_constants)) {
$latest_version = $const['init'];
if ($earliest_version == '' || $cmp === 1 ) {
$earliest_version = $const['end'];
if (!in_array($const['name'], $constant_names)) {
// split PHP5 tokens and pure PHP constants
if ($const['name'] == strtolower($const['name'])) {
$token_names[] = $const['name'];
$constant_names[] = $const['name'];
if (isset ($php5_method_chaining)
&& $php5_method_chaining === true
// when PHP Method chaining is detected, only available for PHP 5
$latest_version = '5.0.0';
ksort($functions_version);
if (count($function_exists) > 0 ) {
if (count($extension_loaded) > 0 ) {
if (count($defined) > 0 ) {
$cond_code = array ($cond_code, array ($function_exists,
sort($ignored_functions);
sort($ignored_extensions);
sort($ignored_constants);
$main_info = array ('ignored_functions' => $ignored_functions,
'ignored_extensions' => $ignored_extensions,
'ignored_constants' => $ignored_constants,
'max_version' => $earliest_version,
'version' => $latest_version,
'functions' => $functions,
'extensions' => $extensions,
'constants' => $constant_names,
'tokens' => $token_names,
'cond_code' => $cond_code);
$functions_version = array_merge($main_info, $functions_version);
return $functions_version;
* Checks if function which has $init version should be keep
* or ignore (version is between $min_ver and $max_ver).
* @param string $init version of current function
* @param string $min_ver minimum version of function to ignore
* @param string $max_ver maximum version of function to ignore
* @return boolean True to ignore function/constant, false otherwise
* @since version 1.4.0 (2006-09-27)
function _ignore ($init, $min_ver, $max_ver)
if ($max_ver && $cmp >= 0 ) {
* Checks if the given token is of this symbolic name
* @param mixed $token Single PHP token to test
* @param string $symbolic Symbolic name of the given token
* @since version 1.7.0b4 (2008-04-03)
function _isToken ($token, $symbolic)
return ($t == $symbolic);
* Computes the difference of arrays
* Computes the difference of arrays and returns result without original keys
* @param array $array1 The array to compare from
* @param array $array2 The array to compare against
* @link http://www.php.net/manual/en/function.array-diff.php#82297
* @since version 1.8.0b2 (2008-06-03)
function _arrayDiff ($array1, $array2)
// This wrapper for array_diff rekeys the array returned
// reinstantiate $array1 variable
// loop through the validated array and move elements to $array1
// this is necessary because the array_diff function
// returns arrays that retain their original keys
foreach ($valid_array as $valid) {
Documentation generated on Sun, 30 Nov 2008 16:30:33 -0500 by phpDocumentor 1.4.0. PEAR Logo Copyright © PHP Group 2004.
|