File indexing completed on 2025-01-19 05:20:59

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_CodeGenerator
0017  * @subpackage PHP
0018  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0019  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0020  * @version    $Id$
0021  */
0022 
0023 /**
0024  * @see Zend_CodeGenerator_Php_Abstract
0025  */
0026 // require_once 'Zend/CodeGenerator/Php/Abstract.php';
0027 
0028 /**
0029  * @see Zend_CodeGenerator_Php_Class
0030  */
0031 // require_once 'Zend/CodeGenerator/Php/Class.php';
0032 
0033 /**
0034  * @category   Zend
0035  * @package    Zend_CodeGenerator
0036  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0037  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0038  */
0039 class Zend_CodeGenerator_Php_File extends Zend_CodeGenerator_Php_Abstract
0040 {
0041 
0042     /**
0043      * @var array Array of Zend_CodeGenerator_Php_File
0044      */
0045     protected static $_fileCodeGenerators = array();
0046 
0047     /**#@+
0048      * @var string
0049      */
0050     protected static $_markerDocblock = '/* Zend_CodeGenerator_Php_File-DocblockMarker */';
0051     protected static $_markerRequire = '/* Zend_CodeGenerator_Php_File-RequireMarker: {?} */';
0052     protected static $_markerClass = '/* Zend_CodeGenerator_Php_File-ClassMarker: {?} */';
0053     /**#@-*/
0054 
0055     /**
0056      * @var string
0057      */
0058     protected $_filename = null;
0059 
0060     /**
0061      * @var Zend_CodeGenerator_Php_Docblock
0062      */
0063     protected $_docblock = null;
0064 
0065     /**
0066      * @var array
0067      */
0068     protected $_requiredFiles = array();
0069 
0070     /**
0071      * @var array
0072      */
0073     protected $_classes = array();
0074 
0075     /**
0076      * @var string
0077      */
0078     protected $_body = null;
0079 
0080     public static function registerFileCodeGenerator(Zend_CodeGenerator_Php_File $fileCodeGenerator, $fileName = null)
0081     {
0082         if ($fileName == null) {
0083             $fileName = $fileCodeGenerator->getFilename();
0084         }
0085 
0086         if ($fileName == '') {
0087             // require_once 'Zend/CodeGenerator/Php/Exception.php';
0088             throw new Zend_CodeGenerator_Php_Exception('FileName does not exist.');
0089         }
0090 
0091         // cannot use realpath since the file might not exist, but we do need to have the index
0092         // in the same DIRECTORY_SEPARATOR that realpath would use:
0093         $fileName = str_replace(array('\\', '/'), DIRECTORY_SEPARATOR, $fileName);
0094 
0095         self::$_fileCodeGenerators[$fileName] = $fileCodeGenerator;
0096 
0097     }
0098 
0099     /**
0100      * fromReflectedFileName() - use this if you intend on generating code generation objects based on the same file.
0101      * This will keep previous changes to the file in tact during the same PHP process
0102      *
0103      * @param string $filePath
0104      * @param bool $usePreviousCodeGeneratorIfItExists
0105      * @param bool $includeIfNotAlreadyIncluded
0106      * @return Zend_CodeGenerator_Php_File
0107      */
0108     public static function fromReflectedFileName($filePath, $usePreviousCodeGeneratorIfItExists = true, $includeIfNotAlreadyIncluded = true)
0109     {
0110         $realpath = realpath($filePath);
0111 
0112         if ($realpath === false) {
0113             if ( ($realpath = Zend_Reflection_File::findRealpathInIncludePath($filePath)) === false) {
0114                 // require_once 'Zend/CodeGenerator/Php/Exception.php';
0115                 throw new Zend_CodeGenerator_Php_Exception('No file for ' . $realpath . ' was found.');
0116             }
0117         }
0118 
0119         if ($usePreviousCodeGeneratorIfItExists && isset(self::$_fileCodeGenerators[$realpath])) {
0120             return self::$_fileCodeGenerators[$realpath];
0121         }
0122 
0123         if ($includeIfNotAlreadyIncluded && !in_array($realpath, get_included_files())) {
0124             include $realpath;
0125         }
0126 
0127         $codeGenerator = self::fromReflection(($fileReflector = new Zend_Reflection_File($realpath)));
0128 
0129         if (!isset(self::$_fileCodeGenerators[$fileReflector->getFileName()])) {
0130             self::$_fileCodeGenerators[$fileReflector->getFileName()] = $codeGenerator;
0131         }
0132 
0133         return $codeGenerator;
0134     }
0135 
0136     /**
0137      * fromReflection()
0138      *
0139      * @param Zend_Reflection_File $reflectionFile
0140      * @return Zend_CodeGenerator_Php_File
0141      */
0142     public static function fromReflection(Zend_Reflection_File $reflectionFile)
0143     {
0144         $file = new self();
0145 
0146         $file->setSourceContent($reflectionFile->getContents());
0147         $file->setSourceDirty(false);
0148 
0149         $body = $reflectionFile->getContents();
0150 
0151         // @todo this whole area needs to be reworked with respect to how body lines are processed
0152         foreach ($reflectionFile->getClasses() as $class) {
0153             $file->setClass(Zend_CodeGenerator_Php_Class::fromReflection($class));
0154             $classStartLine = $class->getStartLine(true);
0155             $classEndLine = $class->getEndLine();
0156 
0157             $bodyLines = explode("\n", $body);
0158             $bodyReturn = array();
0159             for ($lineNum = 1; $lineNum <= count($bodyLines); $lineNum++) {
0160                 if ($lineNum == $classStartLine) {
0161                     $bodyReturn[] = str_replace('?', $class->getName(), self::$_markerClass);  //'/* Zend_CodeGenerator_Php_File-ClassMarker: {' . $class->getName() . '} */';
0162                     $lineNum = $classEndLine;
0163                 } else {
0164                     $bodyReturn[] = $bodyLines[$lineNum - 1]; // adjust for index -> line conversion
0165                 }
0166             }
0167             $body = implode("\n", $bodyReturn);
0168             unset($bodyLines, $bodyReturn, $classStartLine, $classEndLine);
0169         }
0170 
0171         if (($reflectionFile->getDocComment() != '')) {
0172             $docblock = $reflectionFile->getDocblock();
0173             $file->setDocblock(Zend_CodeGenerator_Php_Docblock::fromReflection($docblock));
0174 
0175             $bodyLines = explode("\n", $body);
0176             $bodyReturn = array();
0177             for ($lineNum = 1; $lineNum <= count($bodyLines); $lineNum++) {
0178                 if ($lineNum == $docblock->getStartLine()) {
0179                     $bodyReturn[] = str_replace('?', $class->getName(), self::$_markerDocblock);  //'/* Zend_CodeGenerator_Php_File-ClassMarker: {' . $class->getName() . '} */';
0180                     $lineNum = $docblock->getEndLine();
0181                 } else {
0182                     $bodyReturn[] = $bodyLines[$lineNum - 1]; // adjust for index -> line conversion
0183                 }
0184             }
0185             $body = implode("\n", $bodyReturn);
0186             unset($bodyLines, $bodyReturn, $classStartLine, $classEndLine);
0187         }
0188 
0189         $file->setBody($body);
0190 
0191         return $file;
0192     }
0193 
0194     /**
0195      * setDocblock() Set the docblock
0196      *
0197      * @param Zend_CodeGenerator_Php_Docblock|array|string $docblock
0198      * @return Zend_CodeGenerator_Php_File
0199      */
0200     public function setDocblock($docblock)
0201     {
0202         if (is_string($docblock)) {
0203             $docblock = array('shortDescription' => $docblock);
0204         }
0205 
0206         if (is_array($docblock)) {
0207             $docblock = new Zend_CodeGenerator_Php_Docblock($docblock);
0208         } elseif (!$docblock instanceof Zend_CodeGenerator_Php_Docblock) {
0209             // require_once 'Zend/CodeGenerator/Php/Exception.php';
0210             throw new Zend_CodeGenerator_Php_Exception('setDocblock() is expecting either a string, array or an instance of Zend_CodeGenerator_Php_Docblock');
0211         }
0212 
0213         $this->_docblock = $docblock;
0214         return $this;
0215     }
0216 
0217     /**
0218      * Get docblock
0219      *
0220      * @return Zend_CodeGenerator_Php_Docblock
0221      */
0222     public function getDocblock()
0223     {
0224         return $this->_docblock;
0225     }
0226 
0227     /**
0228      * setRequiredFiles
0229      *
0230      * @param array $requiredFiles
0231      * @return Zend_CodeGenerator_Php_File
0232      */
0233     public function setRequiredFiles($requiredFiles)
0234     {
0235         $this->_requiredFiles = $requiredFiles;
0236         return $this;
0237     }
0238 
0239     /**
0240      * getRequiredFiles()
0241      *
0242      * @return array
0243      */
0244     public function getRequiredFiles()
0245     {
0246         return $this->_requiredFiles;
0247     }
0248 
0249     /**
0250      * setClasses()
0251      *
0252      * @param array $classes
0253      * @return Zend_CodeGenerator_Php_File
0254      */
0255     public function setClasses(Array $classes)
0256     {
0257         foreach ($classes as $class) {
0258             $this->setClass($class);
0259         }
0260         return $this;
0261     }
0262 
0263     /**
0264      * getClass()
0265      *
0266      * @param string $name
0267      * @return Zend_CodeGenerator_Php_Class
0268      */
0269     public function getClass($name = null)
0270     {
0271         if ($name == null) {
0272             reset($this->_classes);
0273             return current($this->_classes);
0274         }
0275 
0276         return $this->_classes[$name];
0277     }
0278 
0279     /**
0280      * setClass()
0281      *
0282      * @param Zend_CodeGenerator_Php_Class|array $class
0283      * @return Zend_CodeGenerator_Php_File
0284      */
0285     public function setClass($class)
0286     {
0287         if (is_array($class)) {
0288             $class = new Zend_CodeGenerator_Php_Class($class);
0289             $className = $class->getName();
0290         } elseif ($class instanceof Zend_CodeGenerator_Php_Class) {
0291             $className = $class->getName();
0292         } else {
0293             // require_once 'Zend/CodeGenerator/Php/Exception.php';
0294             throw new Zend_CodeGenerator_Php_Exception('Expecting either an array or an instance of Zend_CodeGenerator_Php_Class');
0295         }
0296 
0297         // @todo check for dup here
0298 
0299         $this->_classes[$className] = $class;
0300         return $this;
0301     }
0302 
0303     /**
0304      * setFilename()
0305      *
0306      * @param string $filename
0307      * @return Zend_CodeGenerator_Php_File
0308      */
0309     public function setFilename($filename)
0310     {
0311         $this->_filename = $filename;
0312         return $this;
0313     }
0314 
0315     /**
0316      * getFilename()
0317      *
0318      * @return string
0319      */
0320     public function getFilename()
0321     {
0322         return $this->_filename;
0323     }
0324 
0325     /**
0326      * getClasses()
0327      *
0328      * @return array Array of Zend_CodeGenerator_Php_Class
0329      */
0330     public function getClasses()
0331     {
0332         return $this->_classes;
0333     }
0334 
0335     /**
0336      * setBody()
0337      *
0338      * @param string $body
0339      * @return Zend_CodeGenerator_Php_File
0340      */
0341     public function setBody($body)
0342     {
0343         $this->_body = $body;
0344         return $this;
0345     }
0346 
0347     /**
0348      * getBody()
0349      *
0350      * @return string
0351      */
0352     public function getBody()
0353     {
0354         return $this->_body;
0355     }
0356 
0357     /**
0358      * isSourceDirty()
0359      *
0360      * @return bool
0361      */
0362     public function isSourceDirty()
0363     {
0364         if (($docblock = $this->getDocblock()) && $docblock->isSourceDirty()) {
0365             return true;
0366         }
0367 
0368         foreach ($this->_classes as $class) {
0369             if ($class->isSourceDirty()) {
0370                 return true;
0371             }
0372         }
0373 
0374         return parent::isSourceDirty();
0375     }
0376 
0377     /**
0378      * generate()
0379      *
0380      * @return string
0381      */
0382     public function generate()
0383     {
0384         if ($this->isSourceDirty() === false) {
0385             return $this->_sourceContent;
0386         }
0387 
0388         $output = '';
0389 
0390         // start with the body (if there), or open tag
0391         if (preg_match('#(?:\s*)<\?php#', $this->getBody()) == false) {
0392             $output = '<?php' . self::LINE_FEED;
0393         }
0394 
0395         // if there are markers, put the body into the output
0396         $body = $this->getBody();
0397         if (preg_match('#/\* Zend_CodeGenerator_Php_File-(.*?)Marker#', $body)) {
0398             $output .= $body;
0399             $body    = '';
0400         }
0401 
0402         // Add file docblock, if any
0403         if (null !== ($docblock = $this->getDocblock())) {
0404             $docblock->setIndentation('');
0405             $regex = preg_quote(self::$_markerDocblock, '#');
0406             if (preg_match('#'.$regex.'#', $output)) {
0407                 $output  = preg_replace('#'.$regex.'#', $docblock->generate(), $output, 1);
0408             } else {
0409                 $output .= $docblock->generate() . self::LINE_FEED;
0410             }
0411         }
0412 
0413         // newline
0414         $output .= self::LINE_FEED;
0415 
0416         // process required files
0417         // @todo marker replacement for required files
0418         $requiredFiles = $this->getRequiredFiles();
0419         if (!empty($requiredFiles)) {
0420             foreach ($requiredFiles as $requiredFile) {
0421                 $output .= '// require_once \'' . $requiredFile . '\';' . self::LINE_FEED;
0422             }
0423 
0424             $output .= self::LINE_FEED;
0425         }
0426 
0427         // process classes
0428         $classes = $this->getClasses();
0429         if (!empty($classes)) {
0430             foreach ($classes as $class) {
0431                 if($this->getDocblock() == $class->getDocblock()) {
0432                     $class->setDocblock(null);
0433                 }                   
0434                 $regex = str_replace('?', $class->getName(), self::$_markerClass);
0435                 $regex = preg_quote($regex, '#');
0436                 if (preg_match('#'.$regex.'#', $output)) {
0437                     $output = preg_replace('#'.$regex.'#', $class->generate(), $output, 1);
0438                 } else {
0439                     $output .= $class->generate() . self::LINE_FEED;
0440                 }
0441             }
0442 
0443         }
0444 
0445         if (!empty($body)) {
0446 
0447             // add an extra space betwee clsses and
0448             if (!empty($classes)) {
0449                 $output .= self::LINE_FEED;
0450             }
0451 
0452             $output .= $body;
0453         }
0454 
0455         return $output;
0456     }
0457 
0458     public function write()
0459     {
0460         if ($this->_filename == '' || !is_writable(dirname($this->_filename))) {
0461             // require_once 'Zend/CodeGenerator/Php/Exception.php';
0462             throw new Zend_CodeGenerator_Php_Exception('This code generator object is not writable.');
0463         }
0464         file_put_contents($this->_filename, $this->generate());
0465         return $this;
0466     }
0467 
0468 }