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