/framework/vendor/zend/Zend/Reflection/File.php
PHP | 412 lines | 201 code | 44 blank | 167 comment | 30 complexity | 7c3e433e08d59c6f9a10f7adf18a92b4 MD5 | raw file
1<?php 2/** 3 * Zend Framework 4 * 5 * LICENSE 6 * 7 * This source file is subject to the new BSD license that is bundled 8 * with this package in the file LICENSE.txt. 9 * It is also available through the world-wide-web at this URL: 10 * http://framework.zend.com/license/new-bsd 11 * If you did not receive a copy of the license and are unable to 12 * obtain it through the world-wide-web, please send an email 13 * to license@zend.com so we can send you a copy immediately. 14 * 15 * @category Zend 16 * @package Zend_Reflection 17 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) 18 * @license http://framework.zend.com/license/new-bsd New BSD License 19 * @version $Id: File.php 20903 2010-02-04 16:16:47Z matthew $ 20 */ 21 22/** 23 * @see Zend_Reflection_Class 24 */ 25require_once 'Zend/Reflection/Class.php'; 26 27/** 28 * @see Zend_Reflection_Function 29 */ 30require_once 'Zend/Reflection/Function.php'; 31 32/** 33 * @category Zend 34 * @package Zend_Reflection 35 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) 36 * @license http://framework.zend.com/license/new-bsd New BSD License 37 */ 38class Zend_Reflection_File implements Reflector 39{ 40 /** 41 * @var string 42 */ 43 protected $_filepath = null; 44 45 /** 46 * @var string 47 */ 48 protected $_docComment = null; 49 50 /** 51 * @var int 52 */ 53 protected $_startLine = 1; 54 55 /** 56 * @var int 57 */ 58 protected $_endLine = null; 59 60 /** 61 * @var string[] 62 */ 63 protected $_requiredFiles = array(); 64 65 /** 66 * @var Zend_Reflection_Class[] 67 */ 68 protected $_classes = array(); 69 70 /** 71 * @var Zend_Reflection_Function[] 72 */ 73 protected $_functions = array(); 74 75 /** 76 * @var string 77 */ 78 protected $_contents = null; 79 80 /** 81 * Constructor 82 * 83 * @param string $file 84 * @return void 85 */ 86 public function __construct($file) 87 { 88 $fileName = $file; 89 90 if (($fileRealpath = realpath($fileName)) === false) { 91 $fileRealpath = self::findRealpathInIncludePath($file); 92 } 93 94 if (!$fileRealpath || !in_array($fileRealpath, get_included_files())) { 95 require_once 'Zend/Reflection/Exception.php'; 96 throw new Zend_Reflection_Exception('File ' . $file . ' must be required before it can be reflected'); 97 } 98 99 $this->_fileName = $fileRealpath; 100 $this->_contents = file_get_contents($this->_fileName); 101 $this->_reflect(); 102 } 103 104 /** 105 * Find realpath of file based on include_path 106 * 107 * @param string $fileName 108 * @return string 109 */ 110 public static function findRealpathInIncludePath($fileName) 111 { 112 require_once 'Zend/Loader.php'; 113 $includePaths = Zend_Loader::explodeIncludePath(); 114 while (count($includePaths) > 0) { 115 $filePath = array_shift($includePaths) . DIRECTORY_SEPARATOR . $fileName; 116 117 if ( ($foundRealpath = realpath($filePath)) !== false) { 118 break; 119 } 120 } 121 122 return $foundRealpath; 123 } 124 125 /** 126 * Export 127 * 128 * Required by the Reflector interface. 129 * 130 * @todo What should this do? 131 * @return null 132 */ 133 public static function export() 134 { 135 return null; 136 } 137 138 /** 139 * Return the file name of the reflected file 140 * 141 * @return string 142 */ 143 public function getFileName() 144 { 145 return $this->_fileName; 146 } 147 148 /** 149 * Get the start line - Always 1, staying consistent with the Reflection API 150 * 151 * @return int 152 */ 153 public function getStartLine() 154 { 155 return $this->_startLine; 156 } 157 158 /** 159 * Get the end line / number of lines 160 * 161 * @return int 162 */ 163 public function getEndLine() 164 { 165 return $this->_endLine; 166 } 167 168 /** 169 * Return the doc comment 170 * 171 * @return string 172 */ 173 public function getDocComment() 174 { 175 return $this->_docComment; 176 } 177 178 /** 179 * Return the docblock 180 * 181 * @param string $reflectionClass Reflection class to use 182 * @return Zend_Reflection_Docblock 183 */ 184 public function getDocblock($reflectionClass = 'Zend_Reflection_Docblock') 185 { 186 $instance = new $reflectionClass($this); 187 if (!$instance instanceof Zend_Reflection_Docblock) { 188 require_once 'Zend/Reflection/Exception.php'; 189 throw new Zend_Reflection_Exception('Invalid reflection class specified; must extend Zend_Reflection_Docblock'); 190 } 191 return $instance; 192 } 193 194 /** 195 * Return the reflection classes of the classes found inside this file 196 * 197 * @param string $reflectionClass Name of reflection class to use for instances 198 * @return array Array of Zend_Reflection_Class instances 199 */ 200 public function getClasses($reflectionClass = 'Zend_Reflection_Class') 201 { 202 $classes = array(); 203 foreach ($this->_classes as $class) { 204 $instance = new $reflectionClass($class); 205 if (!$instance instanceof Zend_Reflection_Class) { 206 require_once 'Zend/Reflection/Exception.php'; 207 throw new Zend_Reflection_Exception('Invalid reflection class provided; must extend Zend_Reflection_Class'); 208 } 209 $classes[] = $instance; 210 } 211 return $classes; 212 } 213 214 /** 215 * Return the reflection functions of the functions found inside this file 216 * 217 * @param string $reflectionClass Name of reflection class to use for instances 218 * @return array Array of Zend_Reflection_Functions 219 */ 220 public function getFunctions($reflectionClass = 'Zend_Reflection_Function') 221 { 222 $functions = array(); 223 foreach ($this->_functions as $function) { 224 $instance = new $reflectionClass($function); 225 if (!$instance instanceof Zend_Reflection_Function) { 226 require_once 'Zend/Reflection/Exception.php'; 227 throw new Zend_Reflection_Exception('Invalid reflection class provided; must extend Zend_Reflection_Function'); 228 } 229 $functions[] = $instance; 230 } 231 return $functions; 232 } 233 234 /** 235 * Retrieve the reflection class of a given class found in this file 236 * 237 * @param null|string $name 238 * @param string $reflectionClass Reflection class to use when creating reflection instance 239 * @return Zend_Reflection_Class 240 * @throws Zend_Reflection_Exception for invalid class name or invalid reflection class 241 */ 242 public function getClass($name = null, $reflectionClass = 'Zend_Reflection_Class') 243 { 244 if ($name === null) { 245 reset($this->_classes); 246 $selected = current($this->_classes); 247 $instance = new $reflectionClass($selected); 248 if (!$instance instanceof Zend_Reflection_Class) { 249 require_once 'Zend/Reflection/Exception.php'; 250 throw new Zend_Reflection_Exception('Invalid reflection class given; must extend Zend_Reflection_Class'); 251 } 252 return $instance; 253 } 254 255 if (in_array($name, $this->_classes)) { 256 $instance = new $reflectionClass($name); 257 if (!$instance instanceof Zend_Reflection_Class) { 258 require_once 'Zend/Reflection/Exception.php'; 259 throw new Zend_Reflection_Exception('Invalid reflection class given; must extend Zend_Reflection_Class'); 260 } 261 return $instance; 262 } 263 264 require_once 'Zend/Reflection/Exception.php'; 265 throw new Zend_Reflection_Exception('Class by name ' . $name . ' not found.'); 266 } 267 268 /** 269 * Return the full contents of file 270 * 271 * @return string 272 */ 273 public function getContents() 274 { 275 return $this->_contents; 276 } 277 278 /** 279 * Serialize to string 280 * 281 * Required by the Reflector interface 282 * 283 * @todo What should this serialization look like? 284 * @return string 285 */ 286 public function __toString() 287 { 288 return ''; 289 } 290 291 /** 292 * This method does the work of "reflecting" the file 293 * 294 * Uses PHP's tokenizer to perform file reflection. 295 * 296 * @return void 297 */ 298 protected function _reflect() 299 { 300 $contents = $this->_contents; 301 $tokens = token_get_all($contents); 302 303 $functionTrapped = false; 304 $classTrapped = false; 305 $requireTrapped = false; 306 $openBraces = 0; 307 308 $this->_checkFileDocBlock($tokens); 309 310 foreach ($tokens as $token) { 311 /* 312 * Tokens are characters representing symbols or arrays 313 * representing strings. The keys/values in the arrays are 314 * 315 * - 0 => token id, 316 * - 1 => string, 317 * - 2 => line number 318 * 319 * Token ID's are explained here: 320 * http://www.php.net/manual/en/tokens.php. 321 */ 322 323 if (is_array($token)) { 324 $type = $token[0]; 325 $value = $token[1]; 326 $lineNum = $token[2]; 327 } else { 328 // It's a symbol 329 // Maintain the count of open braces 330 if ($token == '{') { 331 $openBraces++; 332 } else if ($token == '}') { 333 $openBraces--; 334 } 335 336 continue; 337 } 338 339 switch ($type) { 340 // Name of something 341 case T_STRING: 342 if ($functionTrapped) { 343 $this->_functions[] = $value; 344 $functionTrapped = false; 345 } elseif ($classTrapped) { 346 $this->_classes[] = $value; 347 $classTrapped = false; 348 } 349 continue; 350 351 // Required file names are T_CONSTANT_ENCAPSED_STRING 352 case T_CONSTANT_ENCAPSED_STRING: 353 if ($requireTrapped) { 354 $this->_requiredFiles[] = $value ."\n"; 355 $requireTrapped = false; 356 } 357 continue; 358 359 // Functions 360 case T_FUNCTION: 361 if ($openBraces == 0) { 362 $functionTrapped = true; 363 } 364 break; 365 366 // Classes 367 case T_CLASS: 368 case T_INTERFACE: 369 $classTrapped = true; 370 break; 371 372 // All types of requires 373 case T_REQUIRE: 374 case T_REQUIRE_ONCE: 375 case T_INCLUDE: 376 case T_INCLUDE_ONCE: 377 $requireTrapped = true; 378 break; 379 380 // Default case: do nothing 381 default: 382 break; 383 } 384 } 385 386 $this->_endLine = count(explode("\n", $this->_contents)); 387 } 388 389 /** 390 * Validate / check a file level docblock 391 * 392 * @param array $tokens Array of tokenizer tokens 393 * @return void 394 */ 395 protected function _checkFileDocBlock($tokens) { 396 foreach ($tokens as $token) { 397 $type = $token[0]; 398 $value = $token[1]; 399 $lineNum = $token[2]; 400 if(($type == T_OPEN_TAG) || ($type == T_WHITESPACE)) { 401 continue; 402 } elseif ($type == T_DOC_COMMENT) { 403 $this->_docComment = $value; 404 $this->_startLine = $lineNum + substr_count($value, "\n") + 1; 405 return; 406 } else { 407 // Only whitespace is allowed before file docblocks 408 return; 409 } 410 } 411 } 412}