web/Zend/CodeGenerator/Php/File.php
changeset 0 4eba9c11703f
equal deleted inserted replaced
-1:000000000000 0:4eba9c11703f
       
     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_CodeGenerator
       
    17  * @subpackage PHP
       
    18  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    19  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
    20  * @version    $Id: File.php 20096 2010-01-06 02:05:09Z bkarwin $
       
    21  */
       
    22 
       
    23 /**
       
    24  * @see Zend_CodeGenerator_Php_Abstract
       
    25  */
       
    26 require_once 'Zend/CodeGenerator/Php/Abstract.php';
       
    27 
       
    28 /**
       
    29  * @see Zend_CodeGenerator_Php_Class
       
    30  */
       
    31 require_once 'Zend/CodeGenerator/Php/Class.php';
       
    32 
       
    33 /**
       
    34  * @category   Zend
       
    35  * @package    Zend_CodeGenerator
       
    36  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    37  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
    38  */
       
    39 class Zend_CodeGenerator_Php_File extends Zend_CodeGenerator_Php_Abstract
       
    40 {
       
    41 
       
    42     /**
       
    43      * @var array Array of Zend_CodeGenerator_Php_File
       
    44      */
       
    45     protected static $_fileCodeGenerators = array();
       
    46 
       
    47     /**#@+
       
    48      * @var string
       
    49      */
       
    50     protected static $_markerDocblock = '/* Zend_CodeGenerator_Php_File-DocblockMarker */';
       
    51     protected static $_markerRequire = '/* Zend_CodeGenerator_Php_File-RequireMarker: {?} */';
       
    52     protected static $_markerClass = '/* Zend_CodeGenerator_Php_File-ClassMarker: {?} */';
       
    53     /**#@-*/
       
    54 
       
    55     /**
       
    56      * @var string
       
    57      */
       
    58     protected $_filename = null;
       
    59 
       
    60     /**
       
    61      * @var Zend_CodeGenerator_Php_Docblock
       
    62      */
       
    63     protected $_docblock = null;
       
    64 
       
    65     /**
       
    66      * @var array
       
    67      */
       
    68     protected $_requiredFiles = array();
       
    69 
       
    70     /**
       
    71      * @var array
       
    72      */
       
    73     protected $_classes = array();
       
    74 
       
    75     /**
       
    76      * @var string
       
    77      */
       
    78     protected $_body = null;
       
    79 
       
    80     public static function registerFileCodeGenerator(Zend_CodeGenerator_Php_File $fileCodeGenerator, $fileName = null)
       
    81     {
       
    82         if ($fileName == null) {
       
    83             $fileName = $fileCodeGenerator->getFilename();
       
    84         }
       
    85 
       
    86         if ($fileName == '') {
       
    87             require_once 'Zend/CodeGenerator/Php/Exception.php';
       
    88             throw new Zend_CodeGenerator_Php_Exception('FileName does not exist.');
       
    89         }
       
    90 
       
    91         // cannot use realpath since the file might not exist, but we do need to have the index
       
    92         // in the same DIRECTORY_SEPARATOR that realpath would use:
       
    93         $fileName = str_replace(array('\\', '/'), DIRECTORY_SEPARATOR, $fileName);
       
    94 
       
    95         self::$_fileCodeGenerators[$fileName] = $fileCodeGenerator;
       
    96 
       
    97     }
       
    98 
       
    99     /**
       
   100      * fromReflectedFilePath() - use this if you intend on generating code generation objects based on the same file.
       
   101      * This will keep previous changes to the file in tact during the same PHP process
       
   102      *
       
   103      * @param string $filePath
       
   104      * @param bool $usePreviousCodeGeneratorIfItExists
       
   105      * @param bool $includeIfNotAlreadyIncluded
       
   106      * @return Zend_CodeGenerator_Php_File
       
   107      */
       
   108     public static function fromReflectedFileName($filePath, $usePreviousCodeGeneratorIfItExists = true, $includeIfNotAlreadyIncluded = true)
       
   109     {
       
   110         $realpath = realpath($filePath);
       
   111 
       
   112         if ($realpath === false) {
       
   113             if ( ($realpath = Zend_Reflection_file::findRealpathInIncludePath($filePath)) === false) {
       
   114                 require_once 'Zend/CodeGenerator/Php/Exception.php';
       
   115                 throw new Zend_CodeGenerator_Php_Exception('No file for ' . $realpath . ' was found.');
       
   116             }
       
   117         }
       
   118 
       
   119         if ($usePreviousCodeGeneratorIfItExists && isset(self::$_fileCodeGenerators[$realpath])) {
       
   120             return self::$_fileCodeGenerators[$realpath];
       
   121         }
       
   122 
       
   123         if ($includeIfNotAlreadyIncluded && !in_array($realpath, get_included_files())) {
       
   124             include $realpath;
       
   125         }
       
   126 
       
   127         $codeGenerator = self::fromReflection(($fileReflector = new Zend_Reflection_File($realpath)));
       
   128 
       
   129         if (!isset(self::$_fileCodeGenerators[$fileReflector->getFileName()])) {
       
   130             self::$_fileCodeGenerators[$fileReflector->getFileName()] = $codeGenerator;
       
   131         }
       
   132 
       
   133         return $codeGenerator;
       
   134     }
       
   135 
       
   136     /**
       
   137      * fromReflection()
       
   138      *
       
   139      * @param Zend_Reflection_File $reflectionFile
       
   140      * @return Zend_CodeGenerator_Php_File
       
   141      */
       
   142     public static function fromReflection(Zend_Reflection_File $reflectionFile)
       
   143     {
       
   144         $file = new self();
       
   145 
       
   146         $file->setSourceContent($reflectionFile->getContents());
       
   147         $file->setSourceDirty(false);
       
   148 
       
   149         $body = $reflectionFile->getContents();
       
   150 
       
   151         // @todo this whole area needs to be reworked with respect to how body lines are processed
       
   152         foreach ($reflectionFile->getClasses() as $class) {
       
   153             $file->setClass(Zend_CodeGenerator_Php_Class::fromReflection($class));
       
   154             $classStartLine = $class->getStartLine(true);
       
   155             $classEndLine = $class->getEndLine();
       
   156 
       
   157             $bodyLines = explode("\n", $body);
       
   158             $bodyReturn = array();
       
   159             for ($lineNum = 1; $lineNum <= count($bodyLines); $lineNum++) {
       
   160                 if ($lineNum == $classStartLine) {
       
   161                     $bodyReturn[] = str_replace('?', $class->getName(), self::$_markerClass);  //'/* Zend_CodeGenerator_Php_File-ClassMarker: {' . $class->getName() . '} */';
       
   162                     $lineNum = $classEndLine;
       
   163                 } else {
       
   164                     $bodyReturn[] = $bodyLines[$lineNum - 1]; // adjust for index -> line conversion
       
   165                 }
       
   166             }
       
   167             $body = implode("\n", $bodyReturn);
       
   168             unset($bodyLines, $bodyReturn, $classStartLine, $classEndLine);
       
   169         }
       
   170 
       
   171         if (($reflectionFile->getDocComment() != '')) {
       
   172             $docblock = $reflectionFile->getDocblock();
       
   173             $file->setDocblock(Zend_CodeGenerator_Php_Docblock::fromReflection($docblock));
       
   174 
       
   175             $bodyLines = explode("\n", $body);
       
   176             $bodyReturn = array();
       
   177             for ($lineNum = 1; $lineNum <= count($bodyLines); $lineNum++) {
       
   178                 if ($lineNum == $docblock->getStartLine()) {
       
   179                     $bodyReturn[] = str_replace('?', $class->getName(), self::$_markerDocblock);  //'/* Zend_CodeGenerator_Php_File-ClassMarker: {' . $class->getName() . '} */';
       
   180                     $lineNum = $docblock->getEndLine();
       
   181                 } else {
       
   182                     $bodyReturn[] = $bodyLines[$lineNum - 1]; // adjust for index -> line conversion
       
   183                 }
       
   184             }
       
   185             $body = implode("\n", $bodyReturn);
       
   186             unset($bodyLines, $bodyReturn, $classStartLine, $classEndLine);
       
   187         }
       
   188 
       
   189         $file->setBody($body);
       
   190 
       
   191         return $file;
       
   192     }
       
   193 
       
   194     /**
       
   195      * setDocblock() Set the docblock
       
   196      *
       
   197      * @param Zend_CodeGenerator_Php_Docblock|array|string $docblock
       
   198      * @return Zend_CodeGenerator_Php_File
       
   199      */
       
   200     public function setDocblock($docblock)
       
   201     {
       
   202         if (is_string($docblock)) {
       
   203             $docblock = array('shortDescription' => $docblock);
       
   204         }
       
   205 
       
   206         if (is_array($docblock)) {
       
   207             $docblock = new Zend_CodeGenerator_Php_Docblock($docblock);
       
   208         } elseif (!$docblock instanceof Zend_CodeGenerator_Php_Docblock) {
       
   209             require_once 'Zend/CodeGenerator/Php/Exception.php';
       
   210             throw new Zend_CodeGenerator_Php_Exception('setDocblock() is expecting either a string, array or an instance of Zend_CodeGenerator_Php_Docblock');
       
   211         }
       
   212 
       
   213         $this->_docblock = $docblock;
       
   214         return $this;
       
   215     }
       
   216 
       
   217     /**
       
   218      * Get docblock
       
   219      *
       
   220      * @return Zend_CodeGenerator_Php_Docblock
       
   221      */
       
   222     public function getDocblock()
       
   223     {
       
   224         return $this->_docblock;
       
   225     }
       
   226 
       
   227     /**
       
   228      * setRequiredFiles
       
   229      *
       
   230      * @param array $requiredFiles
       
   231      * @return Zend_CodeGenerator_Php_File
       
   232      */
       
   233     public function setRequiredFiles($requiredFiles)
       
   234     {
       
   235         $this->_requiredFiles = $requiredFiles;
       
   236         return $this;
       
   237     }
       
   238 
       
   239     /**
       
   240      * getRequiredFiles()
       
   241      *
       
   242      * @return array
       
   243      */
       
   244     public function getRequiredFiles()
       
   245     {
       
   246         return $this->_requiredFiles;
       
   247     }
       
   248 
       
   249     /**
       
   250      * setClasses()
       
   251      *
       
   252      * @param array $classes
       
   253      * @return Zend_CodeGenerator_Php_File
       
   254      */
       
   255     public function setClasses(Array $classes)
       
   256     {
       
   257         foreach ($classes as $class) {
       
   258             $this->setClass($class);
       
   259         }
       
   260         return $this;
       
   261     }
       
   262 
       
   263     /**
       
   264      * getClass()
       
   265      *
       
   266      * @param string $name
       
   267      * @return Zend_CodeGenerator_Php_Class
       
   268      */
       
   269     public function getClass($name = null)
       
   270     {
       
   271         if ($name == null) {
       
   272             reset($this->_classes);
       
   273             return current($this->_classes);
       
   274         }
       
   275 
       
   276         return $this->_classes[$name];
       
   277     }
       
   278 
       
   279     /**
       
   280      * setClass()
       
   281      *
       
   282      * @param Zend_CodeGenerator_Php_Class|array $class
       
   283      * @return Zend_CodeGenerator_Php_File
       
   284      */
       
   285     public function setClass($class)
       
   286     {
       
   287         if (is_array($class)) {
       
   288             $class = new Zend_CodeGenerator_Php_Class($class);
       
   289             $className = $class->getName();
       
   290         } elseif ($class instanceof Zend_CodeGenerator_Php_Class) {
       
   291             $className = $class->getName();
       
   292         } else {
       
   293             require_once 'Zend/CodeGenerator/Php/Exception.php';
       
   294             throw new Zend_CodeGenerator_Php_Exception('Expecting either an array or an instance of Zend_CodeGenerator_Php_Class');
       
   295         }
       
   296 
       
   297         // @todo check for dup here
       
   298 
       
   299         $this->_classes[$className] = $class;
       
   300         return $this;
       
   301     }
       
   302 
       
   303     /**
       
   304      * setFilename()
       
   305      *
       
   306      * @param string $filename
       
   307      * @return Zend_CodeGenerator_Php_File
       
   308      */
       
   309     public function setFilename($filename)
       
   310     {
       
   311         $this->_filename = $filename;
       
   312         return $this;
       
   313     }
       
   314 
       
   315     /**
       
   316      * getFilename()
       
   317      *
       
   318      * @return string
       
   319      */
       
   320     public function getFilename()
       
   321     {
       
   322         return $this->_filename;
       
   323     }
       
   324 
       
   325     /**
       
   326      * getClasses()
       
   327      *
       
   328      * @return array Array of Zend_CodeGenerator_Php_Class
       
   329      */
       
   330     public function getClasses()
       
   331     {
       
   332         return $this->_classes;
       
   333     }
       
   334 
       
   335     /**
       
   336      * setBody()
       
   337      *
       
   338      * @param string $body
       
   339      * @return Zend_CodeGenerator_Php_File
       
   340      */
       
   341     public function setBody($body)
       
   342     {
       
   343         $this->_body = $body;
       
   344         return $this;
       
   345     }
       
   346 
       
   347     /**
       
   348      * getBody()
       
   349      *
       
   350      * @return string
       
   351      */
       
   352     public function getBody()
       
   353     {
       
   354         return $this->_body;
       
   355     }
       
   356 
       
   357     /**
       
   358      * isSourceDirty()
       
   359      *
       
   360      * @return bool
       
   361      */
       
   362     public function isSourceDirty()
       
   363     {
       
   364         if (($docblock = $this->getDocblock()) && $docblock->isSourceDirty()) {
       
   365             return true;
       
   366         }
       
   367 
       
   368         foreach ($this->_classes as $class) {
       
   369             if ($class->isSourceDirty()) {
       
   370                 return true;
       
   371             }
       
   372         }
       
   373 
       
   374         return parent::isSourceDirty();
       
   375     }
       
   376 
       
   377     /**
       
   378      * generate()
       
   379      *
       
   380      * @return string
       
   381      */
       
   382     public function generate()
       
   383     {
       
   384         if ($this->isSourceDirty() === false) {
       
   385             return $this->_sourceContent;
       
   386         }
       
   387 
       
   388         $output = '';
       
   389 
       
   390         // start with the body (if there), or open tag
       
   391         if (preg_match('#(?:\s*)<\?php#', $this->getBody()) == false) {
       
   392             $output = '<?php' . self::LINE_FEED;
       
   393         }
       
   394 
       
   395         // if there are markers, put the body into the output
       
   396         $body = $this->getBody();
       
   397         if (preg_match('#/\* Zend_CodeGenerator_Php_File-(.*?)Marker:#', $body)) {
       
   398             $output .= $body;
       
   399             $body    = '';
       
   400         }
       
   401 
       
   402         // Add file docblock, if any
       
   403         if (null !== ($docblock = $this->getDocblock())) {
       
   404             $docblock->setIndentation('');
       
   405             $regex = preg_quote(self::$_markerDocblock, '#');
       
   406             if (preg_match('#'.$regex.'#', $output)) {
       
   407                 $output  = preg_replace('#'.$regex.'#', $docblock->generate(), $output, 1);
       
   408             } else {
       
   409                 $output .= $docblock->generate() . self::LINE_FEED;
       
   410             }
       
   411         }
       
   412 
       
   413         // newline
       
   414         $output .= self::LINE_FEED;
       
   415 
       
   416         // process required files
       
   417         // @todo marker replacement for required files
       
   418         $requiredFiles = $this->getRequiredFiles();
       
   419         if (!empty($requiredFiles)) {
       
   420             foreach ($requiredFiles as $requiredFile) {
       
   421                 $output .= 'require_once \'' . $requiredFile . '\';' . self::LINE_FEED;
       
   422             }
       
   423 
       
   424             $output .= self::LINE_FEED;
       
   425         }
       
   426 
       
   427         // process classes
       
   428         $classes = $this->getClasses();
       
   429         if (!empty($classes)) {
       
   430             foreach ($classes as $class) {
       
   431                 $regex = str_replace('?', $class->getName(), self::$_markerClass);
       
   432                 $regex = preg_quote($regex, '#');
       
   433                 if (preg_match('#'.$regex.'#', $output)) {
       
   434                     $output = preg_replace('#'.$regex.'#', $class->generate(), $output, 1);
       
   435                 } else {
       
   436                     $output .= $class->generate() . self::LINE_FEED;
       
   437                 }
       
   438             }
       
   439 
       
   440         }
       
   441 
       
   442         if (!empty($body)) {
       
   443 
       
   444             // add an extra space betwee clsses and
       
   445             if (!empty($classes)) {
       
   446                 $output .= self::LINE_FEED;
       
   447             }
       
   448 
       
   449             $output .= $body;
       
   450         }
       
   451 
       
   452         return $output;
       
   453     }
       
   454 
       
   455     public function write()
       
   456     {
       
   457         if ($this->_filename == '' || !is_writable(dirname($this->_filename))) {
       
   458             require_once 'Zend/CodeGenerator/Php/Exception.php';
       
   459             throw new Zend_CodeGenerator_Php_Exception('This code generator object is not writable.');
       
   460         }
       
   461         file_put_contents($this->_filename, $this->generate());
       
   462         return $this;
       
   463     }
       
   464 
       
   465 }