PHP_CompatInfo
[ class tree: PHP_CompatInfo ] [ index: PHP_CompatInfo ] [ all elements ]

Source for file Parser.php

Documentation is available at Parser.php

  1. <?php
  2. /**
  3.  * Copyright (c) 2008, Davey Shafik <davey@php.net>
  4.  *                     Laurent Laville <pear@laurent-laville.org>
  5.  *
  6.  * All rights reserved.
  7.  *
  8.  * Redistribution and use in source and binary forms, with or without
  9.  * modification, are permitted provided that the following conditions
  10.  * are met:
  11.  *
  12.  *     * Redistributions of source code must retain the above copyright
  13.  *       notice, this list of conditions and the following disclaimer.
  14.  *     * Redistributions in binary form must reproduce the above copyright
  15.  *       notice, this list of conditions and the following disclaimer in the
  16.  *       documentation and/or other materials provided with the distribution.
  17.  *     * Neither the name of the authors nor the names of its contributors
  18.  *       may be used to endorse or promote products derived from this software
  19.  *       without specific prior written permission.
  20.  *
  21.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  22.  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  23.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  24.  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
  25.  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  26.  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  27.  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  28.  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  29.  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  30.  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  31.  * POSSIBILITY OF SUCH DAMAGE.
  32.  *
  33.  * PHP versions 4 and 5
  34.  *
  35.  * @category PHP
  36.  * @package  PHP_CompatInfo
  37.  * @author   Davey Shafik <davey@php.net>
  38.  * @author   Laurent Laville <pear@laurent-laville.org>
  39.  * @license  http://www.opensource.org/licenses/bsd-license.php  BSD
  40.  * @version  CVS: $Id: Parser.php,v 1.15 2008/11/29 10:00:46 farell Exp $
  41.  * @link     http://pear.php.net/package/PHP_CompatInfo
  42.  * @since    File available since Release 1.8.0b2
  43.  */
  44.  
  45. require_once 'Event/Dispatcher.php';
  46. require_once 'File/Find.php';
  47.  
  48. /**
  49.  * An array of class init versions and extension
  50.  */
  51. require_once 'PHP/CompatInfo/class_array.php';
  52.  
  53. /**
  54.  * An array of function init versions and extension
  55.  */
  56. require_once 'PHP/CompatInfo/func_array.php';
  57.  
  58. /**
  59.  * An array of constants and their init versions
  60.  */
  61. require_once 'PHP/CompatInfo/const_array.php';
  62.  
  63. /**
  64.  * An abstract base class for CompatInfo renderers
  65.  */
  66. require_once 'PHP/CompatInfo/Renderer.php';
  67.  
  68. /**
  69.  * Event name of parsing data source start process
  70.  */
  71. define('PHP_COMPATINFO_EVENT_AUDITSTARTED''auditStarted');
  72. /**
  73.  * Event name of parsing data source end process
  74.  */
  75. define('PHP_COMPATINFO_EVENT_AUDITFINISHED''auditFinished');
  76. /**
  77.  * Event name of parsing a file start process
  78.  */
  79. define('PHP_COMPATINFO_EVENT_FILESTARTED''fileStarted');
  80. /**
  81.  * Event name of parsing a file end process
  82.  */
  83. define('PHP_COMPATINFO_EVENT_FILEFINISHED''fileFinished');
  84. /**
  85.  * Event name of parsing a file start process
  86.  */
  87. define('PHP_COMPATINFO_EVENT_CODESTARTED''codeStarted');
  88. /**
  89.  * Event name of parsing a file end process
  90.  */
  91. define('PHP_COMPATINFO_EVENT_CODEFINISHED''codeFinished');
  92.  
  93. /**
  94.  * Parser logic
  95.  *
  96.  * This class is the model in the MVC design pattern of API 1.8.0 (since beta 2)
  97.  *
  98.  * @category PHP
  99.  * @package  PHP_CompatInfo
  100.  * @author   Laurent Laville <pear@laurent-laville.org>
  101.  * @license  http://www.opensource.org/licenses/bsd-license.php  BSD
  102.  * @version  Release: 1.9.0b1
  103.  * @link     http://pear.php.net/package/PHP_CompatInfo
  104.  * @since    Class available since Release 1.8.0b2
  105.  */
  106. {
  107.     /**
  108.      * Instance of concrete renderer used to show parse results
  109.      *
  110.      * @var    object 
  111.      * @since  1.8.0b2
  112.      * @access protected
  113.      */
  114.     var $renderer;
  115.  
  116.     /**
  117.      * Stores the event dispatcher which handles notifications
  118.      *
  119.      * @var    object 
  120.      * @since  1.8.0b2
  121.      * @access protected
  122.      */
  123.     var $dispatcher;
  124.  
  125.     /**
  126.      * Count the number of observer registered.
  127.      * The Event_Dispatcher will be add on first observer registration, and
  128.      * will be removed with the last observer.
  129.      *
  130.      * @var    integer 
  131.      * @since  1.8.0b2
  132.      * @access private
  133.      */
  134.     var $_observerCount;
  135.  
  136.     /**
  137.      * @var string Earliest version of PHP to use
  138.      * @since  0.7.0
  139.      */
  140.     var $latest_version = '4.0.0';
  141.  
  142.     /**
  143.      * @var string Last version of PHP to use
  144.      */
  145.     var $earliest_version = '';
  146.  
  147.     /**
  148.      * @var array Parsing options
  149.      */
  150.     var $options;
  151.  
  152.     /**
  153.      * @var array Data Source
  154.      * @since  1.8.0b2
  155.      */
  156.     var $dataSource;
  157.  
  158.     /**
  159.      * @var array Directory list found when parsing data source
  160.      * @since  1.8.0b2
  161.      * @see    getDirlist()
  162.      */
  163.     var $directories;
  164.  
  165.     /**
  166.      * @var array List of files ignored when parsing data source
  167.      * @since  1.8.0b2
  168.      * @see    getIgnoredFiles()
  169.      */
  170.     var $ignored_files = array();
  171.  
  172.     /**
  173.      * @var array Result of the latest data source parsing
  174.      * @since  1.9.0b1
  175.      * @see    parseData()
  176.      */
  177.     var $latest_parse = null;
  178.  
  179.     /**
  180.      * Class constructor (ZE1) for PHP4
  181.      *
  182.      * @access public
  183.      * @since  version 1.8.0b2 (2008-06-03)
  184.      */
  185.     function PHP_CompatInfo_Parser()
  186.     {
  187.         $this->__construct();
  188.     }
  189.  
  190.     /**
  191.      * Class constructor (ZE2) for PHP5+
  192.      *
  193.      * @access public
  194.      * @since  version 1.8.0b2 (2008-06-03)
  195.      */
  196.     function __construct()
  197.     {
  198.         $this->options = array(
  199.             'file_ext' => array('php''php4''inc''phtml'),
  200.             'recurse_dir' => true,
  201.             'debug' => false,
  202.             'is_string' => false,
  203.             'ignore_files' => array(),
  204.             'ignore_dirs' => array()
  205.             );
  206.     }
  207.  
  208.     /**
  209.      * Set up driver to be used
  210.      *
  211.      * Set up driver to be used, dependant on specified type.
  212.      *
  213.      * @param string $type Name the type of driver (html, text...)
  214.      * @param array  $conf A hash containing any additional configuration
  215.      *
  216.      * @access public
  217.      * @return void 
  218.      * @since  version 1.8.0b2 (2008-06-03)
  219.      */
  220.     function setOutputDriver($type$conf = array())
  221.     {
  222.         $this->renderer =PHP_CompatInfo_Renderer::factory($this$type$conf);
  223.     }
  224.  
  225.     /**
  226.      * Registers a new listener
  227.      *
  228.      * Registers a new listener with the given criteria.
  229.      *
  230.      * @param mixed  $callback A PHP callback
  231.      * @param string $nName    (optional) Expected notification name
  232.      *
  233.      * @access public
  234.      * @return void 
  235.      * @since  version 1.8.0b2 (2008-06-03)
  236.      */
  237.     function addListener($callback$nName = EVENT_DISPATCHER_GLOBAL)
  238.     {
  239.         $this->dispatcher =Event_Dispatcher::getInstance();
  240.         // $this->dispatcher->setNotificationClass('PHP_CompatInfo_Audit');
  241.         $this->dispatcher->addObserver($callback$nName);
  242.         $this->_observerCount++;
  243.     }
  244.  
  245.     /**
  246.      * Removes a registered listener
  247.      *
  248.      * Removes a registered listener that correspond to the given criteria.
  249.      *
  250.      * @param mixed  $callback A PHP callback
  251.      * @param string $nName    (optional) Expected notification name
  252.      *
  253.      * @access public
  254.      * @return bool  True if listener was removed, false otherwise.
  255.      * @since  version 1.8.0b2 (2008-06-03)
  256.      */
  257.     function removeListener($callback$nName = EVENT_DISPATCHER_GLOBAL)
  258.     {
  259.         $result $this->dispatcher->removeObserver($callback$nName);
  260.  
  261.         if ($result{
  262.             $this->_observerCount--;
  263.             if ($this->_observerCount == 0{
  264.                 unset($this->dispatcher);
  265.             }
  266.         }
  267.         return $result;
  268.     }
  269.  
  270.     /**
  271.      * Post a new notification to all listeners registered.
  272.      *
  273.      * This notification occured only if a dispatcher exists. That means if
  274.      * at least one listener was registered.
  275.      *
  276.      * @param string $event Name of the notification handler
  277.      * @param array  $info  (optional) Additional information about the notification
  278.      *
  279.      * @access public
  280.      * @return void 
  281.      * @since  version 1.8.0b2 (2008-06-03)
  282.      */
  283.     function notifyListeners($event$info = array())
  284.     {
  285.         if (isset($this->dispatcher)) {
  286.             $this->dispatcher->post($this$event$info);
  287.         }
  288.     }
  289.  
  290.     /**
  291.      * Load components list
  292.      *
  293.      * Load components list for a PHP version or subset
  294.      *
  295.      * @param string         $min           PHP minimal version
  296.      * @param string|boolean$max           (optional) PHP maximal version
  297.      * @param boolean        $include_const (optional) include constants list
  298.      *                                                  in final result
  299.      * @param boolean        $groupby_vers  (optional) give initial php version
  300.      *                                                  of function or constant
  301.      *
  302.      * @return array         An array of php function/constant names history
  303.      * @access public
  304.      * @static
  305.      * @since  version 1.2.0 (2006-08-23)
  306.      */
  307.     function loadVersion($min$max = false,
  308.                          $include_const = false$groupby_vers = false)
  309.     {
  310.         $keys = array();
  311.         foreach ($GLOBALS['_PHP_COMPATINFO_FUNCS'as $func => $arr{
  312.             if (isset($arr['pecl']&& $arr['pecl'=== true{
  313.                 continue;
  314.             }
  315.             $vmin $arr['init'];
  316.             if (version_compare($vmin$min< 0{
  317.                 continue;
  318.             }
  319.             if ($max{
  320.                 $end (isset($arr['end'])) $arr['end'$vmin;
  321.  
  322.                 if (version_compare($end$max< 1{
  323.                     if ($groupby_vers === true{
  324.                         $keys[$vmin][$func;
  325.                     else {
  326.                         $keys[$func;
  327.                     }
  328.                 }
  329.             else {
  330.                 if ($groupby_vers === true{
  331.                     $keys[$vmin][$func;
  332.                 else {
  333.                     $keys[$func;
  334.                 }
  335.             }
  336.         }
  337.         ksort($keys);
  338.  
  339.         if ($include_const === true{
  340.             $keys = array('functions' => $keys'constants' => array());
  341.             foreach ($GLOBALS['_PHP_COMPATINFO_CONST'as $const => $arr{
  342.                 $vmin $arr['init'];
  343.                 if (version_compare($vmin$min< 0{
  344.                     continue;
  345.                 }
  346.                 if ($max{
  347.                     $end (isset($arr['end'])) $arr['end'$vmin;
  348.  
  349.                     if (version_compare($end$max< 1{
  350.                         if ($groupby_vers === true{
  351.                             $keys['constants'][$vmin][$arr['name'];
  352.                         else {
  353.                             $keys['constants'][$arr['name'];
  354.                         }
  355.                     }
  356.                 else {
  357.                     if ($groupby_vers === true{
  358.                         $keys['constants'][$vmin][$arr['name'];
  359.                     else {
  360.                         $keys['constants'][$arr['name'];
  361.                     }
  362.                 }
  363.             }
  364.             ksort($keys['constants']);
  365.         }
  366.         return $keys;
  367.     }
  368.  
  369.     /**
  370.      * Returns list of directory parsed
  371.      *
  372.      * Returns list of directory parsed, depending of restrictive parser options.
  373.      *
  374.      * @param mixed $dir     The directory name
  375.      * @param array $options An array of parser options. See parseData() method.
  376.      *
  377.      * @access public
  378.      * @return array   list of directories that should be parsed
  379.      * @since  version 1.8.0b2 (2008-06-03)
  380.      */
  381.     function getDirlist($dir$options)
  382.     {
  383.         if (!isset($this->directories)) {
  384.             $this->getFilelist($dir$options);
  385.         }
  386.  
  387.         return $this->directories;
  388.     }
  389.  
  390.     /**
  391.      * Returns list of files parsed
  392.      *
  393.      * Returns list of files parsed, depending of restrictive parser options.
  394.      *
  395.      * @param mixed $dir     The directory name where to look files
  396.      * @param array $options An array of parser options. See parseData() method.
  397.      *
  398.      * @access public
  399.      * @return array   list of files that should be parsed
  400.      * @since  version 1.8.0b2 (2008-06-03)
  401.      */
  402.     function getFilelist($dir$options)
  403.     {
  404.         $skipped = array();
  405.         $ignored = array();
  406.  
  407.         $options             array_merge($this->options$options);
  408.         $options['file_ext'array_map('strtolower'$options['file_ext']);
  409.  
  410.         if ($dir{strlen($dir)-1== '/' || $dir{strlen($dir)-1== '\\'{
  411.             $dir substr($dir0-1);
  412.         }
  413.  
  414.         // use system directory separator rather than forward slash by default
  415.         $ff         = new File_Find();
  416.         $ff->dirsep = DIRECTORY_SEPARATOR;
  417.  
  418.         // get directory list that should be ignored from scope
  419.         $ignore_dirs = array();
  420.         if (count($options['ignore_dirs']> 0{
  421.             foreach ($options['ignore_dirs'as $cond{
  422.                 $cond        str_replace('\\'"\\\\"$cond);
  423.                 $dirs        $ff->search('`'.$cond.'`'$dir'perl',
  424.                                            true'directories');
  425.                 $ignore_dirs array_merge($ignore_dirs$dirs);
  426.             }
  427.         }
  428.  
  429.         // get file list that should be ignored from scope
  430.         $ignore_files = array();
  431.         if (count($options['ignore_files']> 0{
  432.             foreach ($options['ignore_files'as $cond{
  433.                 $cond         str_replace('\\'"\\\\"$cond);
  434.                 $files        $ff->search('`'.$cond.'`'$dir'perl',
  435.                                             true'files');
  436.                 $ignore_files array_merge($ignore_files$files);
  437.             }
  438.         }
  439.  
  440.         list($directories$files$ff->maptree($dir);
  441.  
  442.         foreach ($files as $file{
  443.  
  444.             $file_info pathinfo($file);
  445.             if ($options['recurse_dir'== false
  446.                 && $file_info['dirname'!= $dir{
  447.                 $skipped[$file;
  448.                 continue;
  449.             }
  450.             if (in_array($file_info['dirname']$ignore_dirs)) {
  451.                 $ignored[$file;
  452.  
  453.             elseif (in_array($file$ignore_files)) {
  454.                 $ignored[$file;
  455.  
  456.             else {
  457.                 if (isset($file_info['extension'])
  458.                     && in_array(strtolower($file_info['extension']),
  459.                                 $options['file_ext'])) {
  460.                     continue;
  461.                 }
  462.                 $ignored[$file;
  463.             }
  464.         }
  465.  
  466.         $files PHP_CompatInfo_Parser::_arrayDiff($files,
  467.                                                    array_merge($ignored$skipped));
  468.         $this->directories
  469.                = PHP_CompatInfo_Parser::_arrayDiff($directories$ignore_dirs);
  470.         $this->ignored_files
  471.                = $ignored;
  472.  
  473.         return $files;
  474.     }
  475.  
  476.     /**
  477.      * Returns list of files ignored
  478.      *
  479.      * Returns list of files ignored while parsing directories
  480.      *
  481.      * @access public
  482.      * @return array or false on error
  483.      * @since  version 1.8.0b2 (2008-06-03)
  484.      */
  485.     function getIgnoredFiles()
  486.     {
  487.         return $this->ignored_files;
  488.     }
  489.  
  490.     /**
  491.      * Returns the latest parse data source version
  492.      *
  493.      * Returns the latest parse data source version, minimum and/or maximum
  494.      *
  495.      * @param mixed $file (optional) A specific filename or not (false)
  496.      * @param bool  $max  (optional) Level with or without contextual data
  497.      *
  498.      * @access public
  499.      * @return mixed Null on error or if there were no previous data parsing
  500.      * @since  version 1.9.0b1 (2008-11-30)
  501.      */
  502.     function getVersion($file = false$max = false)
  503.     {
  504.         $key ($max === true'max_version' 'version';
  505.  
  506.         if (!is_array($this->latest_parse)) {
  507.             // no code analysis found
  508.             $version = null;
  509.         elseif ($file === false{
  510.             $version $this->latest_parse[$key];
  511.         elseif (isset($this->latest_parse[$file])) {
  512.             $version $this->latest_parse[$file][$key];
  513.         else {
  514.             $version = null;
  515.         }
  516.  
  517.         return $version;
  518.     }
  519.  
  520.     /**
  521.      * Returns the latest parse data source classes declared
  522.      *
  523.      * Returns the latest parse data source classes declared (internal or
  524.      * end-user defined)
  525.      *
  526.      * @param mixed $file (optional) A specific filename or not (false)
  527.      *
  528.      * @access public
  529.      * @return mixed Null on error or if there were no previous data parsing
  530.      * @since  version 1.9.0b1 (2008-11-30)
  531.      */
  532.     function getClasses($file = false)
  533.     {
  534.         if (!is_array($this->latest_parse)) {
  535.             // no code analysis found
  536.             $classes = null;
  537.         elseif ($file === false{
  538.             $classes $this->latest_parse['classes'];
  539.         elseif (isset($this->latest_parse[$file])) {
  540.             $classes $this->latest_parse[$file]['classes'];
  541.         else {
  542.             $classes = null;
  543.         }
  544.  
  545.         return $classes;
  546.     }
  547.  
  548.     /**
  549.      * Returns the latest parse data source functions declared
  550.      *
  551.      * Returns the latest parse data source functions declared (internal or
  552.      * end-user defined)
  553.      *
  554.      * @param mixed $file (optional) A specific filename or not (false)
  555.      *
  556.      * @access public
  557.      * @return mixed Null on error or if there were no previous data parsing
  558.      * @since  version 1.9.0b1 (2008-11-30)
  559.      */
  560.     function getFunctions($file = false)
  561.     {
  562.         if (!is_array($this->latest_parse)) {
  563.             // no code analysis found
  564.             $functions = null;
  565.         elseif ($file === false{
  566.             $functions $this->latest_parse['functions'];
  567.         elseif (isset($this->latest_parse[$file])) {
  568.             $functions $this->latest_parse[$file]['functions'];
  569.         else {
  570.             $functions = null;
  571.         }
  572.  
  573.         return $functions;
  574.     }
  575.  
  576.     /**
  577.      * Returns the latest parse data source extensions used
  578.      *
  579.      * Returns the latest parse data source extensions used
  580.      *
  581.      * @param mixed $file (optional) A specific filename or not (false)
  582.      *
  583.      * @access public
  584.      * @return mixed Null on error or if there were no previous data parsing
  585.      * @since  version 1.9.0b1 (2008-11-30)
  586.      */
  587.     function getExtensions($file = false)
  588.     {
  589.         if (!is_array($this->latest_parse)) {
  590.             // no code analysis found
  591.             $extensions = null;
  592.         elseif ($file === false{
  593.             $extensions $this->latest_parse['extensions'];
  594.         elseif (isset($this->latest_parse[$file])) {
  595.             $extensions $this->latest_parse[$file]['extensions'];
  596.         else {
  597.             $extensions = null;
  598.         }
  599.  
  600.         return $extensions;
  601.     }
  602.  
  603.     /**
  604.      * Returns the latest parse data source constants declared
  605.      *
  606.      * Returns the latest parse data source constants declared (internal or
  607.      * end-user defined)
  608.      *
  609.      * @param mixed $file (optional) A specific filename or not (false)
  610.      *
  611.      * @access public
  612.      * @return mixed Null on error or if there were no previous data parsing
  613.      * @since  version 1.9.0b1 (2008-11-30)
  614.      */
  615.     function getConstants($file = false)
  616.     {
  617.         if (!is_array($this->latest_parse)) {
  618.             // no code analysis found
  619.             $constants = null;
  620.         elseif ($file === false{
  621.             $constants $this->latest_parse['constants'];
  622.         elseif (isset($this->latest_parse[$file])) {
  623.             $constants $this->latest_parse[$file]['constants'];
  624.         else {
  625.             $constants = null;
  626.         }
  627.  
  628.         return $constants;
  629.     }
  630.  
  631.     /**
  632.      * Returns the latest parse data source tokens declared
  633.      *
  634.      * Returns the latest parse data source PHP5+ tokens declared
  635.      *
  636.      * @param mixed $file (optional) A specific filename or not (false)
  637.      *
  638.      * @access public
  639.      * @return mixed Null on error or if there were no previous data parsing
  640.      * @since  version 1.9.0b1 (2008-11-30)
  641.      */
  642.     function getTokens($file = false)
  643.     {
  644.         if (!is_array($this->latest_parse)) {
  645.             // no code analysis found
  646.         elseif ($file === false{
  647.             $tokens $this->latest_parse['tokens'];
  648.         elseif (isset($this->latest_parse[$file])) {
  649.             $tokens $this->latest_parse[$file]['tokens'];
  650.         else {
  651.             $tokens = null;
  652.         }
  653.  
  654.         return $tokens;
  655.     }
  656.  
  657.     /**
  658.      * Returns the latest parse data source conditions
  659.      *
  660.      * Returns the latest parse data source conditions, with or without
  661.      * contextual data
  662.      *
  663.      * @param mixed $file      (optional) A specific filename or not (false)
  664.      * @param bool  $levelOnly (optional) Level with or without contextual data
  665.      *
  666.      * @access public
  667.      * @return mixed Null on error or if there were no previous data parsing
  668.      * @since  version 1.9.0b1 (2008-11-30)
  669.      */
  670.     function getConditions($file = false$levelOnly = false)
  671.     {
  672.         if (!is_array($this->latest_parse)) {
  673.             // no code analysis found
  674.             $conditions = null;
  675.         elseif ($file === false{
  676.             $conditions $this->latest_parse['cond_code'];
  677.         elseif (isset($this->latest_parse[$file])) {
  678.             $conditions $this->latest_parse[$file]['cond_code'];
  679.         else {
  680.             $conditions = null;
  681.         }
  682.  
  683.         if (is_array($conditions&& $levelOnly === true{
  684.             $conditions $conditions[0];
  685.         }
  686.         return $conditions;
  687.     }
  688.  
  689.     /**
  690.      * Parse a data source
  691.      *
  692.      * Parse a data source with auto detect ability. This data source, may be
  693.      * one of these follows: a directory, a file, a string (chunk of code),
  694.      * an array of multiple origin.
  695.      *
  696.      * Each of five parsing functions support common and specifics options.
  697.      *
  698.      *  * Common options :
  699.      *  - 'debug'                   Contains a boolean to control whether
  700.      *                              extra ouput is shown.
  701.      *  - 'ignore_functions'        Contains an array of functions to ignore
  702.      *                              when calculating the version needed.
  703.      *  - 'ignore_constants'        Contains an array of constants to ignore
  704.      *                              when calculating the version needed.
  705.      *  - 'ignore_extensions'       Contains an array of php extensions to ignore
  706.      *                              when calculating the version needed.
  707.      *  - 'ignore_versions'         Contains an array of php versions to ignore
  708.      *                              when calculating the version needed.
  709.      *  - 'ignore_functions_match'  Contains an array of function patterns to ignore
  710.      *                              when calculating the version needed.
  711.      *  - 'ignore_extensions_match' Contains an array of extension patterns to ignore
  712.      *                              when calculating the version needed.
  713.      *  - 'ignore_constants_match'  Contains an array of constant patterns to ignore
  714.      *                              when calculating the version needed.
  715.      *
  716.      *  * parseArray, parseDir|parseFolder, specific options :
  717.      *  - 'file_ext'                Contains an array of file extensions to parse
  718.      *                              for PHP code. Default: php, php4, inc, phtml
  719.      *  - 'ignore_files'            Contains an array of files to ignore.
  720.      *                              File names are case insensitive.
  721.      *
  722.      *  * parseArray specific options :
  723.      *  - 'is_string'               Contains a boolean which says if the array values
  724.      *                              are strings or file names.
  725.      *
  726.      *  * parseDir|parseFolder specific options :
  727.      *  - 'recurse_dir'             Boolean on whether to recursively find files
  728.      *  - 'ignore_dirs'             Contains an array of directories to ignore.
  729.      *                              Directory names are case insensitive.
  730.      *
  731.      * @param mixed $dataSource The data source (may be file, dir, string, or array)
  732.      * @param array $options    An array of options. See above.
  733.      *
  734.      * @access public
  735.      * @return array or false on error
  736.      * @since  version 1.8.0b2 (2008-06-03)
  737.      */
  738.     function parseData($dataSource$options = array())
  739.     {
  740.         $this->options = array_merge($this->options$options);
  741.  
  742.         $dataType  gettype($dataSource);
  743.         $dataCount = 0;
  744.         // - when array source with mixed content incompatible
  745.         // - if all directories are not readable
  746.         // - if data source invalid type: other than file, directory, string
  747.  
  748.         if ($dataType == 'string' || $dataType == 'array'{
  749.             if (is_array($dataSource)) {
  750.                 //$dataType = 'array';
  751.             elseif (is_dir($dataSource)) {
  752.                 $dataType   'directory';
  753.                 $dataSource = array($dataSource);
  754.             elseif (is_file($dataSource)) {
  755.                 $dataType   'file';
  756.                 $dataSource = array($dataSource);
  757.             elseif (substr($dataSource05== '<?php'{
  758.                 //$dataType = 'string';
  759.                 $this->options = array_merge($this->options,
  760.                                              array('is_string' => true));
  761.                 $dataSource    = array($dataSource);
  762.             else {
  763.                 //$dataType = 'string';
  764.                 // directory or file are misspelled
  765.             }
  766.             if (is_array($dataSource)) {
  767.                 $dataSource $this->_validateDataSource($dataSource,
  768.                                                          $this->options);
  769.                 $dataCount  count($dataSource);
  770.             }
  771.         }
  772.  
  773.         $this->dataSource = array('dataSource' => $dataSource,
  774.                                   'dataType' => $dataType,
  775.                                   'dataCount' => $dataCount);
  776.  
  777.         $eventInfo array_merge($this->dataSource,
  778.                                  array('parseOptions' => $this->options));
  779.  
  780.         // notify all observers that parsing data source begin
  781.         $this->notifyListeners(PHP_COMPATINFO_EVENT_AUDITSTARTED$eventInfo);
  782.  
  783.         if ($dataCount == 0{
  784.             $parseData = false;
  785.         else {
  786.             switch ($dataType{
  787.             case 'array' :
  788.                 $parseData $this->_parseArray($dataSource$this->options);
  789.                 break;
  790.             case 'string' :
  791.                 $parseData $this->_parseString($dataSource$this->options);
  792.                 break;
  793.             case 'file' :
  794.                 $parseData $this->_parseFile($dataSource$this->options);
  795.                 break;
  796.             case 'directory' :
  797.                 $parseData $this->_parseDir($dataSource$this->options);
  798.                 break;
  799.             }
  800.         }
  801.  
  802.         // notify all observers that parsing data source is over
  803.         $this->notifyListeners(PHP_COMPATINFO_EVENT_AUDITFINISHED$parseData);
  804.  
  805.         $this->latest_parse = $parseData;
  806.         return $parseData;
  807.     }
  808.  
  809.     /**
  810.      * Validate content of data source
  811.      *
  812.      * Validate content of data source list, before parsing each source
  813.      *
  814.      * @param mixed $dataSource The data source (may be file, dir, or string)
  815.      * @param array $options    Parser options (see parseData() method for details)
  816.      *
  817.      * @access private
  818.      * @return array   empty array on error
  819.      * @since  version 1.8.0b3 (2008-06-07)
  820.      */
  821.     function _validateDataSource($dataSource$options = array())
  822.     {
  823.         /**
  824.          * Array by default expect to contains list of files and/or directories.
  825.          * If you want a list of chunk of code (strings), 'is_string' option
  826.          * must be set to true.
  827.          */
  828.         $list = array();
  829.  
  830.         foreach ($dataSource as $source{
  831.             if ($options['is_string'=== true{
  832.                 if (is_string($source)) {
  833.                     $list[$source;
  834.                 else {
  835.                     /**
  836.                      * One of items is not a string (chunk of code). All
  837.                      * data sources parsing are stopped and considered as invalid.
  838.                      */
  839.                     $list = array();
  840.                     break;
  841.                 }
  842.             else {
  843.                 if (is_dir($source&& is_readable($source)) {
  844.                     $files $this->getFilelist($source$options);
  845.                     $list  array_merge($list$files);
  846.                 elseif (is_file($source)) {
  847.                     $list[$source;
  848.                 else {
  849.                     /**
  850.                      * One of items is not a valid file or directory. All
  851.                      * data sources parsing are stopped and considered as invalid.
  852.                      */
  853.                     $list = array();
  854.                     break;
  855.                 }
  856.             }
  857.         }
  858.  
  859.         return $list;
  860.     }
  861.  
  862.     /**
  863.      * Parse an Array of Files
  864.      *
  865.      * You can parse an array of Files or Strings, to parse
  866.      * strings, $options['is_string'] must be set to true
  867.      *
  868.      * @param array $dataSource Array of file &| directory names or code strings
  869.      * @param array $options    Parser options (see parseData() method for details)
  870.      *
  871.      * @access private
  872.      * @return array or false on error
  873.      * @since  version 0.7.0 (2004-03-09)
  874.      * @see    parseData()
  875.      */
  876.     function _parseArray($dataSource$options = array())
  877.     {
  878.         // Each data source have been checked before (see _validateDataSource() )
  879.         if (is_file($dataSource[0])) {
  880.             $parseData $this->_parseDir($dataSource$options);
  881.         else {
  882.             $parseData $this->_parseString($dataSource$options);
  883.         }
  884.  
  885.         return $parseData;
  886.     }
  887.  
  888.     /**
  889.      * Parse a string
  890.      *
  891.      * Parse a string for its compatibility info.
  892.      *
  893.      * @param array $strings PHP Code to parse
  894.      * @param array $options Parser options (see parseData() method for details)
  895.      *
  896.      * @access private
  897.      * @return array or false on error
  898.      * @since  version 0.7.0 (2004-03-09)
  899.      * @see    parseData()
  900.      */
  901.     function _parseString($strings$options = array())
  902.     {
  903.         $results $this->_parseElements($strings$options);
  904.         return $results;
  905.     }
  906.  
  907.     /**
  908.      * Parse a single file
  909.      *
  910.      * Parse a single file for its compatibility info.
  911.      *
  912.      * @param string $file    File to parse
  913.      * @param array  $options Parser options (see parseData() method for details)
  914.      *
  915.      * @access private
  916.      * @return array or false on error
  917.      * @since  version 0.7.0 (2004-03-09)
  918.      * @see    parseData()
  919.      */
  920.     function _parseFile($file$options = array())
  921.     {
  922.         $results $this->_parseElements($file$options);
  923.         return $results;
  924.     }
  925.  
  926.     /**
  927.      * Parse a directory
  928.      *
  929.      * Parse a directory recursively for its compatibility info
  930.      *
  931.      * @param array $files   Files list of folder to parse
  932.      * @param array $options Parser options (see parseData() method for details)
  933.      *
  934.      * @access private
  935.      * @return array or false on error
  936.      * @since  version 0.8.0 (2004-04-22)
  937.      * @see    parseData()
  938.      */
  939.     function _parseDir($files$options = array())
  940.     {
  941.         $results $this->_parseElements($files$options);
  942.         return $results;
  943.     }
  944.  
  945.     /**
  946.      * Parse a list of elements
  947.      *
  948.      * Parse a list of directory|file elements, or chunk of code (strings)
  949.      *
  950.      * @param array $elements Array of file &| directory names or code strings
  951.      * @param array $options  Parser options (see parseData() method for details)
  952.      *
  953.      * @access private
  954.      * @return array 
  955.      * @since  version 1.8.0b3 (2008-06-07)
  956.      * @see    _parseString(), _parseDir()
  957.      */
  958.     function _parseElements($elements$options = array())
  959.     {
  960.         $files_parsed       = array();
  961.         $latest_version     $this->latest_version;
  962.         $earliest_version   $this->earliest_version;
  963.         $all_functions      = array();
  964.         $classes            = array();
  965.         $functions          = array();
  966.         $extensions         = array();
  967.         $constants          = array();
  968.         $tokens             = array();
  969.         $ignored_functions  = array();
  970.         $ignored_extensions = array();
  971.         $ignored_constants  = array();
  972.         $function_exists    = array();
  973.         $extension_loaded   = array();
  974.         $defined            = array();
  975.         $cond_code          = 0;
  976.  
  977.         foreach ($elements as $p => $element{
  978.             $index $p + 1;
  979.             if (is_file($element)) {
  980.                 if (in_array($element$options['ignore_files'])) {
  981.                     $this->ignored_files[$element;
  982.                     continue;
  983.                 }
  984.                 $eventInfo
  985.                     = array('filename' => $element'fileindex' => $index);
  986.                 $this->notifyListeners(PHP_COMPATINFO_EVENT_FILESTARTED$eventInfo);
  987.  
  988.                 $tokens_list          $this->_tokenize($element);
  989.                 $kfile                $element;
  990.                 $files_parsed[$kfile$this->_parseTokens($tokens_list$options);
  991.  
  992.                 $this->notifyListeners(PHP_COMPATINFO_EVENT_FILEFINISHED);
  993.             else {
  994.                 $eventInfo
  995.                     = array('stringdata' => $element'stringindex' => $index);
  996.                 $this->notifyListeners(PHP_COMPATINFO_EVENT_CODESTARTED$eventInfo);
  997.  
  998.                 $tokens_list          $this->_tokenize($elementtrue);
  999.                 $kfile                'string_' $index;
  1000.                 $files_parsed[$kfile$this->_parseTokens($tokens_list$options);
  1001.  
  1002.                 $this->notifyListeners(PHP_COMPATINFO_EVENT_CODEFINISHED);
  1003.             }
  1004.         }
  1005.  
  1006.         foreach ($files_parsed as $fn => $file{
  1007.             $cmp version_compare($latest_version$file['version']);
  1008.             if ($cmp === -1{
  1009.                 $latest_version $file['version'];
  1010.             }
  1011.             if ($file['max_version'!= ''{
  1012.                 $cmp version_compare($earliest_version$file['max_version']);
  1013.                 if ($earliest_version == '' || $cmp === 1{
  1014.                     $earliest_version $file['max_version'];
  1015.                 }
  1016.             }
  1017.             foreach ($file['classes'as $class{
  1018.                 if (!in_array($class$classes)) {
  1019.                     $classes[$class;
  1020.                 }
  1021.             }
  1022.             foreach ($file['functions'as $func{
  1023.                 if (!in_array($func$functions)) {
  1024.                     $functions[$func;
  1025.                 }
  1026.             }
  1027.             foreach ($file['extensions'as $ext{
  1028.                 if (!in_array($ext$extensions)) {
  1029.                     $extensions[$ext;
  1030.                 }
  1031.             }
  1032.             foreach ($file['constants'as $const{
  1033.                 if (!in_array($const$constants)) {
  1034.                     $constants[$const;
  1035.                 }
  1036.             }
  1037.             foreach ($file['tokens'as $token{
  1038.                 if (!in_array($token$tokens)) {
  1039.                     $tokens[$token;
  1040.                 }
  1041.             }
  1042.             foreach ($file['ignored_functions'as $if{
  1043.                 if (!in_array($if$ignored_functions)) {
  1044.                     $ignored_functions[$if;
  1045.                 }
  1046.             }
  1047.             foreach ($file['ignored_extensions'as $ie{
  1048.                 if (!in_array($ie$ignored_extensions)) {
  1049.                     $ignored_extensions[$ie;
  1050.                 }
  1051.             }
  1052.             foreach ($file['ignored_constants'as $ic{
  1053.                 if (!in_array($ic$ignored_constants)) {
  1054.                     $ignored_constants[$ic;
  1055.                 }
  1056.             }
  1057.             foreach ($file['cond_code'][1][0as $ccf{
  1058.                 if (!in_array($ccf$function_exists)) {
  1059.                     $function_exists[$ccf;
  1060.                 }
  1061.             }
  1062.             foreach ($file['cond_code'][1][1as $cce{
  1063.                 if (!in_array($cce$extension_loaded)) {
  1064.                     $extension_loaded[$cce;
  1065.                 }
  1066.             }
  1067.             foreach ($file['cond_code'][1][2as $ccc{
  1068.                 if (!in_array($ccc$defined)) {
  1069.                     $defined[$ccc;
  1070.                 }
  1071.             }
  1072.             if ($options['debug'=== false{
  1073.                 unset($files_parsed[$fn]['cond_code'][1]);
  1074.             else {
  1075.                 unset($file['ignored_functions']);
  1076.                 unset($file['ignored_extensions']);
  1077.                 unset($file['ignored_constants']);
  1078.                 unset($file['max_version']);
  1079.                 unset($file['version']);
  1080.                 unset($file['classes']);
  1081.                 unset($file['functions']);
  1082.                 unset($file['extensions']);
  1083.                 unset($file['constants']);
  1084.                 unset($file['tokens']);
  1085.                 unset($file['cond_code']);
  1086.  
  1087.                 foreach ($file as $version => $functions{
  1088.                     // extra information available only when debug mode is on
  1089.                     if (isset($all_functions[$version])) {
  1090.                         foreach ($functions as $func{
  1091.                             $k array_search($func$all_functions[$version]);
  1092.                             if ($k === false{
  1093.                                 $all_functions[$version][$func;
  1094.                             }
  1095.                         }
  1096.                     else {
  1097.                         $all_functions[$version$functions;
  1098.                     }
  1099.                 }
  1100.             }
  1101.         }
  1102.  
  1103.         if (count($files_parsed== 0{
  1104.             return false;
  1105.         }
  1106.  
  1107.         if (count($function_exists> 0{
  1108.             $cond_code += 1;
  1109.         }
  1110.         if (count($extension_loaded> 0{
  1111.             $cond_code += 2;
  1112.         }
  1113.         if (count($defined> 0{
  1114.             $cond_code += 4;
  1115.         }
  1116.         if ($options['debug'=== false{
  1117.             $cond_code = array($cond_code);
  1118.         else {
  1119.             $cond_code = array($cond_codearray($function_exists,
  1120.                                                  $extension_loaded,
  1121.                                                  $defined));
  1122.         }
  1123.  
  1124.         sort($ignored_functions);
  1125.         sort($ignored_extensions);
  1126.         sort($ignored_constants);
  1127.         sort($classes);
  1128.         sort($functions);
  1129.         sort($extensions);
  1130.         sort($constants);
  1131.         sort($tokens);
  1132.         $main_info = array('ignored_files'      => $this->getIgnoredFiles(),
  1133.                            'ignored_functions'  => $ignored_functions,
  1134.                            'ignored_extensions' => $ignored_extensions,
  1135.                            'ignored_constants'  => $ignored_constants,
  1136.                            'max_version'   => $earliest_version,
  1137.                            'version'       => $latest_version,
  1138.                            'classes'       => $classes,
  1139.                            'functions'     => $functions,
  1140.                            'extensions'    => $extensions,
  1141.                            'constants'     => $constants,
  1142.                            'tokens'        => $tokens,
  1143.                            'cond_code'     => $cond_code);
  1144.  
  1145.         if (count($files_parsed== 1{
  1146.             if ($options['debug'=== false{
  1147.                 $parseData $main_info;
  1148.             else {
  1149.                 $main_info = array('ignored_files' => $this->getIgnoredFiles());
  1150.                 $parseData array_merge($main_info,
  1151.                                          $files_parsed[$kfile]$all_functions);
  1152.             }
  1153.         else {
  1154.             if ($options['debug'=== false{
  1155.                 $parseData array_merge($main_info$files_parsed);
  1156.             else {
  1157.                 $parseData array_merge($main_info$all_functions$files_parsed);
  1158.             }
  1159.         }
  1160.  
  1161.         $this->notifyListeners(PHP_COMPATINFO_EVENT_FILEFINISHED$parseData);
  1162.         return $parseData;
  1163.     }
  1164.  
  1165.     /**
  1166.      * Token a file or string
  1167.      *
  1168.      * @param string  $input     Filename or PHP code
  1169.      * @param boolean $is_string Whether or note the input is a string
  1170.      * @param boolean $debug     add token names for human read
  1171.      *
  1172.      * @access private
  1173.      * @return array 
  1174.      * @since  version 0.7.0 (2004-03-09)
  1175.      */
  1176.     function _tokenize($input$is_string = false$debug = false)
  1177.     {
  1178.         if ($is_string === false{
  1179.             $input file_get_contents($inputtrue);
  1180.         }
  1181.         $tokens token_get_all($input);
  1182.  
  1183.         if ($debug === true{
  1184.             $r = array();
  1185.             foreach ($tokens as $token{
  1186.                 if (is_array($token)) {
  1187.                     $token[token_name($token[0]);
  1188.                 else {
  1189.                     $token $token[0];
  1190.                 }
  1191.                 $r[$token;
  1192.             }
  1193.         else {
  1194.             $r $tokens;
  1195.         }
  1196.         return $r;
  1197.     }
  1198.  
  1199.     /**
  1200.      * Parse the given Tokens
  1201.      *
  1202.      * The tokens are those returned by token_get_all() which is nicely
  1203.      * wrapped in PHP_CompatInfo::_tokenize
  1204.      *
  1205.      * @param array   $tokens  Array of PHP Tokens
  1206.      * @param boolean $options Show Extra Output
  1207.      *
  1208.      * @access private
  1209.      * @return array 
  1210.      * @since  version 0.7.0 (2004-03-09)
  1211.      */
  1212.     function _parseTokens($tokens$options)
  1213.     {
  1214.         static $akeys;
  1215.  
  1216.         $classes            = array();
  1217.         $functions          = array();
  1218.         $functions_version  = array();
  1219.         $latest_version     $this->latest_version;
  1220.         $earliest_version   $this->earliest_version;
  1221.         $extensions         = array();
  1222.         $constants          = array();
  1223.         $constant_names     = array();
  1224.         $token_names        = array();
  1225.         $udf                = array();
  1226.         $ignore_functions   = array();
  1227.         $ignored_functions  = array();
  1228.         $ignore_extensions  = array();
  1229.         $ignored_extensions = array();
  1230.         $ignore_constants   = array();
  1231.         $ignored_constants  = array();
  1232.         $function_exists    = array();
  1233.         $extension_loaded   = array();
  1234.         $defined            = array();
  1235.         $cond_code          = 0;
  1236.  
  1237.         if (isset($options['ignore_constants'])) {
  1238.             $options['ignore_constants']
  1239.                 = array_map('strtoupper'$options['ignore_constants']);
  1240.         else {
  1241.             $options['ignore_constants'= array();
  1242.         }
  1243.         if (isset($options['ignore_extensions'])) {
  1244.             $options['ignore_extensions']
  1245.                 = array_map('strtolower'$options['ignore_extensions']);
  1246.         else {
  1247.             $options['ignore_extensions'= array();
  1248.         }
  1249.         if (isset($options['ignore_versions'][0])) {
  1250.             $min_ver $options['ignore_versions'][0];
  1251.         else {
  1252.             $min_ver = false;
  1253.         }
  1254.         if (isset($options['ignore_versions'][1])) {
  1255.             $max_ver $options['ignore_versions'][1];
  1256.         else {
  1257.             $max_ver = false;
  1258.         }
  1259.  
  1260.         if (isset($options['ignore_functions_match'])) {
  1261.             list($ifm_compare$ifm_patterns$options['ignore_functions_match'];
  1262.         else {
  1263.             $ifm_compare = false;
  1264.         }
  1265.         if (isset($options['ignore_extensions_match'])) {
  1266.             list($iem_compare$iem_patterns$options['ignore_extensions_match'];
  1267.         else {
  1268.             $iem_compare = false;
  1269.         }
  1270.         if (isset($options['ignore_constants_match'])) {
  1271.             list($icm_compare$icm_patterns$options['ignore_constants_match'];
  1272.         else {
  1273.             $icm_compare = false;
  1274.         }
  1275.  
  1276.         $token_count sizeof($tokens);
  1277.         $i           = 0;
  1278.         $found_class = false;
  1279.         while ($i $token_count{
  1280.             if ($this->_isToken($tokens[$i]'T_FUNCTION')) {
  1281.                 $found_func = false;
  1282.             else {
  1283.                 $found_func = true;
  1284.             }
  1285.             while ($found_func == false{
  1286.                 $i += 1;
  1287.                 if ($this->_isToken($tokens[$i]'T_STRING')) {
  1288.                     $found_func = true;
  1289.                     $func       $tokens[$i][1];
  1290.                     if ($found_class === false
  1291.                         || in_array($func$function_exists)) {
  1292.                         $udf[$func;
  1293.                     }
  1294.                 }
  1295.             }
  1296.  
  1297.             // Try to detect PHP method chaining implementation
  1298.             if ($this->_isToken($tokens[$i]'T_VARIABLE')
  1299.                 && $this->_isToken($tokens[$i+1]'T_OBJECT_OPERATOR')
  1300.                 && $this->_isToken($tokens[$i+2]'T_STRING')
  1301.                 && $this->_isToken($tokens[$i+3]'(')) {
  1302.  
  1303.                 $i                   += 3;
  1304.                 $php5_method_chaining = false;
  1305.                 while (((!is_array($tokens[$i]&& $tokens[$i== ';'=== false)
  1306.                     && (!$this->_isToken($tokens[$i]'T_CLOSE_TAG'))
  1307.                     {
  1308.                     $i += 1;
  1309.                     if ((($this->_isToken($tokens[$i]')'))
  1310.                         || ($this->_isToken($tokens[$i]'T_WHITESPACE')))
  1311.                         && $this->_isToken($tokens[$i+1]'T_OBJECT_OPERATOR')) {
  1312.  
  1313.                         $php5_method_chaining = true;
  1314.                     }
  1315.                 }
  1316.             }
  1317.  
  1318.             // Compare "ignore_functions_match" pre-condition
  1319.             if (is_string($ifm_compare)) {
  1320.                 if (strcasecmp('preg_match'$ifm_compare!= 0{
  1321.                     // Try to catch function_exists() condition
  1322.                     if ($this->_isToken($tokens[$i]'T_STRING')
  1323.                         && (strcasecmp($tokens[$i][1]$ifm_compare== 0)) {
  1324.  
  1325.                         while ((!$this->_isToken($tokens[$i],
  1326.                                                  'T_CONSTANT_ENCAPSED_STRING'))) {
  1327.                             $i += 1;
  1328.                         }
  1329.                         $func trim($tokens[$i][1]"'");
  1330.  
  1331.                         /**
  1332.                          * try if function_exists()
  1333.                          * match one or more pattern condition
  1334.                          */
  1335.                         foreach ($ifm_patterns as $pattern{
  1336.                             if (preg_match($pattern$func=== 1{
  1337.                                 $ignore_functions[$func;
  1338.                             }
  1339.                         }
  1340.                     }
  1341.                 }
  1342.             }
  1343.  
  1344.             // Compare "ignore_extensions_match" pre-condition
  1345.             if (is_string($iem_compare)) {
  1346.                 if (strcasecmp('preg_match'$iem_compare!= 0{
  1347.                     // Try to catch extension_loaded() condition
  1348.                     if ($this->_isToken($tokens[$i]'T_STRING')
  1349.                         && (strcasecmp($tokens[$i][1]$iem_compare== 0)) {
  1350.  
  1351.                         while ((!$this->_isToken($tokens[$i],
  1352.                                                  'T_CONSTANT_ENCAPSED_STRING'))) {
  1353.                             $i += 1;
  1354.                         }
  1355.                         $ext trim($tokens[$i][1]"'");
  1356.  
  1357.                         /**
  1358.                          * try if extension_loaded()
  1359.                          * match one or more pattern condition
  1360.                          */
  1361.                         foreach ($iem_patterns as $pattern{
  1362.                             if (preg_match($pattern$ext=== 1{
  1363.                                 $ignore_extensions[$ext;
  1364.                             }
  1365.                         }
  1366.                     }
  1367.                 }
  1368.             }
  1369.  
  1370.             // Compare "ignore_constants_match" pre-condition
  1371.             if (is_string($icm_compare)) {
  1372.                 if (strcasecmp('preg_match'$icm_compare!= 0{
  1373.                     // Try to catch defined() condition
  1374.                     if ($this->_isToken($tokens[$i]'T_STRING')
  1375.                         && (strcasecmp($tokens[$i][1]$icm_compare== 0)) {
  1376.  
  1377.                         while ((!$this->_isToken($tokens[$i],
  1378.                                                  'T_CONSTANT_ENCAPSED_STRING'))) {
  1379.                             $i += 1;
  1380.                         }
  1381.                         $cst trim($tokens[$i][1]"'");
  1382.  
  1383.                         /**
  1384.                          * try if defined()
  1385.                          * match one or more pattern condition
  1386.                          */
  1387.                         foreach ($icm_patterns as $pattern{
  1388.                             if (preg_match($pattern$cst=== 1{
  1389.                                 $ignore_constants[$cst;
  1390.                             }
  1391.                         }
  1392.                     }
  1393.                 }
  1394.             }
  1395.  
  1396.             // try to detect class instantiation
  1397.             if ($this->_isToken($tokens[$i]'T_STRING')
  1398.                 && (isset($tokens[$i-2]))
  1399.                 && $this->_isToken($tokens[$i-2]'T_NEW')) {
  1400.  
  1401.                 $is_class = true;
  1402.                 $classes[$tokens[$i][1];
  1403.             else {
  1404.                 $is_class = false;
  1405.             }
  1406.  
  1407.             if ($this->_isToken($tokens[$i]'T_STRING')
  1408.                 && $is_class == false
  1409.                 && (isset($tokens[$i+1]))
  1410.                 && $this->_isToken($tokens[$i+1]'(')) {
  1411.  
  1412.                 $is_function = false;
  1413.  
  1414.                 if (isset($tokens[$i-1])
  1415.                     && !$this->_isToken($tokens[$i-1]'T_DOUBLE_COLON')
  1416.                     && !$this->_isToken($tokens[$i-1]'T_OBJECT_OPERATOR')) {
  1417.  
  1418.                     if (isset($tokens[$i-2])
  1419.                         && $this->_isToken($tokens[$i-2]'T_FUNCTION')) {
  1420.                         // its a function declaration
  1421.                     else {
  1422.                         $is_function = true;
  1423.                     }
  1424.                 }
  1425.                 if ($is_function == true || !is_array($tokens[$i-1])) {
  1426.                     $functions[strtolower($tokens[$i][1]);
  1427.                 }
  1428.             }
  1429.  
  1430.             // try to detect condition function_exists()
  1431.             if ($this->_isToken($tokens[$i]'T_STRING')
  1432.                 && (strcasecmp($tokens[$i][1]'function_exists'== 0)) {
  1433.  
  1434.                 $j $i;
  1435.                 while ((!$this->_isToken($tokens[$j]')'))) {
  1436.                     if ($this->_isToken($tokens[$j]'T_CONSTANT_ENCAPSED_STRING')) {
  1437.                         $t_string          $tokens[$j][1];
  1438.                         $t_string          trim($t_string"'");
  1439.                         $t_string          trim($t_string'"');
  1440.                         $function_exists[$t_string;
  1441.                     }
  1442.                     $j++;
  1443.                 }
  1444.             }
  1445.             // try to detect condition extension_loaded()
  1446.             if ($this->_isToken($tokens[$i]'T_STRING')
  1447.                 && (strcasecmp($tokens[$i][1]'extension_loaded'== 0)) {
  1448.  
  1449.                 $j $i;
  1450.                 while ((!$this->_isToken($tokens[$j]')'))) {
  1451.                     if ($this->_isToken($tokens[$j]'T_CONSTANT_ENCAPSED_STRING')) {
  1452.                         $t_string           $tokens[$j][1];
  1453.                         $t_string           trim($t_string"'");
  1454.                         $t_string           trim($t_string'"');
  1455.                         $extension_loaded[$t_string;
  1456.                     }
  1457.                     $j++;
  1458.                 }
  1459.             }
  1460.             // try to detect condition defined()
  1461.             if ($this->_isToken($tokens[$i]'T_STRING')
  1462.                 && (strcasecmp($tokens[$i][1]'defined'== 0)) {
  1463.  
  1464.                 $j $i;
  1465.                 while ((!$this->_isToken($tokens[$j]')'))) {
  1466.                     if ($this->_isToken($tokens[$j]'T_CONSTANT_ENCAPSED_STRING')) {
  1467.                         $t_string  $tokens[$j][1];
  1468.                         $t_string  trim($t_string"'");
  1469.                         $t_string  trim($t_string'"');
  1470.                         $defined[$t_string;
  1471.                     }
  1472.                     $j++;
  1473.                 }
  1474.             }
  1475.  
  1476.             // try to detect beginning of a class
  1477.             if ($this->_isToken($tokens[$i]'T_CLASS')) {
  1478.                 $found_class = true;
  1479.             }
  1480.  
  1481.             if (is_array($tokens[$i])) {
  1482.                 if (!isset($akeys)) {
  1483.                     // build contents one time only (static variable)
  1484.                     $akeys array_keys($GLOBALS['_PHP_COMPATINFO_CONST']);
  1485.                 }
  1486.                 $const strtoupper($tokens[$i][1]);
  1487.                 $found array_search($const$akeys);
  1488.                 if ($found !== false{
  1489.                     if ($this->_isToken($tokens[$i]'T_ENCAPSED_AND_WHITESPACE')) {
  1490.                         // PHP 5 constant tokens found into a string
  1491.                     else {
  1492.                         // Compare "ignore_constants_match" free condition
  1493.                         $icm_preg_match = false;
  1494.                         if (is_string($icm_compare)) {
  1495.                             if (strcasecmp('preg_match'$icm_compare== 0{
  1496.                                 /**
  1497.                                  * try if preg_match()
  1498.                                  * match one or more pattern condition
  1499.                                  */
  1500.                                 foreach ($icm_patterns as $pattern{
  1501.                                     if (preg_match($pattern$const=== 1{
  1502.                                         $icm_preg_match = true;
  1503.                                         break;
  1504.                                     }
  1505.                                 }
  1506.                             }
  1507.                         }
  1508.  
  1509.                         $init $GLOBALS['_PHP_COMPATINFO_CONST'][$const]['init'];
  1510.                         if (!PHP_CompatInfo_Parser::_ignore($init,
  1511.                                                             $min_ver$max_ver)) {
  1512.                             $constants[$const;
  1513.                             if (in_array($const$ignore_constants)
  1514.                                 || in_array($const$options['ignore_constants'])
  1515.                                 || $icm_preg_match{
  1516.                                 $ignored_constants[$const;
  1517.                             else {
  1518.                                 $latest_version $init;
  1519.                             }
  1520.                         }
  1521.                     }
  1522.                 }
  1523.             }
  1524.             $i += 1;
  1525.         }
  1526.  
  1527.         $classes   array_unique($classes);
  1528.         $functions array_unique($functions);
  1529.         if (isset($options['ignore_functions'])) {
  1530.             $options['ignore_functions']
  1531.                 = array_map('strtolower'$options['ignore_functions']);
  1532.         else {
  1533.             $options['ignore_functions'= array();
  1534.         }
  1535.         if (count($ignore_functions> 0{
  1536.             $ignore_functions array_map('strtolower'$ignore_functions);
  1537.             $options['ignore_functions']
  1538.                 = array_merge($options['ignore_functions']$ignore_functions);
  1539.             $options['ignore_functions']
  1540.                 = array_unique($options['ignore_functions']);
  1541.         }
  1542.         if (count($ignore_extensions> 0{
  1543.             $ignore_extensions array_map('strtolower'$ignore_extensions);
  1544.             $options['ignore_extensions']
  1545.                 = array_merge($options['ignore_extensions']$ignore_extensions);
  1546.             $options['ignore_extensions']
  1547.                 = array_unique($options['ignore_extensions']);
  1548.         }
  1549.  
  1550.         foreach ($classes as $name{
  1551.             if (!isset($GLOBALS['_PHP_COMPATINFO_CLASS'][$name])) {
  1552.                 continue;  // skip this unknown class
  1553.             }
  1554.             $class $GLOBALS['_PHP_COMPATINFO_CLASS'][$name];
  1555.             if (PHP_CompatInfo_Parser::_ignore($class['init']$min_ver$max_ver)) {
  1556.                 continue;  // skip this class version
  1557.             }
  1558.  
  1559.             $cmp version_compare($latest_version$class['init']);
  1560.             if ($cmp === -1{
  1561.                 $latest_version $class['init'];
  1562.             }
  1563.             if (array_key_exists('end'$class)) {
  1564.                 $cmp version_compare($earliest_version$class['end']);
  1565.                 if ($earliest_version == '' || $cmp === 1{
  1566.                     $earliest_version $class['end'];
  1567.                 }
  1568.             }
  1569.  
  1570.             if (array_key_exists('ext'$class)) {
  1571.                 // this class depends of an extension
  1572.                 $extensions[$class['ext'];
  1573.             }
  1574.         }
  1575.  
  1576.         foreach ($functions as $name{
  1577.             if (!isset($GLOBALS['_PHP_COMPATINFO_FUNCS'][$name])) {
  1578.                 continue;  // skip this unknown function
  1579.             }
  1580.             $func $GLOBALS['_PHP_COMPATINFO_FUNCS'][$name];
  1581.  
  1582.             // retrieve if available the extension name
  1583.             if ((isset($func['ext']))
  1584.                 && ($func['ext'!= 'ext_standard')
  1585.                 && ($func['ext'!= 'zend')) {
  1586.                 if ($func['pecl'=== false{
  1587.                     $extension substr($func['ext']4);
  1588.                     if ($extension{0== '_'{
  1589.                         $extension $func['ext'];
  1590.                     }
  1591.                 else {
  1592.                     $extension $func['ext'];
  1593.                 }
  1594.             else {
  1595.                 $extension = false;
  1596.             }
  1597.  
  1598.             // Compare "ignore_functions_match" free condition
  1599.             $ifm_preg_match = false;
  1600.             if (is_string($ifm_compare)) {
  1601.                 if (strcasecmp('preg_match'$ifm_compare== 0{
  1602.                     /**
  1603.                      * try if preg_match()
  1604.                      * match one or more pattern condition
  1605.                      */
  1606.                     foreach ($ifm_patterns as $pattern{
  1607.                         if (preg_match($pattern$name=== 1{
  1608.                             $ifm_preg_match = true;
  1609.                             break;
  1610.                         }
  1611.                     }
  1612.                 }
  1613.             }
  1614.  
  1615.             if ((!in_array($name$udf))
  1616.                 && (!in_array($name$options['ignore_functions']))
  1617.                 && ($ifm_preg_match === false)) {
  1618.  
  1619.                 if ($extension && !in_array($extension$extensions)) {
  1620.                     $extensions[substr($func['ext']04== 'ext_'
  1621.                         ? $extension $func['ext'];
  1622.                 }
  1623.  
  1624.                 // Compare "ignore_extensions_match" free condition
  1625.                 $iem_preg_match = false;
  1626.                 if (is_string($iem_compare)) {
  1627.                     if (strcasecmp('preg_match'$iem_compare== 0{
  1628.                         /**
  1629.                          * try if preg_match()
  1630.                          * match one or more pattern condition
  1631.                          */
  1632.                         foreach ($iem_patterns as $pattern{
  1633.                             if (preg_match($pattern$extension=== 1{
  1634.                                 $iem_preg_match = true;
  1635.                                 break;
  1636.                             }
  1637.                         }
  1638.                     }
  1639.                 }
  1640.  
  1641.                 if ($extension
  1642.                     && (in_array($extension$options['ignore_extensions'])
  1643.                         || $iem_preg_match)) {
  1644.                     if (!in_array($extension$ignored_extensions)) {
  1645.                         // extension is ignored (only once)
  1646.                         $ignored_extensions[$extension;
  1647.                     }
  1648.                     // all extension functions are also ignored
  1649.                     $ignored_functions[$name;
  1650.                     continue;  // skip this extension function
  1651.                 }
  1652.  
  1653.                 if (PHP_CompatInfo_Parser::_ignore($func['init'],
  1654.                                                    $min_ver$max_ver)) {
  1655.                     continue;  // skip this function version
  1656.                 }
  1657.  
  1658.                 if ($options['debug'== true{
  1659.                     $functions_version[$func['init']][= array(
  1660.                         'function' => $name,
  1661.                         'extension' => substr($func['ext']04== 'ext_'
  1662.                             ? $extension $func['ext'],
  1663.                         'pecl' => $func['pecl']
  1664.                         );
  1665.                 }
  1666.                 if ($extension === false
  1667.                     || (isset($func['pecl']&& $func['pecl'=== false) ) {
  1668.                     $cmp version_compare($latest_version$func['init']);
  1669.                     if ($cmp === -1{
  1670.                         $latest_version $func['init'];
  1671.                     }
  1672.                     if (array_key_exists('end'$func)) {
  1673.                         $cmp version_compare($earliest_version$func['end']);
  1674.                         if ($earliest_version == '' || $cmp === 1{
  1675.                             $earliest_version $func['end'];
  1676.                         }
  1677.                     }
  1678.                 }
  1679.  
  1680.             else {
  1681.                 // function is ignored
  1682.                 $ignored_functions[$name;
  1683.             }
  1684.         }
  1685.  
  1686.         $ignored_constants array_unique($ignored_constants);
  1687.         $constants         array_unique($constants);
  1688.         foreach ($constants as $constant{
  1689.             $const $GLOBALS['_PHP_COMPATINFO_CONST'][$constant];
  1690.             if (PHP_CompatInfo_Parser::_ignore($const['init']$min_ver$max_ver)) {
  1691.                 continue;  // skip this constant version
  1692.             }
  1693.             if (!in_array($constant$ignored_constants)) {
  1694.                 $cmp version_compare($latest_version$const['init']);
  1695.                 if ($cmp === -1{
  1696.                     $latest_version $const['init'];
  1697.                 }
  1698.                 if (array_key_exists('end'$const)) {
  1699.                     $cmp version_compare($earliest_version$const['end']);
  1700.                     if ($earliest_version == '' || $cmp === 1{
  1701.                         $earliest_version $const['end'];
  1702.                     }
  1703.                 }
  1704.             }
  1705.             if (!in_array($const['name']$constant_names)) {
  1706.                 // split PHP5 tokens and pure PHP constants
  1707.                 if ($const['name'== strtolower($const['name'])) {
  1708.                     $token_names[$const['name'];
  1709.                 else {
  1710.                     $constant_names[$const['name'];
  1711.                 }
  1712.             }
  1713.         }
  1714.  
  1715.         if (isset($php5_method_chaining)
  1716.             && $php5_method_chaining === true
  1717.             && version_compare($latest_version'5.0.0'< 0{
  1718.             // when PHP Method chaining is detected, only available for PHP 5
  1719.             $latest_version '5.0.0';
  1720.         }
  1721.  
  1722.         ksort($functions_version);
  1723.  
  1724.         if (count($function_exists> 0{
  1725.             $function_exists array_unique($function_exists);
  1726.             $cond_code      += 1;
  1727.         }
  1728.         if (count($extension_loaded> 0{
  1729.             $extension_loaded array_unique($extension_loaded);
  1730.             $cond_code       += 2;
  1731.         }
  1732.         if (count($defined> 0{
  1733.             $defined    array_unique($defined);
  1734.             $cond_code += 4;
  1735.         }
  1736.         $cond_code = array($cond_codearray($function_exists,
  1737.                                              $extension_loaded,
  1738.                                              $defined));
  1739.  
  1740.         sort($ignored_functions);
  1741.         sort($ignored_extensions);
  1742.         sort($ignored_constants);
  1743.         sort($classes);
  1744.         sort($functions);
  1745.         sort($extensions);
  1746.         sort($constant_names);
  1747.         sort($token_names);
  1748.         $main_info = array('ignored_functions'  => $ignored_functions,
  1749.                            'ignored_extensions' => $ignored_extensions,
  1750.                            'ignored_constants'  => $ignored_constants,
  1751.                            'max_version' => $earliest_version,
  1752.                            'version'     => $latest_version,
  1753.                            'classes'     => $classes,
  1754.                            'functions'   => $functions,
  1755.                            'extensions'  => $extensions,
  1756.                            'constants'   => $constant_names,
  1757.                            'tokens'      => $token_names,
  1758.                            'cond_code'   => $cond_code);
  1759.  
  1760.         $functions_version array_merge($main_info$functions_version);
  1761.         return $functions_version;
  1762.     }
  1763.  
  1764.     /**
  1765.      * Checks if function which has $init version should be keep
  1766.      * or ignore (version is between $min_ver and $max_ver).
  1767.      *
  1768.      * @param string $init    version of current function
  1769.      * @param string $min_ver minimum version of function to ignore
  1770.      * @param string $max_ver maximum version of function to ignore
  1771.      *
  1772.      * @access private
  1773.      * @return boolean True to ignore function/constant, false otherwise
  1774.      * @since  version 1.4.0 (2006-09-27)
  1775.      * @static
  1776.      */
  1777.     function _ignore($init$min_ver$max_ver)
  1778.     {
  1779.         if ($min_ver{
  1780.             $cmp version_compare($init$min_ver);
  1781.             if ($max_ver && $cmp >= 0{
  1782.                 $cmp version_compare($init$max_ver);
  1783.                 if ($cmp < 1{
  1784.                     return true;
  1785.                 }
  1786.             elseif ($cmp === 0{
  1787.                 return true;
  1788.             }
  1789.         }
  1790.         return false;
  1791.     }
  1792.  
  1793.     /**
  1794.      * Checks if the given token is of this symbolic name
  1795.      *
  1796.      * @param mixed  $token    Single PHP token to test
  1797.      * @param string $symbolic Symbolic name of the given token
  1798.      *
  1799.      * @access private
  1800.      * @return bool 
  1801.      * @since  version 1.7.0b4 (2008-04-03)
  1802.      */
  1803.     function _isToken($token$symbolic)
  1804.     {
  1805.         if (is_array($token)) {
  1806.             $t token_name($token[0]);
  1807.         else {
  1808.             $t $token;
  1809.         }
  1810.         return ($t == $symbolic);
  1811.     }
  1812.  
  1813.     /**
  1814.      * Computes the difference of arrays
  1815.      *
  1816.      * Computes the difference of arrays and returns result without original keys
  1817.      *
  1818.      * @param array $array1 The array to compare from
  1819.      * @param array $array2 The array to compare against
  1820.      *
  1821.      * @access private
  1822.      * @static
  1823.      * @link   http://www.php.net/manual/en/function.array-diff.php#82297
  1824.      * @return array 
  1825.      * @since  version 1.8.0b2 (2008-06-03)
  1826.      */
  1827.     function _arrayDiff($array1$array2)
  1828.     {
  1829.         // This wrapper for array_diff rekeys the array returned
  1830.         $valid_array array_diff($array1$array2);
  1831.  
  1832.         // reinstantiate $array1 variable
  1833.         $array1 = array();
  1834.  
  1835.         // loop through the validated array and move elements to $array1
  1836.         // this is necessary because the array_diff function
  1837.         // returns arrays that retain their original keys
  1838.         foreach ($valid_array as $valid{
  1839.             $array1[$valid;
  1840.         }
  1841.         return $array1;
  1842.     }
  1843. }
  1844. ?>

Documentation generated on Sun, 30 Nov 2008 16:30:33 -0500 by phpDocumentor 1.4.0. PEAR Logo Copyright © PHP Group 2004.