<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
// +----------------------------------------------------------------------+
// | PHP version 4 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2004 The PHP Group |
// +----------------------------------------------------------------------+
// | This source file is subject to version 3.0 of the PHP license, |
// | that is bundled with this package in the file LICENSE, and is |
// | available through the world-wide-web at the following url: |
// | http://www.php.net/license/3_0.txt. |
// | If you did not receive a copy of the PHP license and are unable to |
// | obtain it through the world-wide-web, please send a note to |
// | license@php.net so we can mail you a copy immediately. |
// +----------------------------------------------------------------------+
// | Authors: Davey Shafik <davey@php.net> |
// +----------------------------------------------------------------------+
//
// $Id: CompatInfo.php,v 1.15 2005/03/06 00:38:01 davey Exp $
/**
* Check Compatibility of chunk of PHP code
* @package PHP_CompatInfo
* @category PHP
*/
/**
* An array of function init versions and extension
*/
require_once 'PHP/data/func_array.php';
/**
* An array of constants and their init versions
*/
require_once 'PHP/data/const_array.php';
/**
* Check Compatibility of chunk of PHP code
*
* @package PHP_CompatInfo
* @author Davey Shafik <davey@php.net>
* @copyright Copyright 2003 Davey Shafik and Synaptic Media. All Rights Reserved.
* @example docs/examples/checkConstants.php Example that shows minimum version with Constants
* @example docs/examples/parseFile.php Example on how to parse a file
* @example docs/examples/parseDir.php Example on how to parse a directory
* @example docs/examples/parseArray.php Example on using using parseArray() to parse a script
* @example docs/examples/parseString.php Example on how to parse a string
* @example docs/examples/Cli.php Example of using PHP_CompatInfo_Cli
*/
class PHP_CompatInfo {
/**
* @var string Earliest version of PHP to use
*/
var $latest_version = '4.0.0';
/**
* @var boolean Toggle parseDir recursion
*/
var $recurse_dir = true;
/**
* Parse a file for its Compatibility info
*
* @param string $file Path of File to parse
* @param array $options An array of options where:
* 'debug' contains a boolean
* to control whether extra
* ouput is shown.
* 'ignore_functions' contains an array
* of functions to ignore when
* calculating the version needed.
* @access public
* @return Array
*/
function parseFile($file, $options = array())
{
$options = array_merge(array('debug' => false),$options);
if (!($tokens = $this->_tokenize($file))) {
return false;
}
return $this->_parseTokens($tokens,$options);
}
/**
* Parse a string for its Compatibility info
*
* @param string $string PHP Code to parses
* @param array $options An array of options where:
* 'debug' contains a boolean
* to control whether extra
* ouput is shown.
* 'ignore_functions' contains an array
* of functions to ignore when
* calculating the version needed.
* @access public
* @return Array
*/
function parseString($string, $options = array())
{
$options = array_merge(array('debug' => false),$options);
if (!($tokens = $this->_tokenize($string,true))) {
return false;
}
return $this->_parseTokens($tokens,$options);
}
/**
* Parse a directory recursively for its Compatibility info
*
* @see PHP_CompatInfo::_fileList()
* @param string $dir Path of folder to parse
* @param array $options Array of user options where:
* 'file_ext' Contains an array of file
* extensions to parse for PHP
* code. Default: php, php4,
* inc, phtml
* 'recurse_dir' Boolean on whether to
* recursively find files
* 'debug' contains a boolean
* to control whether extra
* ouput is shown.
* 'ignore_files' contains an array of
* files to ignore. File
* names are case insensitive.
* 'ignore_dirs' contains an array of
* directories to ignore.
* Directory names are case
* insensitive.
* 'ignore_functions' contains an array
* of functions to ignore when
* calculating the version needed.
* @access public
* @return array
*/
function parseDir($dir,$options = array())
{
$files = array();
$latest_version = $this->latest_version;
$extensions = array();
$constants = array();
$ignored = array();
$default_options = array('file_ext' => array('php','php4','inc','phtml'), 'recurse_dir' => true, 'debug' => false, 'ignore_files' => array(), 'ignore_dirs' => array());
$options = array_merge($default_options,$options);
if(is_dir($dir) && is_readable($dir)) {
if($dir{strlen($dir)-1} == '/' || $dir{strlen($dir)-1} == '\\') {
$dir = substr($dir,0,-1);
}
array_map('strtolower', $options['file_ext']);
array_map('strtolower', $options['ignore_files']);
array_map('strtolower', $options['ignore_dirs']);
$files_raw = $this->_fileList($dir,$options);
foreach($files_raw as $file) {
if(in_array(strtolower($file),$options['ignore_files'])) {
$ignored[] = $file;
continue;
}
$file_info = pathinfo($file);
if (isset($file_info['extension']) && in_array(strtolower($file_info['extension']),$options['file_ext'])) {
$tokens = $this->_tokenize($file);
if ($tokens != false) {
$files[$file] = $this->_parseTokens($tokens,$options);
} else {
return false;
}
}
}
foreach($files as $file) {
$cmp = version_compare($latest_version,$file['version']);
if ((int)$cmp === -1) {
$latest_version = $file['version'];
}
foreach($file['extensions'] as $ext) {
if(!in_array($ext,$extensions)) {
$extensions[] = $ext;
}
}
foreach ($file['constants'] as $const) {
if(!in_array($const,$constants)) {
$constants[] = $const;
}
}
}
if (sizeof($files) < 1) {
return false;
}
$files['constants'] = $constants;
$files['extensions'] = $extensions;
$files['version'] = $latest_version;
$files['ignored_files'] = $ignored;
return array_reverse($files);
} else {
return false;
}
}
/**
* Alias of parseDir
*
* @uses PHP_CompatInfo::parseDir()
* @access public
*/
function parseFolder($folder,$options = array()) {
return $this->parseDir($folder,$options);
}
/**
* 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 $files Array of file names or code strings
* @param array $options An array of options where:
* 'file_ext' Contains an array of file
* extensions to parse for PHP
* code. Default: php, php4,
* inc, phtml
* 'debug' contains a boolean
* to control whether extra
* ouput is shown.
* 'is_string' contains a boolean
* which says if the array values
* are strings or file names.
* 'ignore_files' contains an array of
* files to ignore. File
* names are case sensitive.
* 'ignore_functions' contains an array
* of functions to ignore when
* calculating the version needed.
* @access public
* @return array
*/
function parseArray($files,$options = array()) {
$latest_version = $this->latest_version;
$extensions = array();
$constants = array();
$options = array_merge(array('file_ext' => array('php','php4','inc','phtml'), 'is_string' => false,'debug' => false, 'ignore_files' => array()),$options);
$options['ignore_files'] = array_map("strtolower",$options['ignore_files']);
foreach($files as $file) {
if ($options['is_string'] == false) {
$pathinfo = pathinfo($file);
if (!in_array(strtolower($file),$options['ignore_files']) && in_array($pathinfo['extension'],$options['file_ext'])) {
$tokens = $this->_tokenize($file,$options['is_string']);
if ($tokens != false) {
$files_parsed[$file] = $this->_parseTokens($tokens,$options);
} else {
$files_parsed[$file] = false;
}
} else {
$ignored[] = $file;
}
} else {
$tokens = $this->_tokenize($file,$options['is_string']);
if ($tokens != false) {
$files_parsed[] = $this->_parseTokens($tokens,$options);
} else {
$files_parsed[] = false;
}
}
}
foreach($files_parsed as $file) {
if ($file != false) {
$cmp = version_compare($latest_version,$file['version']);
if ((int)$cmp === -1) {
$latest_version = $file['version'];
}
foreach($file['extensions'] as $ext) {
if(!in_array($ext,$extensions)) {
$extensions[] = $ext;
}
}
foreach($file['constants'] as $const) {
if(!in_array($const,$constants)) {
$constants[] = $const;
}
}
}
}
$files_parsed['constants'] = $constants;
$files_parsed['extensions'] = $extensions;
$files_parsed['version'] = $latest_version;
$files_parsed['ignored_files'] = isset($ignored) ? $ignored : array();
return array_reverse($files_parsed);
}
/**
* Parse the given Tokens
*
* 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 $debug Show Extra Output
* @access private
* @return array
*/
function _parseTokens($tokens, $options)
{
$functions = array();
$functions_version = array();
$latest_version = $this->latest_version;
$extensions = array();
$constants = array();
$constant_names = array();
$udf = array();
/* Check for PHP 5 stuffs */
$php5_tokens = @array(
T_ABSTRACT => 'abstract',
T_CATCH => 'catch',
T_FINAL => 'final',
T_INSTANCEOF => 'instanceof',
T_PRIVATE => 'private',
T_PROTECTED => 'protected',
T_PUBLIC => 'public',
T_THROW => 'throw',
T_TRY => 'try',
T_CLONE => 'clone',
T_INTERFACE => 'interface',
T_IMPLEMENTS => 'implements',
);
foreach ($php5_tokens as $php5_token => $value) {
if (in_array(array($php5_token, $value), $tokens)
|| in_array(array($php5_token, strtoupper($value)), $tokens)
|| in_array(array($php5_token, ucfirst($value)), $tokens)) {
$constants[] = $php5_token;
$latest_version = '5.0.0';
break;
}
}
$token_count = sizeof($tokens);
$i = 0;
while ($i < $token_count) {
$found_func = true;
if (is_array($tokens[$i]) && (token_name($tokens[$i][0]) == 'T_FUNCTION')) {
$found_func = false;
}
while ($found_func == false) {
$i += 1;
if (is_array($tokens[$i]) && (token_name($tokens[$i][0]) == 'T_STRING')) {
$found_func = true;
$udf[] = $tokens[$i][1];
}
}
if (is_array($tokens[$i]) && (token_name($tokens[$i][0]) == 'T_STRING')) {
if (isset($tokens[$i + 1]) && ($tokens[$i + 1][0] == '(') &&
(is_array($tokens[$i - 1])) &&
(token_name($tokens[$i - 1][0]) != 'T_DOUBLE_COLON') &&
(token_name($tokens[$i - 1][0]) != 'T_OBJECT_OPERATOR')) {
$functions[] = $tokens[$i][1];
}
}
if (is_array($tokens[$i])) {
if (in_array(token_name($tokens[$i][0]),$GLOBALS['const'])) {
$constants[] = token_name($tokens[$i][0]);
}
} else {
if (in_array($tokens[$i][0], $GLOBALS['const'])) {
$constants[] = $tokens[$i][0];
}
}
$i += 1;
}
$functions = array_unique($functions);
if (isset($options['ignore_functions'])) {
$options['ignore_functions'] = array_map("strtolower",$options['ignore_functions']);
} else {
$options['ignore_functions'] = array();
}
foreach($functions as $name) {
if (isset($GLOBALS['funcs'][$name]) && (!in_array($name,$udf) && (!in_array($name,$options['ignore_functions'])))) {
if ($options['debug'] == true) {
$functions_version[$GLOBALS['funcs'][$name]['init']][] = array('function' => $name, 'extension' => $GLOBALS['funcs'][$name]['ext']);
}
$cmp = version_compare($latest_version,$GLOBALS['funcs'][$name]['init']);
if ((int)$cmp === -1) {
$latest_version = $GLOBALS['funcs'][$name]['init'];
}
if ((!empty($GLOBALS['funcs'][$name]['ext'])) && ($GLOBALS['funcs'][$name]['ext'] != 'ext_standard') && ($GLOBALS['funcs'][$name]['ext'] != 'zend')) {
$extension = substr($GLOBALS['funcs'][$name]['ext'],4);
if ($extension{0} != '_') {
if(!in_array($extension,$extensions)) {
$extensions[] = $extension;
}
} else {
$ext = substr($extension, 1);
if(!in_array($extension,$extensions)) {
$extensions[] = $extension;
}
}
}
}
}
$constants = array_unique($constants);
foreach($constants as $constant) {
$cmp = version_compare($latest_version,$GLOBALS['const'][$constant]['init']);
if ((int)$cmp === -1) {
$latest_version = $GLOBALS['const'][$constant]['init'];
}
if(!in_array($GLOBALS['const'][$constant]['name'],$constant_names)) {
$constant_names[] = $GLOBALS['const'][$constant]['name'];
}
}
ksort($functions_version);
$functions_version['constants'] = $constant_names;
$functions_version['extensions'] = $extensions;
$functions_version['version'] = $latest_version;
$functions_version = array_reverse($functions_version);
return $functions_version;
}
/**
* Token a file or string
*
* @param string $input Filename or PHP code
* @param boolean $is_string Whether or note the input is a string
* @access private
* @return array
*/
function _tokenize($input,$is_string = false)
{
if ($is_string == false) {
$input = @file_get_contents($input,1);
if (is_string($input)) {
return token_get_all($input);
}
return false;
} else {
return token_get_all($input);
}
}
/**
* Retrieve a listing of every file in $directory and
* all subdirectories. Taken from PEAR_PackageFileManager_File
*
* @param string $directory full path to the directory you want the list of
* @access private
* @return array list of files in a directory
*/
function _fileList($directory,$options)
{
$ret = false;
if (@is_dir($directory) && (!in_array(strtolower($directory),$options['ignore_dirs']))) {
$ret = array();
$d = @dir($directory);
while($d && $entry=$d->read()) {
if ($entry{0} != '.') {
if (is_file($directory . DIRECTORY_SEPARATOR . $entry)) {
$ret[] = $directory . DIRECTORY_SEPARATOR . $entry;
}
if (is_dir($directory . DIRECTORY_SEPARATOR . $entry) && ($options['recurse_dir'] != false)) {
$tmp = $this->_fileList($directory . DIRECTORY_SEPARATOR . $entry,$options);
if (is_array($tmp)) {
foreach($tmp as $ent) {
$ret[] = $ent;
}
}
}
}
}
if ($d) {
$d->close();
}
} else {
return false;
}
return $ret;
}
}
?>