466 lines
14 KiB
PHP
466 lines
14 KiB
PHP
|
<?php
|
||
|
/**
|
||
|
* Zend Framework
|
||
|
*
|
||
|
* LICENSE
|
||
|
*
|
||
|
* This source file is subject to the new BSD license that is bundled
|
||
|
* with this package in the file LICENSE.txt.
|
||
|
* It is also available through the world-wide-web at this URL:
|
||
|
* http://framework.zend.com/license/new-bsd
|
||
|
* If you did not receive a copy of the license and are unable to
|
||
|
* obtain it through the world-wide-web, please send an email
|
||
|
* to license@zend.com so we can send you a copy immediately.
|
||
|
*
|
||
|
* @category Zend
|
||
|
* @package Zend_CodeGenerator
|
||
|
* @subpackage PHP
|
||
|
* @copyright Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
|
||
|
* @license http://framework.zend.com/license/new-bsd New BSD License
|
||
|
* @version $Id: File.php 16971 2009-07-22 18:05:45Z mikaelkael $
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
* @see Zend_CodeGenerator_Php_Abstract
|
||
|
*/
|
||
|
require_once 'Zend/CodeGenerator/Php/Abstract.php';
|
||
|
|
||
|
/**
|
||
|
* @see Zend_CodeGenerator_Php_Class
|
||
|
*/
|
||
|
require_once 'Zend/CodeGenerator/Php/Class.php';
|
||
|
|
||
|
/**
|
||
|
* @category Zend
|
||
|
* @package Zend_CodeGenerator
|
||
|
* @copyright Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
|
||
|
* @license http://framework.zend.com/license/new-bsd New BSD License
|
||
|
*/
|
||
|
class Zend_CodeGenerator_Php_File extends Zend_CodeGenerator_Php_Abstract
|
||
|
{
|
||
|
|
||
|
/**
|
||
|
* @var array Array of Zend_CodeGenerator_Php_File
|
||
|
*/
|
||
|
protected static $_fileCodeGenerators = array();
|
||
|
|
||
|
/**#@+
|
||
|
* @var string
|
||
|
*/
|
||
|
protected static $_markerDocblock = '/* Zend_CodeGenerator_Php_File-DocblockMarker */';
|
||
|
protected static $_markerRequire = '/* Zend_CodeGenerator_Php_File-RequireMarker: {?} */';
|
||
|
protected static $_markerClass = '/* Zend_CodeGenerator_Php_File-ClassMarker: {?} */';
|
||
|
/**#@-*/
|
||
|
|
||
|
/**
|
||
|
* @var string
|
||
|
*/
|
||
|
protected $_filename = null;
|
||
|
|
||
|
/**
|
||
|
* @var Zend_CodeGenerator_Php_Docblock
|
||
|
*/
|
||
|
protected $_docblock = null;
|
||
|
|
||
|
/**
|
||
|
* @var array
|
||
|
*/
|
||
|
protected $_requiredFiles = array();
|
||
|
|
||
|
/**
|
||
|
* @var array
|
||
|
*/
|
||
|
protected $_classes = array();
|
||
|
|
||
|
/**
|
||
|
* @var string
|
||
|
*/
|
||
|
protected $_body = null;
|
||
|
|
||
|
public static function registerFileCodeGenerator(Zend_CodeGenerator_Php_File $fileCodeGenerator, $fileName = null)
|
||
|
{
|
||
|
if ($fileName == null) {
|
||
|
$fileName = $fileCodeGenerator->getFilename();
|
||
|
}
|
||
|
|
||
|
if ($fileName == '') {
|
||
|
require_once 'Zend/CodeGenerator/Php/Exception.php';
|
||
|
throw new Zend_CodeGenerator_Php_Exception('FileName does not exist.');
|
||
|
}
|
||
|
|
||
|
// cannot use realpath since the file might not exist, but we do need to have the index
|
||
|
// in the same DIRECTORY_SEPARATOR that realpath would use:
|
||
|
$fileName = str_replace(array('\\', '/'), DIRECTORY_SEPARATOR, $fileName);
|
||
|
|
||
|
self::$_fileCodeGenerators[$fileName] = $fileCodeGenerator;
|
||
|
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* fromReflectedFilePath() - use this if you intend on generating code generation objects based on the same file.
|
||
|
* This will keep previous changes to the file in tact during the same PHP process
|
||
|
*
|
||
|
* @param string $filePath
|
||
|
* @param bool $usePreviousCodeGeneratorIfItExists
|
||
|
* @param bool $includeIfNotAlreadyIncluded
|
||
|
* @return Zend_CodeGenerator_Php_File
|
||
|
*/
|
||
|
public static function fromReflectedFileName($filePath, $usePreviousCodeGeneratorIfItExists = true, $includeIfNotAlreadyIncluded = true)
|
||
|
{
|
||
|
$realpath = realpath($filePath);
|
||
|
|
||
|
if ($realpath === false) {
|
||
|
if ( ($realpath = Zend_Reflection_file::findRealpathInIncludePath($filePath)) === false) {
|
||
|
require_once 'Zend/CodeGenerator/Php/Exception.php';
|
||
|
throw new Zend_CodeGenerator_Php_Exception('No file for ' . $realpath . ' was found.');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ($usePreviousCodeGeneratorIfItExists && isset(self::$_fileCodeGenerators[$realpath])) {
|
||
|
return self::$_fileCodeGenerators[$realpath];
|
||
|
}
|
||
|
|
||
|
if ($includeIfNotAlreadyIncluded && !in_array($realpath, get_included_files())) {
|
||
|
include $realpath;
|
||
|
}
|
||
|
|
||
|
$codeGenerator = self::fromReflection(($fileReflector = new Zend_Reflection_File($realpath)));
|
||
|
|
||
|
if (!isset(self::$_fileCodeGenerators[$fileReflector->getFileName()])) {
|
||
|
self::$_fileCodeGenerators[$fileReflector->getFileName()] = $codeGenerator;
|
||
|
}
|
||
|
|
||
|
return $codeGenerator;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* fromReflection()
|
||
|
*
|
||
|
* @param Zend_Reflection_File $reflectionFile
|
||
|
* @return Zend_CodeGenerator_Php_File
|
||
|
*/
|
||
|
public static function fromReflection(Zend_Reflection_File $reflectionFile)
|
||
|
{
|
||
|
$file = new self();
|
||
|
|
||
|
$file->setSourceContent($reflectionFile->getContents());
|
||
|
$file->setSourceDirty(false);
|
||
|
|
||
|
$body = $reflectionFile->getContents();
|
||
|
|
||
|
// @todo this whole area needs to be reworked with respect to how body lines are processed
|
||
|
foreach ($reflectionFile->getClasses() as $class) {
|
||
|
$file->setClass(Zend_CodeGenerator_Php_Class::fromReflection($class));
|
||
|
$classStartLine = $class->getStartLine(true);
|
||
|
$classEndLine = $class->getEndLine();
|
||
|
|
||
|
$bodyLines = explode("\n", $body);
|
||
|
$bodyReturn = array();
|
||
|
for ($lineNum = 1; $lineNum <= count($bodyLines); $lineNum++) {
|
||
|
if ($lineNum == $classStartLine) {
|
||
|
$bodyReturn[] = str_replace('?', $class->getName(), self::$_markerClass); //'/* Zend_CodeGenerator_Php_File-ClassMarker: {' . $class->getName() . '} */';
|
||
|
$lineNum = $classEndLine;
|
||
|
} else {
|
||
|
$bodyReturn[] = $bodyLines[$lineNum - 1]; // adjust for index -> line conversion
|
||
|
}
|
||
|
}
|
||
|
$body = implode("\n", $bodyReturn);
|
||
|
unset($bodyLines, $bodyReturn, $classStartLine, $classEndLine);
|
||
|
}
|
||
|
|
||
|
if (($reflectionFile->getDocComment() != '')) {
|
||
|
$docblock = $reflectionFile->getDocblock();
|
||
|
$file->setDocblock(Zend_CodeGenerator_Php_Docblock::fromReflection($docblock));
|
||
|
|
||
|
$bodyLines = explode("\n", $body);
|
||
|
$bodyReturn = array();
|
||
|
for ($lineNum = 1; $lineNum <= count($bodyLines); $lineNum++) {
|
||
|
if ($lineNum == $docblock->getStartLine()) {
|
||
|
$bodyReturn[] = str_replace('?', $class->getName(), self::$_markerDocblock); //'/* Zend_CodeGenerator_Php_File-ClassMarker: {' . $class->getName() . '} */';
|
||
|
$lineNum = $docblock->getEndLine();
|
||
|
} else {
|
||
|
$bodyReturn[] = $bodyLines[$lineNum - 1]; // adjust for index -> line conversion
|
||
|
}
|
||
|
}
|
||
|
$body = implode("\n", $bodyReturn);
|
||
|
unset($bodyLines, $bodyReturn, $classStartLine, $classEndLine);
|
||
|
}
|
||
|
|
||
|
$file->setBody($body);
|
||
|
|
||
|
return $file;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* setDocblock() Set the docblock
|
||
|
*
|
||
|
* @param Zend_CodeGenerator_Php_Docblock|array|string $docblock
|
||
|
* @return Zend_CodeGenerator_Php_File
|
||
|
*/
|
||
|
public function setDocblock($docblock)
|
||
|
{
|
||
|
if (is_string($docblock)) {
|
||
|
$docblock = array('shortDescription' => $docblock);
|
||
|
}
|
||
|
|
||
|
if (is_array($docblock)) {
|
||
|
$docblock = new Zend_CodeGenerator_Php_Docblock($docblock);
|
||
|
} elseif (!$docblock instanceof Zend_CodeGenerator_Php_Docblock) {
|
||
|
require_once 'Zend/CodeGenerator/Php/Exception.php';
|
||
|
throw new Zend_CodeGenerator_Php_Exception('setDocblock() is expecting either a string, array or an instance of Zend_CodeGenerator_Php_Docblock');
|
||
|
}
|
||
|
|
||
|
$this->_docblock = $docblock;
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get docblock
|
||
|
*
|
||
|
* @return Zend_CodeGenerator_Php_Docblock
|
||
|
*/
|
||
|
public function getDocblock()
|
||
|
{
|
||
|
return $this->_docblock;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* setRequiredFiles
|
||
|
*
|
||
|
* @param array $requiredFiles
|
||
|
* @return Zend_CodeGenerator_Php_File
|
||
|
*/
|
||
|
public function setRequiredFiles($requiredFiles)
|
||
|
{
|
||
|
$this->_requiredFiles = $requiredFiles;
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* getRequiredFiles()
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
public function getRequiredFiles()
|
||
|
{
|
||
|
return $this->_requiredFiles;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* setClasses()
|
||
|
*
|
||
|
* @param array $classes
|
||
|
* @return Zend_CodeGenerator_Php_File
|
||
|
*/
|
||
|
public function setClasses(Array $classes)
|
||
|
{
|
||
|
foreach ($classes as $class) {
|
||
|
$this->setClass($class);
|
||
|
}
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* getClass()
|
||
|
*
|
||
|
* @param string $name
|
||
|
* @return Zend_CodeGenerator_Php_Class
|
||
|
*/
|
||
|
public function getClass($name = null)
|
||
|
{
|
||
|
if ($name == null) {
|
||
|
reset($this->_classes);
|
||
|
return current($this->_classes);
|
||
|
}
|
||
|
|
||
|
return $this->_classes[$name];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* setClass()
|
||
|
*
|
||
|
* @param Zend_CodeGenerator_Php_Class|array $class
|
||
|
* @return Zend_CodeGenerator_Php_File
|
||
|
*/
|
||
|
public function setClass($class)
|
||
|
{
|
||
|
if (is_array($class)) {
|
||
|
$class = new Zend_CodeGenerator_Php_Class($class);
|
||
|
$className = $class->getName();
|
||
|
} elseif ($class instanceof Zend_CodeGenerator_Php_Class) {
|
||
|
$className = $class->getName();
|
||
|
} else {
|
||
|
require_once 'Zend/CodeGenerator/Php/Exception.php';
|
||
|
throw new Zend_CodeGenerator_Php_Exception('Expecting either an array or an instance of Zend_CodeGenerator_Php_Class');
|
||
|
}
|
||
|
|
||
|
// @todo check for dup here
|
||
|
|
||
|
$this->_classes[$className] = $class;
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* setFilename()
|
||
|
*
|
||
|
* @param string $filename
|
||
|
* @return Zend_CodeGenerator_Php_File
|
||
|
*/
|
||
|
public function setFilename($filename)
|
||
|
{
|
||
|
$this->_filename = $filename;
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* getFilename()
|
||
|
*
|
||
|
* @return string
|
||
|
*/
|
||
|
public function getFilename()
|
||
|
{
|
||
|
return $this->_filename;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* getClasses()
|
||
|
*
|
||
|
* @return array Array of Zend_CodeGenerator_Php_Class
|
||
|
*/
|
||
|
public function getClasses()
|
||
|
{
|
||
|
return $this->_classes;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* setBody()
|
||
|
*
|
||
|
* @param string $body
|
||
|
* @return Zend_CodeGenerator_Php_File
|
||
|
*/
|
||
|
public function setBody($body)
|
||
|
{
|
||
|
$this->_body = $body;
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* getBody()
|
||
|
*
|
||
|
* @return string
|
||
|
*/
|
||
|
public function getBody()
|
||
|
{
|
||
|
return $this->_body;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* isSourceDirty()
|
||
|
*
|
||
|
* @return bool
|
||
|
*/
|
||
|
public function isSourceDirty()
|
||
|
{
|
||
|
if (($docblock = $this->getDocblock()) && $docblock->isSourceDirty()) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
foreach ($this->_classes as $class) {
|
||
|
if ($class->isSourceDirty()) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return parent::isSourceDirty();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* generate()
|
||
|
*
|
||
|
* @return string
|
||
|
*/
|
||
|
public function generate()
|
||
|
{
|
||
|
if ($this->isSourceDirty() === false) {
|
||
|
return $this->_sourceContent;
|
||
|
}
|
||
|
|
||
|
$output = '';
|
||
|
|
||
|
// start with the body (if there), or open tag
|
||
|
if (preg_match('#(?:\s*)<\?php#', $this->getBody()) == false) {
|
||
|
$output = '<?php' . self::LINE_FEED;
|
||
|
}
|
||
|
|
||
|
// if there are markers, put the body into the output
|
||
|
$body = $this->getBody();
|
||
|
if (preg_match('#/\* Zend_CodeGenerator_Php_File-(.*?)Marker:#', $body)) {
|
||
|
$output .= $body;
|
||
|
$body = '';
|
||
|
}
|
||
|
|
||
|
// Add file docblock, if any
|
||
|
if (null !== ($docblock = $this->getDocblock())) {
|
||
|
$docblock->setIndentation('');
|
||
|
$regex = preg_quote(self::$_markerDocblock, '#');
|
||
|
if (preg_match('#'.$regex.'#', $output)) {
|
||
|
$output = preg_replace('#'.$regex.'#', $docblock->generate(), $output, 1);
|
||
|
} else {
|
||
|
$output .= $docblock->generate() . self::LINE_FEED;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// newline
|
||
|
$output .= self::LINE_FEED;
|
||
|
|
||
|
// process required files
|
||
|
// @todo marker replacement for required files
|
||
|
$requiredFiles = $this->getRequiredFiles();
|
||
|
if (!empty($requiredFiles)) {
|
||
|
foreach ($requiredFiles as $requiredFile) {
|
||
|
$output .= 'require_once \'' . $requiredFile . '\';' . self::LINE_FEED;
|
||
|
}
|
||
|
|
||
|
$output .= self::LINE_FEED;
|
||
|
}
|
||
|
|
||
|
// process classes
|
||
|
$classes = $this->getClasses();
|
||
|
if (!empty($classes)) {
|
||
|
foreach ($classes as $class) {
|
||
|
$regex = str_replace('?', $class->getName(), self::$_markerClass);
|
||
|
$regex = preg_quote($regex, '#');
|
||
|
if (preg_match('#'.$regex.'#', $output)) {
|
||
|
$output = preg_replace('#'.$regex.'#', $class->generate(), $output, 1);
|
||
|
} else {
|
||
|
$output .= $class->generate() . self::LINE_FEED;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
if (!empty($body)) {
|
||
|
|
||
|
// add an extra space betwee clsses and
|
||
|
if (!empty($classes)) {
|
||
|
$output .= self::LINE_FEED;
|
||
|
}
|
||
|
|
||
|
$output .= $body;
|
||
|
}
|
||
|
|
||
|
return $output;
|
||
|
}
|
||
|
|
||
|
public function write()
|
||
|
{
|
||
|
if ($this->_filename == '' || !is_writable(dirname($this->_filename))) {
|
||
|
require_once 'Zend/CodeGenerator/Php/Exception.php';
|
||
|
throw new Zend_CodeGenerator_Php_Exception('This code generator object is not writable.');
|
||
|
}
|
||
|
file_put_contents($this->_filename, $this->generate());
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
}
|