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 }