|
|
|
@ -8,161 +8,73 @@
|
|
|
|
|
namespace yii\gii; |
|
|
|
|
|
|
|
|
|
use Yii; |
|
|
|
|
use ReflectionClass; |
|
|
|
|
use yii\base\InvalidConfigException; |
|
|
|
|
use yii\base\Model; |
|
|
|
|
use yii\base\View; |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @author Qiang Xue <qiang.xue@gmail.com> |
|
|
|
|
* @since 2.0 |
|
|
|
|
*/ |
|
|
|
|
class Generator extends Model |
|
|
|
|
abstract class Generator extends Model |
|
|
|
|
{ |
|
|
|
|
public $templates = array(); |
|
|
|
|
/** |
|
|
|
|
* @var string |
|
|
|
|
* @var string the name of the code template that the user has selected. |
|
|
|
|
* The value of this property is internally managed by this class and {@link CCodeGenerator}. |
|
|
|
|
*/ |
|
|
|
|
public $id; |
|
|
|
|
public $template; |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @return string name of the code generator |
|
|
|
|
*/ |
|
|
|
|
public function getName() |
|
|
|
|
abstract public function getName(); |
|
|
|
|
|
|
|
|
|
public function init() |
|
|
|
|
{ |
|
|
|
|
return 'unknown'; |
|
|
|
|
parent::init(); |
|
|
|
|
if (!isset($this->templates['default'])) { |
|
|
|
|
$this->templates['default'] = $this->getDefaultTemplate(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public function getDescription() |
|
|
|
|
/** |
|
|
|
|
* Prepares the code files to be generated. |
|
|
|
|
* This is the main method that child classes should implement. It should contain the logic |
|
|
|
|
* that populates the {@link files} property with a list of code files to be generated. |
|
|
|
|
*/ |
|
|
|
|
public function prepare() |
|
|
|
|
{ |
|
|
|
|
return ''; |
|
|
|
|
return array(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public function getUrl() |
|
|
|
|
/** |
|
|
|
|
* Returns a list of code templates that are required. |
|
|
|
|
* Derived classes usually should override this method. |
|
|
|
|
* @return array list of code templates that are required. They should be file paths |
|
|
|
|
* relative to {@link templatePath}. |
|
|
|
|
*/ |
|
|
|
|
public function requiredTemplates() |
|
|
|
|
{ |
|
|
|
|
return Yii::$app->controller->createUrl('default/view', array('id' => $this->id)); |
|
|
|
|
return array(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public function renderForm() |
|
|
|
|
public function getViewFile() |
|
|
|
|
{ |
|
|
|
|
return ''; |
|
|
|
|
$class = new ReflectionClass($this); |
|
|
|
|
return dirname($class->getFileName()) . '/views/form.php'; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public function renderFileList() |
|
|
|
|
public function getDefaultTemplate() |
|
|
|
|
{ |
|
|
|
|
return ''; |
|
|
|
|
$class = new ReflectionClass($this); |
|
|
|
|
return dirname($class->getFileName()) . '/templates'; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const STATUS_NEW = 1; |
|
|
|
|
const STATUS_PREVIEW = 2; |
|
|
|
|
const STATUS_SUCCESS = 3; |
|
|
|
|
const STATUS_ERROR = 4; |
|
|
|
|
|
|
|
|
|
static $keywords = array( |
|
|
|
|
'__class__', |
|
|
|
|
'__dir__', |
|
|
|
|
'__file__', |
|
|
|
|
'__function__', |
|
|
|
|
'__line__', |
|
|
|
|
'__method__', |
|
|
|
|
'__namespace__', |
|
|
|
|
'abstract', |
|
|
|
|
'and', |
|
|
|
|
'array', |
|
|
|
|
'as', |
|
|
|
|
'break', |
|
|
|
|
'case', |
|
|
|
|
'catch', |
|
|
|
|
'cfunction', |
|
|
|
|
'class', |
|
|
|
|
'clone', |
|
|
|
|
'const', |
|
|
|
|
'continue', |
|
|
|
|
'declare', |
|
|
|
|
'default', |
|
|
|
|
'die', |
|
|
|
|
'do', |
|
|
|
|
'echo', |
|
|
|
|
'else', |
|
|
|
|
'elseif', |
|
|
|
|
'empty', |
|
|
|
|
'enddeclare', |
|
|
|
|
'endfor', |
|
|
|
|
'endforeach', |
|
|
|
|
'endif', |
|
|
|
|
'endswitch', |
|
|
|
|
'endwhile', |
|
|
|
|
'eval', |
|
|
|
|
'exception', |
|
|
|
|
'exit', |
|
|
|
|
'extends', |
|
|
|
|
'final', |
|
|
|
|
'final', |
|
|
|
|
'for', |
|
|
|
|
'foreach', |
|
|
|
|
'function', |
|
|
|
|
'global', |
|
|
|
|
'goto', |
|
|
|
|
'if', |
|
|
|
|
'implements', |
|
|
|
|
'include', |
|
|
|
|
'include_once', |
|
|
|
|
'instanceof', |
|
|
|
|
'interface', |
|
|
|
|
'isset', |
|
|
|
|
'list', |
|
|
|
|
'namespace', |
|
|
|
|
'new', |
|
|
|
|
'old_function', |
|
|
|
|
'or', |
|
|
|
|
'parent', |
|
|
|
|
'php_user_filter', |
|
|
|
|
'print', |
|
|
|
|
'private', |
|
|
|
|
'protected', |
|
|
|
|
'public', |
|
|
|
|
'require', |
|
|
|
|
'require_once', |
|
|
|
|
'return', |
|
|
|
|
'static', |
|
|
|
|
'switch', |
|
|
|
|
'this', |
|
|
|
|
'throw', |
|
|
|
|
'try', |
|
|
|
|
'unset', |
|
|
|
|
'use', |
|
|
|
|
'var', |
|
|
|
|
'while', |
|
|
|
|
'xor', |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @var array user confirmations on whether to overwrite existing code files with the newly generated ones. |
|
|
|
|
* The value of this property is internally managed by this class and {@link CCodeGenerator}. |
|
|
|
|
*/ |
|
|
|
|
public $answers; |
|
|
|
|
/** |
|
|
|
|
* @var string the name of the code template that the user has selected. |
|
|
|
|
* The value of this property is internally managed by this class and {@link CCodeGenerator}. |
|
|
|
|
*/ |
|
|
|
|
public $template; |
|
|
|
|
/** |
|
|
|
|
* @var array a list of {@link CCodeFile} objects that represent the code files to be generated. |
|
|
|
|
* The {@link prepare()} method is responsible to populate this property. |
|
|
|
|
*/ |
|
|
|
|
public $files = array(); |
|
|
|
|
/** |
|
|
|
|
* @var integer the status of this model. T |
|
|
|
|
* The value of this property is internally managed by {@link CCodeGenerator}. |
|
|
|
|
*/ |
|
|
|
|
public $status = self::STATUS_NEW; |
|
|
|
|
|
|
|
|
|
private $_stickyAttributes = array(); |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Prepares the code files to be generated. |
|
|
|
|
* This is the main method that child classes should implement. It should contain the logic |
|
|
|
|
* that populates the {@link files} property with a list of code files to be generated. |
|
|
|
|
*/ |
|
|
|
|
public function prepare() |
|
|
|
|
public function getDescription() |
|
|
|
|
{ |
|
|
|
|
|
|
|
|
|
return ''; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
@ -178,35 +90,12 @@ class Generator extends Model
|
|
|
|
|
public function rules() |
|
|
|
|
{ |
|
|
|
|
return array( |
|
|
|
|
array('template', 'required'), |
|
|
|
|
array('template', 'required', 'message' => 'A code template must be selected.'), |
|
|
|
|
array('template', 'validateTemplate', 'skipOnError' => true), |
|
|
|
|
array('template', 'sticky'), |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Validates the template selection. |
|
|
|
|
* This method validates whether the user selects an existing template |
|
|
|
|
* and the template contains all required template files as specified in {@link requiredTemplates}. |
|
|
|
|
* @param string $attribute the attribute to be validated |
|
|
|
|
* @param array $params validation parameters |
|
|
|
|
*/ |
|
|
|
|
public function validateTemplate($attribute, $params) |
|
|
|
|
{ |
|
|
|
|
$templates = $this->templates; |
|
|
|
|
if (!isset($templates[$this->template])) { |
|
|
|
|
$this->addError('template', 'Invalid template selection.'); |
|
|
|
|
} else { |
|
|
|
|
$templatePath = $this->templatePath; |
|
|
|
|
foreach ($this->requiredTemplates() as $template) { |
|
|
|
|
if (!is_file($templatePath . '/' . $template)) { |
|
|
|
|
$this->addError('template', "Unable to find the required code template file '$template'."); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Checks if the named class exists (in a case sensitive manner). |
|
|
|
|
* @param string $name class name to be checked |
|
|
|
|
* @return boolean whether the class exists |
|
|
|
@ -217,40 +106,12 @@ class Generator extends Model
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Declares the model attribute labels. |
|
|
|
|
* Child classes must override this method in the following format: |
|
|
|
|
* <pre> |
|
|
|
|
* return array_merge(parent::attributeLabels(), array( |
|
|
|
|
* ...labels for the child class attributes... |
|
|
|
|
* )); |
|
|
|
|
* </pre> |
|
|
|
|
* @return array the attribute labels |
|
|
|
|
*/ |
|
|
|
|
public function attributeLabels() |
|
|
|
|
{ |
|
|
|
|
return array( |
|
|
|
|
'template' => 'Code Template', |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Returns a list of code templates that are required. |
|
|
|
|
* Derived classes usually should override this method. |
|
|
|
|
* @return array list of code templates that are required. They should be file paths |
|
|
|
|
* relative to {@link templatePath}. |
|
|
|
|
*/ |
|
|
|
|
public function requiredTemplates() |
|
|
|
|
{ |
|
|
|
|
return array(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Saves the generated code into files. |
|
|
|
|
*/ |
|
|
|
|
public function save() |
|
|
|
|
public function save($files, $answers = array()) |
|
|
|
|
{ |
|
|
|
|
$result = true; |
|
|
|
|
foreach ($this->files as $file) { |
|
|
|
|
foreach ($files as $file) { |
|
|
|
|
if ($this->confirmed($file)) { |
|
|
|
|
$result = $file->save() && $result; |
|
|
|
|
} |
|
|
|
@ -259,59 +120,25 @@ class Generator extends Model
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Returns the message to be displayed when the newly generated code is saved successfully. |
|
|
|
|
* Child classes should override this method if the message needs to be customized. |
|
|
|
|
* @return string the message to be displayed when the newly generated code is saved successfully. |
|
|
|
|
*/ |
|
|
|
|
public function successMessage() |
|
|
|
|
{ |
|
|
|
|
return 'The code has been generated successfully.'; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Returns the message to be displayed when some error occurred during code file saving. |
|
|
|
|
* Child classes should override this method if the message needs to be customized. |
|
|
|
|
* @return string the message to be displayed when some error occurred during code file saving. |
|
|
|
|
*/ |
|
|
|
|
public function errorMessage() |
|
|
|
|
{ |
|
|
|
|
return 'There was some error when generating the code. Please check the following messages.'; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Returns a list of available code templates (name=>directory). |
|
|
|
|
* This method simply returns the {@link CCodeGenerator::templates} property value. |
|
|
|
|
* @return array a list of available code templates (name=>directory). |
|
|
|
|
*/ |
|
|
|
|
public function getTemplates() |
|
|
|
|
{ |
|
|
|
|
return Yii::app()->controller->templates; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @return string the directory that contains the template files. |
|
|
|
|
* @throws CHttpException if {@link templates} is empty or template selection is invalid |
|
|
|
|
* @throws InvalidConfigException if {@link templates} is empty or template selection is invalid |
|
|
|
|
*/ |
|
|
|
|
public function getTemplatePath() |
|
|
|
|
{ |
|
|
|
|
$templates = $this->getTemplates(); |
|
|
|
|
if (isset($templates[$this->template])) { |
|
|
|
|
return $templates[$this->template]; |
|
|
|
|
} elseif (empty($templates)) { |
|
|
|
|
throw new CHttpException(500, 'No templates are available.'); |
|
|
|
|
if (isset($this->templates[$this->template])) { |
|
|
|
|
return $this->templates[$this->template]; |
|
|
|
|
} else { |
|
|
|
|
throw new CHttpException(500, 'Invalid template selection.'); |
|
|
|
|
throw new InvalidConfigException("Unknown template: {$this->template}"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @param CCodeFile $file whether the code file should be saved |
|
|
|
|
* @param CodeFile $file whether the code file should be saved |
|
|
|
|
* @return bool whether the confirmation is found in {@link answers} with appropriate {@link operation} |
|
|
|
|
*/ |
|
|
|
|
public function confirmed($file) |
|
|
|
|
{ |
|
|
|
|
return $this->answers === null && $file->operation === CCodeFile::OP_NEW |
|
|
|
|
return $this->answers === null && $file->operation === CodeFile::OP_NEW |
|
|
|
|
|| is_array($this->answers) && isset($this->answers[md5($file->path)]); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -320,24 +147,12 @@ class Generator extends Model
|
|
|
|
|
* This method is manly used in {@link generate} to generate code. |
|
|
|
|
* @param string $templateFile the code template file path |
|
|
|
|
* @param array $_params_ a set of parameters to be extracted and made available in the code template |
|
|
|
|
* @throws CException is template file does not exist |
|
|
|
|
* @return string the generated code |
|
|
|
|
*/ |
|
|
|
|
public function render($templateFile, $_params_ = null) |
|
|
|
|
public function render($templateFile, $params = array()) |
|
|
|
|
{ |
|
|
|
|
if (!is_file($templateFile)) { |
|
|
|
|
throw new CException("The template file '$templateFile' does not exist."); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (is_array($_params_)) { |
|
|
|
|
extract($_params_, EXTR_PREFIX_SAME, 'params'); |
|
|
|
|
} else { |
|
|
|
|
$params = $_params_; |
|
|
|
|
} |
|
|
|
|
ob_start(); |
|
|
|
|
ob_implicit_flush(false); |
|
|
|
|
require($templateFile); |
|
|
|
|
return ob_get_clean(); |
|
|
|
|
$view = new View; |
|
|
|
|
return $view->renderFile($templateFile, $params, $this); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
@ -349,9 +164,9 @@ class Generator extends Model
|
|
|
|
|
foreach ($this->files as $file) { |
|
|
|
|
if ($file->error !== null) { |
|
|
|
|
$output .= "<span class=\"error\">generating {$file->relativePath}<br/> {$file->error}</span>\n"; |
|
|
|
|
} elseif ($file->operation === CCodeFile::OP_NEW && $this->confirmed($file)) { |
|
|
|
|
} elseif ($file->operation === CodeFile::OP_NEW && $this->confirmed($file)) { |
|
|
|
|
$output .= ' generated ' . $file->relativePath . "\n"; |
|
|
|
|
} elseif ($file->operation === CCodeFile::OP_OVERWRITE && $this->confirmed($file)) { |
|
|
|
|
} elseif ($file->operation === CodeFile::OP_OVERWRITE && $this->confirmed($file)) { |
|
|
|
|
$output .= ' overwrote ' . $file->relativePath . "\n"; |
|
|
|
|
} else { |
|
|
|
|
$output .= ' skipped ' . $file->relativePath . "\n"; |
|
|
|
@ -362,66 +177,111 @@ class Generator extends Model
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* The "sticky" validator. |
|
|
|
|
* This validator does not really validate the attributes. |
|
|
|
|
* It actually saves the attribute value in a file to make it sticky. |
|
|
|
|
* @param string $attribute the attribute to be validated |
|
|
|
|
* @param array $params the validation parameters |
|
|
|
|
*/ |
|
|
|
|
public function sticky($attribute, $params) |
|
|
|
|
{ |
|
|
|
|
if (!$this->hasErrors()) { |
|
|
|
|
$this->_stickyAttributes[$attribute] = $this->$attribute; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Loads sticky attributes from a file and populates them into the model. |
|
|
|
|
* Validates the template selection. |
|
|
|
|
* This method validates whether the user selects an existing template |
|
|
|
|
* and the template contains all required template files as specified in {@link requiredTemplates}. |
|
|
|
|
*/ |
|
|
|
|
public function loadStickyAttributes() |
|
|
|
|
public function validateTemplate() |
|
|
|
|
{ |
|
|
|
|
$this->_stickyAttributes = array(); |
|
|
|
|
$path = $this->getStickyFile(); |
|
|
|
|
if (is_file($path)) { |
|
|
|
|
$result = @include($path); |
|
|
|
|
if (is_array($result)) { |
|
|
|
|
$this->_stickyAttributes = $result; |
|
|
|
|
foreach ($this->_stickyAttributes as $name => $value) { |
|
|
|
|
if (property_exists($this, $name) || $this->canSetProperty($name)) { |
|
|
|
|
$this->$name = $value; |
|
|
|
|
} |
|
|
|
|
$templates = $this->templates; |
|
|
|
|
if (!isset($templates[$this->template])) { |
|
|
|
|
$this->addError('template', 'Invalid template selection.'); |
|
|
|
|
} else { |
|
|
|
|
$templatePath = $this->templates[$this->template]; |
|
|
|
|
foreach ($this->requiredTemplates() as $template) { |
|
|
|
|
if (!is_file($templatePath . '/' . $template)) { |
|
|
|
|
$this->addError('template', "Unable to find the required code template file '$template'."); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Saves sticky attributes into a file. |
|
|
|
|
*/ |
|
|
|
|
public function saveStickyAttributes() |
|
|
|
|
{ |
|
|
|
|
$path = $this->getStickyFile(); |
|
|
|
|
@mkdir(dirname($path), 0755, true); |
|
|
|
|
file_put_contents($path, "<?php\nreturn " . var_export($this->_stickyAttributes, true) . ";\n");
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @return string the file path that stores the sticky attribute values. |
|
|
|
|
*/ |
|
|
|
|
public function getStickyFile() |
|
|
|
|
{ |
|
|
|
|
return Yii::app()->runtimePath . '/gii-' . Yii::getVersion() . '/' . get_class($this) . '.php'; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Validates an attribute to make sure it is not taking a PHP reserved keyword. |
|
|
|
|
* @param string $attribute the attribute to be validated |
|
|
|
|
* @param array $params validation parameters |
|
|
|
|
*/ |
|
|
|
|
public function validateReservedWord($attribute, $params) |
|
|
|
|
{ |
|
|
|
|
static $keywords = array( |
|
|
|
|
'__class__', |
|
|
|
|
'__dir__', |
|
|
|
|
'__file__', |
|
|
|
|
'__function__', |
|
|
|
|
'__line__', |
|
|
|
|
'__method__', |
|
|
|
|
'__namespace__', |
|
|
|
|
'abstract', |
|
|
|
|
'and', |
|
|
|
|
'array', |
|
|
|
|
'as', |
|
|
|
|
'break', |
|
|
|
|
'case', |
|
|
|
|
'catch', |
|
|
|
|
'cfunction', |
|
|
|
|
'class', |
|
|
|
|
'clone', |
|
|
|
|
'const', |
|
|
|
|
'continue', |
|
|
|
|
'declare', |
|
|
|
|
'default', |
|
|
|
|
'die', |
|
|
|
|
'do', |
|
|
|
|
'echo', |
|
|
|
|
'else', |
|
|
|
|
'elseif', |
|
|
|
|
'empty', |
|
|
|
|
'enddeclare', |
|
|
|
|
'endfor', |
|
|
|
|
'endforeach', |
|
|
|
|
'endif', |
|
|
|
|
'endswitch', |
|
|
|
|
'endwhile', |
|
|
|
|
'eval', |
|
|
|
|
'exception', |
|
|
|
|
'exit', |
|
|
|
|
'extends', |
|
|
|
|
'final', |
|
|
|
|
'final', |
|
|
|
|
'for', |
|
|
|
|
'foreach', |
|
|
|
|
'function', |
|
|
|
|
'global', |
|
|
|
|
'goto', |
|
|
|
|
'if', |
|
|
|
|
'implements', |
|
|
|
|
'include', |
|
|
|
|
'include_once', |
|
|
|
|
'instanceof', |
|
|
|
|
'interface', |
|
|
|
|
'isset', |
|
|
|
|
'list', |
|
|
|
|
'namespace', |
|
|
|
|
'new', |
|
|
|
|
'old_function', |
|
|
|
|
'or', |
|
|
|
|
'parent', |
|
|
|
|
'php_user_filter', |
|
|
|
|
'print', |
|
|
|
|
'private', |
|
|
|
|
'protected', |
|
|
|
|
'public', |
|
|
|
|
'require', |
|
|
|
|
'require_once', |
|
|
|
|
'return', |
|
|
|
|
'static', |
|
|
|
|
'switch', |
|
|
|
|
'this', |
|
|
|
|
'throw', |
|
|
|
|
'try', |
|
|
|
|
'unset', |
|
|
|
|
'use', |
|
|
|
|
'var', |
|
|
|
|
'while', |
|
|
|
|
'xor', |
|
|
|
|
); |
|
|
|
|
$value = $this->$attribute; |
|
|
|
|
if (in_array(strtolower($value), self::$keywords)) { |
|
|
|
|
if (in_array(strtolower($value), $keywords)) { |
|
|
|
|
$this->addError($attribute, $this->getAttributeLabel($attribute) . ' cannot take a reserved PHP keyword.'); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|