File indexing completed on 2024-05-12 06:02:55

0001 <?php
0002 /**
0003  * Zend Framework
0004  *
0005  * LICENSE
0006  *
0007  * This source file is subject to the new BSD license that is bundled
0008  * with this package in the file LICENSE.txt.
0009  * It is also available through the world-wide-web at this URL:
0010  * http://framework.zend.com/license/new-bsd
0011  * If you did not receive a copy of the license and are unable to
0012  * obtain it through the world-wide-web, please send an email
0013  * to license@zend.com so we can send you a copy immediately.
0014  *
0015  * @category   Zend
0016  * @package    Zend_Reflection
0017  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0018  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0019  * @version    $Id$
0020  */
0021 
0022 /**
0023  * @see Zend_Reflection_Class
0024  */
0025 // require_once 'Zend/Reflection/Class.php';
0026 
0027 /**
0028  * @see Zend_Reflection_Function
0029  */
0030 // require_once 'Zend/Reflection/Function.php';
0031 
0032 /**
0033  * @category   Zend
0034  * @package    Zend_Reflection
0035  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0036  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0037  */
0038 class Zend_Reflection_File implements Reflector
0039 {
0040     /**
0041      * @var string
0042      */
0043     protected $_filepath        = null;
0044 
0045     /**
0046      * @var string
0047      */
0048     protected $_docComment      = null;
0049 
0050     /**
0051      * @var int
0052      */
0053     protected $_startLine       = 1;
0054 
0055     /**
0056      * @var int
0057      */
0058     protected $_endLine         = null;
0059 
0060     /**
0061      * @var string[]
0062      */
0063     protected $_requiredFiles   = array();
0064 
0065     /**
0066      * @var Zend_Reflection_Class[]
0067      */
0068     protected $_classes         = array();
0069 
0070     /**
0071      * @var Zend_Reflection_Function[]
0072      */
0073     protected $_functions       = array();
0074 
0075     /**
0076      * @var string
0077      */
0078     protected $_contents        = null;
0079 
0080     /**
0081      * Constructor
0082      *
0083      * @param  string $file
0084      * @return void
0085      */
0086     public function __construct($file)
0087     {
0088         $fileName = $file;
0089         
0090         $fileRealpath = realpath($fileName);
0091         if ($fileRealpath) {
0092             // realpath() doesn't return false if Suhosin is included
0093             // see http://uk3.php.net/manual/en/function.realpath.php#82770
0094             if (!file_exists($fileRealpath)) {
0095                 $fileRealpath = false;
0096             }
0097         }
0098         
0099         if ($fileRealpath === false) {
0100             $fileRealpath = self::findRealpathInIncludePath($file);
0101         }
0102 
0103         if (!$fileRealpath || !in_array($fileRealpath, get_included_files())) {
0104             // require_once 'Zend/Reflection/Exception.php';
0105             throw new Zend_Reflection_Exception('File ' . $file . ' must be required before it can be reflected');
0106         }
0107 
0108         $this->_fileName = $fileRealpath;
0109         $this->_contents = file_get_contents($this->_fileName);
0110         $this->_reflect();
0111     }
0112 
0113     /**
0114      * Find realpath of file based on include_path
0115      *
0116      * @param  string $fileName
0117      * @return string
0118      */
0119     public static function findRealpathInIncludePath($fileName)
0120     {
0121         // require_once 'Zend/Loader.php';
0122         $includePaths = Zend_Loader::explodeIncludePath();
0123         while (count($includePaths) > 0) {
0124             $filePath = array_shift($includePaths) . DIRECTORY_SEPARATOR . $fileName;
0125 
0126             if ( ($foundRealpath = realpath($filePath)) !== false) {
0127                 break;
0128             }
0129         }
0130 
0131         return $foundRealpath;
0132     }
0133 
0134     /**
0135      * Export
0136      *
0137      * Required by the Reflector interface.
0138      *
0139      * @todo   What should this do?
0140      * @return null
0141      */
0142     public static function export()
0143     {
0144         return null;
0145     }
0146 
0147     /**
0148      * Return the file name of the reflected file
0149      *
0150      * @return string
0151      */
0152     public function getFileName()
0153     {
0154         return $this->_fileName;
0155     }
0156 
0157     /**
0158      * Get the start line - Always 1, staying consistent with the Reflection API
0159      *
0160      * @return int
0161      */
0162     public function getStartLine()
0163     {
0164         return $this->_startLine;
0165     }
0166 
0167     /**
0168      * Get the end line / number of lines
0169      *
0170      * @return int
0171      */
0172     public function getEndLine()
0173     {
0174         return $this->_endLine;
0175     }
0176 
0177     /**
0178      * Return the doc comment
0179      *
0180      * @return string
0181      */
0182     public function getDocComment()
0183     {
0184         return $this->_docComment;
0185     }
0186 
0187     /**
0188      * Return the docblock
0189      *
0190      * @param  string $reflectionClass Reflection class to use
0191      * @return Zend_Reflection_Docblock
0192      */
0193     public function getDocblock($reflectionClass = 'Zend_Reflection_Docblock')
0194     {
0195         $instance = new $reflectionClass($this);
0196         if (!$instance instanceof Zend_Reflection_Docblock) {
0197             // require_once 'Zend/Reflection/Exception.php';
0198             throw new Zend_Reflection_Exception('Invalid reflection class specified; must extend Zend_Reflection_Docblock');
0199         }
0200         return $instance;
0201     }
0202 
0203     /**
0204      * Return the reflection classes of the classes found inside this file
0205      *
0206      * @param  string $reflectionClass Name of reflection class to use for instances
0207      * @return array Array of Zend_Reflection_Class instances
0208      */
0209     public function getClasses($reflectionClass = 'Zend_Reflection_Class')
0210     {
0211         $classes = array();
0212         foreach ($this->_classes as $class) {
0213             $instance = new $reflectionClass($class);
0214             if (!$instance instanceof Zend_Reflection_Class) {
0215                 // require_once 'Zend/Reflection/Exception.php';
0216                 throw new Zend_Reflection_Exception('Invalid reflection class provided; must extend Zend_Reflection_Class');
0217             }
0218             $classes[] = $instance;
0219         }
0220         return $classes;
0221     }
0222 
0223     /**
0224      * Return the reflection functions of the functions found inside this file
0225      *
0226      * @param  string $reflectionClass Name of reflection class to use for instances
0227      * @return array Array of Zend_Reflection_Functions
0228      */
0229     public function getFunctions($reflectionClass = 'Zend_Reflection_Function')
0230     {
0231         $functions = array();
0232         foreach ($this->_functions as $function) {
0233             $instance = new $reflectionClass($function);
0234             if (!$instance instanceof Zend_Reflection_Function) {
0235                 // require_once 'Zend/Reflection/Exception.php';
0236                 throw new Zend_Reflection_Exception('Invalid reflection class provided; must extend Zend_Reflection_Function');
0237             }
0238             $functions[] = $instance;
0239         }
0240         return $functions;
0241     }
0242 
0243     /**
0244      * Retrieve the reflection class of a given class found in this file
0245      *
0246      * @param  null|string $name
0247      * @param  string $reflectionClass Reflection class to use when creating reflection instance
0248      * @return Zend_Reflection_Class
0249      * @throws Zend_Reflection_Exception for invalid class name or invalid reflection class
0250      */
0251     public function getClass($name = null, $reflectionClass = 'Zend_Reflection_Class')
0252     {
0253         if ($name === null) {
0254             reset($this->_classes);
0255             $selected = current($this->_classes);
0256             $instance = new $reflectionClass($selected);
0257             if (!$instance instanceof Zend_Reflection_Class) {
0258                 // require_once 'Zend/Reflection/Exception.php';
0259                 throw new Zend_Reflection_Exception('Invalid reflection class given; must extend Zend_Reflection_Class');
0260             }
0261             return $instance;
0262         }
0263 
0264         if (in_array($name, $this->_classes)) {
0265             $instance = new $reflectionClass($name);
0266             if (!$instance instanceof Zend_Reflection_Class) {
0267                 // require_once 'Zend/Reflection/Exception.php';
0268                 throw new Zend_Reflection_Exception('Invalid reflection class given; must extend Zend_Reflection_Class');
0269             }
0270             return $instance;
0271         }
0272 
0273         // require_once 'Zend/Reflection/Exception.php';
0274         throw new Zend_Reflection_Exception('Class by name ' . $name . ' not found.');
0275     }
0276 
0277     /**
0278      * Return the full contents of file
0279      *
0280      * @return string
0281      */
0282     public function getContents()
0283     {
0284         return $this->_contents;
0285     }
0286 
0287     /**
0288      * Serialize to string
0289      *
0290      * Required by the Reflector interface
0291      *
0292      * @todo   What should this serialization look like?
0293      * @return string
0294      */
0295     public function __toString()
0296     {
0297         return '';
0298     }
0299 
0300     /**
0301      * This method does the work of "reflecting" the file
0302      *
0303      * Uses PHP's tokenizer to perform file reflection.
0304      *
0305      * @return void
0306      */
0307     protected function _reflect()
0308     {
0309         $contents = $this->_contents;
0310         $tokens   = token_get_all($contents);
0311 
0312         $functionTrapped           = false;
0313         $classTrapped              = false;
0314         $requireTrapped            = false;
0315         $embeddedVariableTrapped   = false;
0316         $openBraces                = 0;
0317 
0318         $this->_checkFileDocBlock($tokens);
0319 
0320         foreach ($tokens as $token) {
0321             /*
0322              * Tokens are characters representing symbols or arrays
0323              * representing strings. The keys/values in the arrays are
0324              *
0325              * - 0 => token id,
0326              * - 1 => string,
0327              * - 2 => line number
0328              *
0329              * Token ID's are explained here:
0330              * http://www.php.net/manual/en/tokens.php.
0331              */
0332 
0333             if (is_array($token)) {
0334                 $type    = $token[0];
0335                 $value   = $token[1];
0336                 $lineNum = $token[2];
0337             } else {
0338                 // It's a symbol
0339                 // Maintain the count of open braces
0340                 if ($token == '{') {
0341                     $openBraces++;
0342                 } else if ($token == '}') {
0343                     if ( $embeddedVariableTrapped ) {
0344                         $embeddedVariableTrapped = false;
0345                     } else {
0346                         $openBraces--;
0347                     }
0348                 }
0349 
0350                 continue;
0351             }
0352 
0353             switch ($type) {
0354                 case T_STRING_VARNAME:
0355                 case T_DOLLAR_OPEN_CURLY_BRACES:
0356                 case T_CURLY_OPEN:
0357                     $embeddedVariableTrapped = true;
0358                     continue;
0359 
0360                 // Name of something
0361                 case T_STRING:
0362                     if ($functionTrapped) {
0363                         $this->_functions[] = $value;
0364                         $functionTrapped = false;
0365                     } elseif ($classTrapped) {
0366                         $this->_classes[] = $value;
0367                         $classTrapped = false;
0368                     }
0369                     continue;
0370 
0371                 // Required file names are T_CONSTANT_ENCAPSED_STRING
0372                 case T_CONSTANT_ENCAPSED_STRING:
0373                     if ($requireTrapped) {
0374                         $this->_requiredFiles[] = $value ."\n";
0375                         $requireTrapped = false;
0376                     }
0377                     continue;
0378 
0379                 // Functions
0380                 case T_FUNCTION:
0381                     if ($openBraces == 0) {
0382                         $functionTrapped = true;
0383                     }
0384                     break;
0385 
0386                 // Classes
0387                 case T_CLASS:
0388                 case T_INTERFACE:
0389                     $classTrapped = true;
0390                     break;
0391 
0392                 // All types of requires
0393                 case T_REQUIRE:
0394                 case T_REQUIRE_ONCE:
0395                 case T_INCLUDE:
0396                 case T_INCLUDE_ONCE:
0397                     $requireTrapped = true;
0398                     break;
0399 
0400                 // Default case: do nothing
0401                 default:
0402                     break;
0403             }
0404         }
0405 
0406         $this->_endLine = count(explode("\n", $this->_contents));
0407     }
0408 
0409     /**
0410      * Validate / check a file level docblock
0411      *
0412      * @param  array $tokens Array of tokenizer tokens
0413      * @return void
0414      */
0415     protected function _checkFileDocBlock($tokens) {
0416         foreach ($tokens as $token) {
0417             $type    = $token[0];
0418             $value   = $token[1];
0419             $lineNum = $token[2];
0420             if(($type == T_OPEN_TAG) || ($type == T_WHITESPACE)) {
0421                 continue;
0422             } elseif ($type == T_DOC_COMMENT) {
0423                 $this->_docComment = $value;
0424                 $this->_startLine  = $lineNum + substr_count($value, "\n") + 1;
0425                 return;
0426             } else {
0427                 // Only whitespace is allowed before file docblocks
0428                 return;
0429             }
0430         }
0431     }
0432 }