1<?php 2// 3// +----------------------------------------------------------------------+ 4// | PHP_Parser | 5// +----------------------------------------------------------------------+ 6// | Copyright (c) 1997-2004 The PHP Group | 7// +----------------------------------------------------------------------+ 8// | This source file is subject to version 3.0 of the PHP license, | 9// | that is bundled with this package in the file LICENSE, and is | 10// | available through the world-wide-web at the following url: | 11// | http://www.php.net/license/3_0.txt. | 12// | If you did not receive a copy of the PHP license and are unable to | 13// | obtain it through the world-wide-web, please send a note to | 14// | license@php.net so we can mail you a copy immediately. | 15// +----------------------------------------------------------------------+ 16// | Authors: Greg Beaver <cellog@php.net> | 17// | Alan Knowles <alan_k@php.net> | 18// +----------------------------------------------------------------------+ 19// 20// $Id: Parser.php 230453 2007-02-21 20:41:55Z cellog $ 21// 22 23/* 24* usage : 25* print_r(PHP_Parser::staticParseFile($filename)); 26*/ 27 28require_once 'System.php'; 29// this will be used if the package is approved in PEAR 30//require_once 'Error/Stack.php'; 31require_once 'PEAR/ErrorStack.php'; 32 33define('PHP_PARSER_ERROR_NODRIVER', 1); 34define('PHP_PARSER_ERROR_NOTINITIALIZED', 2); 35define('PHP_PARSER_ERROR_NOINPUT', 3); 36 37class PHP_Parser { 38 const ERROR_NODRIVER = 1; 39 const ERROR_NOTINITIALIZED = 2; 40 const ERROR_NOINPUT = 3; 41 var $_parser; 42 var $_tokenizer; 43 44 /** 45 * Choose the parser and tokenizer 46 * @static 47 * @return PHP_Parser 48 */ 49 function factory($parser='Core', $tokenizer='') 50 { 51 $ret = new PHP_Parser; 52 $ret->setParser($parser); 53 $ret->setTokenizer($tokenizer); 54 return $ret; 55 } 56 57 /** 58 * @param string|object 59 * @return bool 60 * @throws PHP_Parser_Exception 61 */ 62 function setParser($parser='Core') 63 { 64 if (is_object($parser)) { 65 $this->_parser = $parser; 66 return false; 67 } 68 69 70 71 if (!class_exists($parser)) { 72 if ($this->isIncludeable('PHP/Parser/' . $parser . '.php')) { 73 include_once 'PHP/Parser/' . $parser . '.php'; 74 } 75 if (!class_exists('PHP_Parser_' . $parser)) { 76 throw $this->raiseError("no parser driver \"$parser\" found", 77 self::ERROR_NODRIVER, array('driver' => $parser, 78 'type' => 'parse')); 79 } 80 $parser = "PHP_Parser_$parser"; 81 } 82 $this->_parser = new $parser; 83 return true; 84 } 85 86 /** 87 * @param string|object 88 * @return PEAR_Error|false 89 */ 90 function setTokenizer($tokenizer='') 91 { 92 if (is_object($tokenizer)) { 93 $this->_tokenizer = $tokenizer; 94 return false; 95 } 96 if ($tokenizer=='') { 97 $tokenizer = 'PHP_Parser_Tokenizer'; 98 include_once 'PHP/Parser/Tokenizer.php'; 99 $this->_tokenizer = new $tokenizer('',array('parser_class'=>get_class($this->_parser))); 100 return false; 101 } 102 103 if (!class_exists('PHP_Parser_Tokenizer_'.$tokenizer)) { 104 if ($this->isIncludeable('PHP/Parser/Tokenizer/' . $tokenizer . '.php')) { 105 include_once 'PHP/Parser/Tokenizer/' . $tokenizer . '.php'; 106 } 107 if (!class_exists('PHP_Parser_Tokenizer_' . $tokenizer)) { 108 return $this->raiseError("no tokenizer driver \"$tokenizer\" found", 109 self::ERROR_NODRIVER, array('driver' => $tokenizer, 110 'type' => 'tokenize')); 111 } 112 $tokenizer = "PHP_Parser_Tokenizer_$tokenizer"; 113 } 114 $this->_tokenizer = new $tokenizer; 115 return false; 116 } 117 118 /** 119 * @param string input to parse 120 * @param array options for the tokenizer 121 * @return PEAR_Error|false 122 */ 123 function setTokenizerOptions($php, $options = array()) 124 { 125 if (is_object($this->_tokenizer)) { 126 $this->_tokenizer->setOptions($php, $options); 127 return false; 128 } 129 return $this->raiseError("tokenizer must be initialized before setTokenizerOptions", 130 PHP_PARSER_ERROR_NOTINITIALIZED); 131 } 132 133 function raiseError($msg, $code, $params = array()) 134 { 135 return PEAR_ErrorStack::staticPush('PHP_Parser', $code, 136 'exception', $params, $msg); 137 } 138 139 /** 140 * @param string $path relative or absolute include path 141 * @return boolean 142 * @static 143 */ 144 function isIncludeable($path) 145 { 146 if (file_exists($path) && is_readable($path)) { 147 return true; 148 } 149 $ipath = explode(PATH_SEPARATOR, ini_get('include_path')); 150 foreach ($ipath as $include) { 151 $test = realpath($include . DIRECTORY_SEPARATOR . $path); 152 if (file_exists($test) && is_readable($test)) { 153 return true; 154 } 155 } 156 return false; 157 } 158 159 /** 160 * Parse a file with wddx caching options. 161 * 162 * parses a php file, 163 * @param string name of file to parse 164 * @param false|string false = no caching, '' = write to same directory, '/some/dir/' - cache directory 165 * 166 * @return array| object PEAR_Error should return an array of includes and classes.. will grow... 167 * @access public 168 */ 169 static function staticParseFile( 170 $file, 171 $options = array(), 172 $tokenizeroptions = array(), 173 $tokenizerClass = 'PHP_Parser_Tokenizer', 174 $cacheDir=false 175 ) 176 { 177 if ($cacheDir === false) { 178 return self::parse(file_get_contents($file), $options, $tokenizeroptions, $tokenizerClass); 179 } 180 if (!strlen($cacheDir)) { 181 $cacheFile = dirname($file).'/.PHP_Parser/' . basename($file) . '.wddx'; 182 } else { 183 $cacheFile = $cacheDir . $file . '.wddx'; 184 } 185 if (!file_exists(dirname($cacheFile))) { 186 System::mkdir(dirname($cacheFile) ." -p"); 187 } 188 189 //echo "Cache = $cacheFile\n"; 190 if (file_exists($cacheFile) && (filemtime($cacheFile) > filemtime($file))) { 191 //echo "get cache"; 192 return wddx_deserialize(file_get_contents($cacheFile)); 193 } 194 195 // this whole caching needs a much nicer logic to it.. 196 // but for the time being test the filename as md5 in /tmp/ 197 $tmpCacheFile = '/tmp/'.md5($file).'.wddx'; 198 if (file_exists($tmpCacheFile) && (filemtime($tmpCacheFile) > filemtime($file))) { 199 //echo "get cache"; 200 return wddx_deserialize(file_get_contents($tmpCacheFile)); 201 } 202 203 $result = PHP_Parser::parse(file_get_contents($file), $options, $tokenizeroptions, $tokenizerClass); 204 if (function_exists('wddx_set_indent')) { 205 wddx_set_indent(2); 206 } 207 //echo "Writing Cache = $cacheFile\n"; 208 $fh = @fopen ($cacheFile,'w'); 209 if (!$fh) { 210 $fh = fopen ($tmpCacheFile,'w'); 211 } 212 fwrite($fh,wddx_serialize_value($result)); 213 fclose($fh); 214 return $result; 215 } 216 217 218 /** 219 * Parse a string 220 * 221 * parses a php file, 222 * 223 * 224 * @param string name of file to parse 225 * 226 * 227 * @return array| object PEAR_Error should return an array of includes and classes.. will grow... 228 * @access public 229 */ 230 231 232 static function parse( 233 $string, 234 $options = array(), 235 $tokenizeroptions = array(), 236 $tokenizerClass = 'PHP_Parser_Tokenizer') 237 { 238 if (!trim($string)) { 239 throw new Exception('Nothing to parse'); 240 } 241 242 if (($tokenizerClass == 'PHP_Parser_Tokenizer') && !class_exists($tokenizerClass)) { 243 require_once 'PHP/Parser/Tokenizer.php'; 244 } 245 246 $yyInput = new $tokenizerClass($string, $tokenizeroptions); 247 //$yyInput->setOptions($string, $tokenizeroptions); 248 //xdebug_start_profiling(); 249 $t = new PHP_Parser_Core($yyInput); 250 while ($yyInput->advance()) { 251 $t->doParse($yyInput->token, $yyInput->getValue(), $yyInput); 252 } 253 $t->doParse(0, 0); 254 255 return $t; 256 } 257 258 function parseString($php, $tokenoptions = array()) 259 { 260 if (!trim($string)) { 261 throw new Exception('Nothing to parse'); 262 } 263 $this->setTokenizerOptions($php, $tokenoptions); 264 $err = $this->_parser->yyparse($this->_tokenizer); 265 // some parser do not set stuff like this.. 266 if ($err) { 267 return $err; 268 } 269 if (!isset($this->_parser->classes)) { 270 return; 271 } 272 273 return array( 274 'classes' => $this->_parser->classes, 275 'interfaces' => $this->_parser->interfaces, 276 'includes' => $this->_parser->includes, 277 'functions' => $this->_parser->functions, 278 'constants' => $this->_parser->constants, 279 'globals' => $this->_parser->globals 280 ); 281 } 282} 283 284?> 285