Location: PHPKode > scripts > class_requirement > class_requirement/class_requirement.php
<?php
/****************************************************************
*****************************************************************

class_log.php: create to work with log file (create and search).
Copyright (C) 2003  Matthieu MARY hide@address.com

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

You can found more information about GPL licence at:
http://www.gnu.org/licenses/gpl.html

for contact me: hide@address.com
****************************************************************
****************************************************************/

/**
* Can be download at
* http://www.phpclasses.org/
**/
require_once "class_dir.php";
require_once "class_specif.php";

class requirement{
   /**
    * the path to the ini file
    * @type string
    * @private
    **/
   var $iniFile;
   /**
    * the errors arrays
    * @type array
    * @private
    **/
    var $aErrors;

   /**
    * file to analyse
    * @type string
    * @private
    **/
    var $sFile;
   /**
    * dir file to analyse
    * @type string
    * @private
    **/
    var $sDir;
   /**
    * requirement
    * @type string
    * @private
    **/
    var $aRequirement;

   /**
    * array of keywords
    * @type array
    * @private
    **/
    var $aKeywords;
   /**
    * is there a use of require_once or include_once
    * @type bool
    * @private
    **/
    var $bRequirement;
    
   /**
    * Constructor.
    *
    * @public
    **/
   function requirement(){
       $this->iniFile = './functions.ini';
       $this->aErrors = array();
       $this->sFile = '';
       $this->sDir = '';
       $this->aRequirement = array();
       $this->aKeywords = array('foreach','array','for','while','if','elseif','list','each','switch','DISTINCT','COUNT','VALUES','MAX','PASSWORD','MD5','IN');
       $this->bRequirement = FALSE;
   }

   /**
    * Function that check the php file for requirement.
    *
    * @param string sPath_for_analyse required; the file need to be analysed
    * @public
    **/
   function check($sPath_for_analyse='')
   {
       $contents = '';
       $iLatest_version = '5';
       $iFirst_version = '3';
       $aFunctions_ini = array();
       $aFunctions = array();
       $bCSV = FALSE;
       $sNot_formated = '';
       $aLimits = array();
       $aLimits_final = array();
       $aFields = array();
       $aNumbers = array();
       $iNext = 0;
       $aCheck = array();
       $aLimits_detected = array();
       $aVersions_required = array();
       $aVersions_count = array();
       $bEnd = FALSE;


        // set the default values
       for($i = $iFirst_version;$i < $iLatest_version;$i++){
        $aLimits_detected[$i] = array(
            'inf' => $i,
            'sup' => ($i+1));
       }
       // is there a parameter?
       if (trim($sPath_for_analyse)==''){
          $this->aErrors[] = "missing argument 1 for checking";
          return;
       }
       $this->sFile = realpath($sPath_for_analyse);
       $this->sDir = dirname($this->sFile);
       $this->aRequirement = '';
       // file exist?
       if (!file_exists($this->sFile)){
          $this->aErrors[] = "file [".basename($this->sFile)."] doesn't exist";
          return;
       }
       // get the array of functions
       $aFunctions_ini = $this->read_ini_file();
       if (!$aFunctions_ini){
          $this->aErrors[] = "unable to get the function list in the inifile [".$this->iniFile."]";
          $this->aErrors[] = "please use the build method on the documentation path";
          return;
       }
       
       // analyse script
       $aFunctions = $this->study_function($this->sFile);
       $aFunctions['use'] = array_diff($aFunctions['use'],$aFunctions['declared']);
      //  print_r($aFunctions);
      // get the requirements
      foreach($aFunctions['use'] as $id => $function_name){
           $aLimits = array();
           $sNot_formated = trim($aFunctions_ini[$function_name]);
            // get each expressions
           $aFields = explode(',',$sNot_formated);
           if (count($aFields)==1) $aFields = array($sNot_formated);
           foreach($aFields as $i => $sReq){
            if (preg_match_all("/[0-9]{1}(\.[0-9]+)*/",$sReq,$aMatches)){
                $aNumbers = $aMatches[0];
                $iNext = substr($aNumbers[0],0,1)+1;
                $aCheck = array(
                    'count' => ((count($aNumbers)==1)?1:0),
                    '>' => preg_match("/>/",$sReq),
                    '<' => preg_match("/</",$sReq),
                    '=' => preg_match("/=/",$sReq),
                    'CSV' => preg_match("/CSV/",$sReq));
                if ($aCheck['CSV']) $bCSV = TRUE;
                if ($aCheck['count'] || $aCheck['>']){
                    $aLimits[] = array(
                        'inf' => (($aCheck['>'])?$aNumbers[1]:$aNumbers[0]),
                        'sup' => $iNext);
                }//if
                else{
                    $aLimits[] = array(
                        'inf' => $aNumbers[0],
                        'sup' => $aNumbers[1]);
                }//else
            }//if
           }//foreach
           if (count($aLimits)>0) $aLimits_final[] = $aLimits;
      }//foreach
      // set the require value
      if ($this->bRequirement){
         $aLimits[0] = array('inf' => '4.0.1', 'sup' => '5');
         $aLimits_final[] = $aLimits;
      }
      // get the versions of php required
      foreach($aLimits_final as $i => $aDatas){
        foreach($aDatas as $id => $aLimits){
            $iVersion = substr($aLimits['inf'],0,1);
            $aVersions_count[] = $iVersion;
        }
      }
      $aCount = array_count_values($aVersions_count);
      arsort($aCount);
      $aKeys = array_keys($aCount);
      $number_max = $aCount[$aKeys[0]];
      foreach($aCount as $iVersion => $iOcc){
           if ($number_max == $iOcc) $aVersions_required[] = $iVersion;
           else unset($aLimits_detected[$iVersion]);
      }
      // set the limits
      foreach($aLimits_final as $i => $aDatas){
        foreach($aDatas as $id => $aLimits){
            $iVersion = substr($aLimits['inf'],0,1);
            if (in_array($iVersion,$aVersions_required)){
                if (strcmp($aLimits_detected[$iVersion]['inf'],$aLimits['inf'])<0) $aLimits_detected[$iVersion]['inf'] = $aLimits['inf'];
                if (strcmp($aLimits_detected[$iVersion]['sup'],$aLimits['sup'])>0) $aLimits_detected[$iVersion]['sup'] = $aLimits['sup'];
            }
        }
      }
      $this->aRequirement = $aLimits_detected;
      return $this->end();
    }//check


   /**
    * return the requirement
    *
    * @private
    * @type string
    **/
    function end()
    {
        $spe = new specif();
        $sReturn = '';
        foreach($this->aRequirement as $iVersion => $dDatas){
            if (strcmp($dDatas['sup'],$dDatas['inf'])<0){
                 $this->aErrors[] = "this script cannot works ! [requirement min [".$dDatas['inf']."] max [".$dDatas['sup']."]";
                 return;
            }
            if ($dDatas['sup'] == ($iVersion+1)) $sReturn .= 'PHP '.$dDatas['inf'].' or later'.$spe->Endline();
            else $sReturn .= 'PHP '.$dDatas['inf'].' to '.$dDatas['sup'].$spe->Endline();
        }
             return $sReturn;
    }
    
   /**
    * Read the ini file : there is a bug in parse_ini_file function
    * when this function is call on file bigger than 16 ko, there is an error
    *
    * @private
    * @type mixed
    **/
    function read_ini_file()
    {
        $aParse = array();
        if (!file_exists($this->iniFile)) return FALSE;
        if (filesize($this->iniFile)<(1024*16)){
            $aDatas = @parse_ini_file($this->iniFile,TRUE);
            $aParse = $aDatas['function'];
        }
        else{
            $aValues = file($this->iniFile);
            $begin = FALSE;
            foreach($aValues as $line_number => $line){
                if ($begin){
                     $aFields = explode('=',$line);
                     $function_name = strtolower($aFields[0]);
                     for($i = 1,$sF = '';$i < count($aFields);$i++) $sF .= $aFields[$i]."=";
                     $aParse[$function_name] = trim(substr($sF,0,(strlen($sF)-1)));
                }
                if (!$begin) $begin = (!$begin && preg_match("/^\[function\]/",$line));
            }
        }
        return $aParse;
    }

   
   /**
    * Method build the ini file
    *
    * @param string max_execution_time optional, default value '.'; the PATh to the php html doc
    * @public
    * @type void
    **/
    function build($sPath_to_doc='.')
    {
     $dDatas = array();
     $dContents = array();
     // ini file already exists?
     if (file_exists($this->iniFile)){
        $this->aErrors[] = "ini file [".$this->iniFile."] already exists; stop build method";
        return;
     }
     // get list of documentation file
     $dir_doc = new dir($sPath_to_doc);
     if ($dir_doc->DATA_errors_size()){
        $this->aErrors = $dir_doc->DATA_errors();
        return;
     }
     $aFilelist = $dir_doc->LIST_get(FALSE,array('html'));
     if(count($aFilelist)==0){
      $this->aErrors[] = "path [".realpath($sPath_to_doc)."] have any html files; be sure that is the documentation folder";
      return;
     }
     // foreach function file of the doc
     foreach($aFilelist as $id => $filename){
       if (preg_match("/^(function)[.]/",basename($filename))){
         // get its content
         $contents = $this->_get_contents($filename);
         if (trim($contents)=='') continue;
         $aPatterns = array(
            "/(\n<TT>)/",
            "/(<\/TT>\n)/",
            "/(&gt;)/",
            "/(&lt;)/",
            "/(&nbsp;)/",
            "/(&eacute;)/",
            "/(&acirc;)/",
            "/(&agrave;)/",
            "/(&egrave;)/",
            "/(&#39;)/",
            "/(&#36;)/",
            "/(&amp;)/");
         $aReplacements = array(
            " ",
            " ",
            ">",
            "<",
            " ",
            "é",
            "â",
            "à",
            "è",
            "'",
            "$",
            "&");
         $contents = preg_replace($aPatterns,$aReplacements,$contents);

         // get the function name
         $pattern_function = '(fonction=)[a-zA-Z0-9_]+';
         preg_match("/$pattern_function/",$contents,$elts);
         $elts2 = preg_split("/(fonction=)/",$elts[0]);
         $function_name = $elts2[1];
         
         // get php version
         $pattern_version = '(h3>\[)[0-9A-Za-z":<>= .?_\/]+(<\/A>\])[ <>=PH0-9.,CVSonlyuniquement-]+';
         preg_match("/$pattern_version/",$contents,$elts3);
         $aversion = explode("]",$elts3[0]);
         $strrpos = ((preg_match("/[<]{1}[p]?$/",trim($aversion[1])))?strrpos($aversion[1],"<"):strlen($aversion[1]));
         $version = preg_replace("/[ ]+/"," ",trim(substr($aversion[1],0,$strrpos)));

         $dDatas[$function_name] = $version;
       } //if
     }//foreach
     $dContents['function'] = $dDatas;
     $this->_write_ini_file($dContents);
    }//build

   /**
    * @shortdesc Function get contents of a file
    * the contents returns have any comments
    *
    * @param string sFilename required; the path to the file need to be read
    * @private
    **/
   function _get_contents($sFilename)
   {
       $contents  = array();
       $contents_line = '';
       $contents_comment = array();
       $contents_file = file($sFilename);
       $contents = $contents_file;
       $pattern = '^\/\/.*$';
       $pattern2 = '\/\*';
       $pattern3 = '\*\/';
       // remove the comments with //
       foreach($contents_file as $i => $line) if (preg_match("/$pattern/",trim($line))) $contents_comment[] = $line;
       $contents = array_diff($contents,$contents_comment);
       
       // remove the comments with /* and */
       foreach($contents as $i => $line)
       {
            if ($bBegin) $contents_comment[] = $line;
            if ($bBegin) $bBegin = (!preg_match("/$pattern3/",$line));
            if (!$bBegin){
                 $bBegin = preg_match("/$pattern2/",$line);
                 if ($bBegin) $contents_comment[] = $line;
            }
            
        }
       $contents = array_diff($contents,$contents_comment);
       // put the content of the file without comment in line
       foreach($contents as $i => $line) $contents_line .= $line;
       return $contents_line;
   }

   /**
    * Function write the ini file
    *
    * @param array contents required. the hashtable for the ini file
    * @private
    * @type bool
    **/
    function _write_ini_file($contents)
    {
        $spe = new specif();
        if (!is_array($contents)) return FALSE;
        $str = '; generated by ['.basename($_SERVER["PHP_SELF"]).'] on '.date('r').$spe->Endfile();
        foreach ($contents as $section => $values) {
            if (!is_array($values)) return FALSE;
            $str .= $spe->Endfile()."[$section]";
            foreach ($values as $function_name => $php_value) {
                $str .= $spe->Endfile().strtolower($function_name)."=$php_value";
            }
            $str .= $spe->Endfile();
        }
        $fp = @fopen($this->iniFile,'w');
        if (!$fp){
             $this->aErrors[] = "unable to open the iniFile [".$this->iniFile."]";
             return;
             }
        fputs($fp, $str);
        fclose($fp);
        return;
    }//write_ini_file
    
 

   /**
    * @shortdesc get all functions and others
    * Get all functions (declared and uses), get although the declared objects and the tables name of insert query
    *
    * @param string contents required. the contents of the script to study
    * @private
    * @type array
    **/
   function get_function_all($contents)
   {
       $aFunction_object = array();
       $aObject = array();
       $aInsert_labels = array();
       // get the declared objects
        $pattern = '\$[a-zA-Z_0-9]+[ ]*=[ ]*new [a-zA-Z0-9]+';
        $values = $this->get_pattern($contents,$pattern);
        foreach($values[0] as $i => $declared_object){
            $aWords = explode("=",$declared_object);
            $aObject[] = trim(substr($aWords[0],1));
        }

        // get the functions object use
        $pattern = '(((->)|(:{2}))[A-Za-z0-9_]+)+';
        $values = $this->get_pattern($contents,$pattern);
        foreach($values[0] as $i => $function_object){
            $aFunction_object[] = trim(substr($function_object,2));
        }
         // get the insert values of the script
        $pattern = 'INSERT\s+INTO\s+[a-zA-Z_]+\s*\(';
        $values = $this->get_pattern($contents,$pattern);
        if (count($values[0])>0){
            foreach($values[0] as $i => $sInsert_rq){
                $matches = preg_split("/\s+/",trim($sInsert_rq),-1,PREG_SPLIT_NO_EMPTY);
                $aInsert_labels[] = ((preg_match("/\(/",$matches[2]))?substr(trim($matches[2]),0,strlen($matches[2])-1):$matches[2]);
            }
        }
        // get all the functions of the script
        $pattern = '(^|[^a-zA-Z0-9_])([a-zA-Z_][a-zA-Z0-9_]*)\s*\(';
        $values = $this->get_pattern($contents,$pattern);
        $aReturn = array_unique(array_diff($values[2],$this->aKeywords,$aFunction_object,$aObject,$aInsert_labels));
        return $aReturn;
   }

   /**
    * get_pattern
    *
    * @param string contents required. the contents of the script to examine
    * @param string pattern required. the pattern to reconize
    * @private
    * @type array
    **/
   function get_pattern($contents,$pattern)
   {
       preg_match_all("/$pattern/",$contents,$matches);
       return $matches;
   }
   
    /**
    * Get declared function of the script
    *
    * @param string contents required. the contents of the script to examine
    * @private
    * @type array
    **/
   function get_functions_declared($contents)
   {
        $pattern = 'function\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\(';
        $values = $this->get_pattern($contents,$pattern);
        return $values[1];
   }

    /**
    * @shortdesc function which analyse a script
    * Function that will analyse a script
    *
    * @param string contents required. the contents of the script to examine
    * @private
    * @type array
    **/
   function study_function($sFile)
   {
       // get the contents of the script
        $contents = $this->_get_contents($sFile);
        // get it's functions
        $aFunctions_ini = array(
            'declared' => $this->get_functions_declared($contents),
            'use' => $this->get_function_all($contents));
        // get the file requirement
        $aFiles = $this->_get_requirement($contents);
        foreach($aFiles as $i => $sPath){
            if (file_exists($sPath)){
                $aFunctions = $this->study_function($sPath);
                $aFunctions_ini['declared'] =  array_merge($aFunctions_ini['declared'],$aFunctions['declared']);
                $aFunctions_ini['use'] = array_merge($aFunctions_ini['use'],$aFunctions['use']);
            }
        }
        $aFunctions = array(
            'declared' => array_unique($aFunctions_ini['declared']),
            'use' => array_unique($aFunctions_ini['use']));

        return $aFunctions;
    }

    /**
    * @shortdesc function which get requirement files for a script
    * function which get requirement files for a script
    *
    * @param string contents required. the contents of the script to examine
    * @private
    * @type array
    **/
   function _get_requirement($contents)
   {
       $spe = new specif(FALSE);
        $files = array();
        $aIncludes_files = array();
        $complete_path_file = array();
        $include_path = ini_get('include_path');
        $aInclude_path = array();
        $pattern_require = '(require|include)(_once)?[ ]*\(?["\'][a-zA-Z_.]+\)?["\']';
        // get the pattern of require
        $aIncludes_files = $this->get_pattern($contents,$pattern_require);
        // get filenames
        foreach($aIncludes_files[0] as $i => $include_sq){
            $del =  ((strpos($include_sq,"'"))?"'":'"');
            $files_match = explode($del,$include_sq);
            if (!$this->bRequirement) $this->bRequirement = preg_match("/once/",$include_sq);
            $files[] = $files_match[1];
        }
        $files = array_unique($files);
        // get their path
        $aInclude_path = explode(";",$include_path);
        foreach($files as $i => $sPath_file){
            $sPath = $this->sDir.$spe->Path_delimiter().$sPath_file;
            if (!file_exists($sPath)) $complete_path_file[] = $aInclude_path[1].$spe->Path_delimiter().$sPath_file;
            else $complete_path_file[] = $sPath;
        }
        return array_unique($complete_path_file);
    }

}// class

?>
Return current item: class_requirement