You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
429 lines
11 KiB
429 lines
11 KiB
11 years ago
|
<?php
|
||
|
/**
|
||
|
* @link http://www.yiiframework.com/
|
||
|
* @copyright Copyright (c) 2008 Yii Software LLC
|
||
|
* @license http://www.yiiframework.com/license/
|
||
|
*/
|
||
|
|
||
|
namespace yii\gii;
|
||
|
|
||
|
use Yii;
|
||
|
use yii\base\Model;
|
||
|
|
||
|
/**
|
||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||
|
* @since 2.0
|
||
|
*/
|
||
|
class Generator extends Model
|
||
|
{
|
||
|
/**
|
||
|
* @var string
|
||
|
*/
|
||
|
public $id;
|
||
|
|
||
|
/**
|
||
|
* @return string name of the code generator
|
||
|
*/
|
||
|
public function getName()
|
||
|
{
|
||
|
return 'unknown';
|
||
|
}
|
||
|
|
||
|
public function getDescription()
|
||
|
{
|
||
|
return '';
|
||
|
}
|
||
|
|
||
|
public function getUrl()
|
||
|
{
|
||
|
return Yii::$app->controller->createUrl('default/view', array('id' => $this->id));
|
||
|
}
|
||
|
|
||
|
public function renderForm()
|
||
|
{
|
||
|
return '';
|
||
|
}
|
||
|
|
||
|
public function renderFileList()
|
||
|
{
|
||
|
return '';
|
||
|
}
|
||
|
|
||
|
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()
|
||
|
{
|
||
|
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Declares the model validation rules.
|
||
|
* Child classes must override this method in the following format:
|
||
|
* <pre>
|
||
|
* return array_merge(parent::rules(), array(
|
||
|
* ...rules for the child class...
|
||
|
* ));
|
||
|
* </pre>
|
||
|
* @return array validation rules
|
||
|
*/
|
||
|
public function rules()
|
||
|
{
|
||
|
return array(
|
||
|
array('template', 'required'),
|
||
|
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
|
||
|
*/
|
||
|
public function classExists($name)
|
||
|
{
|
||
|
return class_exists($name, false) && in_array($name, get_declared_classes());
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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()
|
||
|
{
|
||
|
$result = true;
|
||
|
foreach ($this->files as $file) {
|
||
|
if ($this->confirmed($file)) {
|
||
|
$result = $file->save() && $result;
|
||
|
}
|
||
|
}
|
||
|
return $result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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
|
||
|
*/
|
||
|
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.');
|
||
|
} else {
|
||
|
throw new CHttpException(500, 'Invalid template selection.');
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param CCodeFile $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
|
||
|
|| is_array($this->answers) && isset($this->answers[md5($file->path)]);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Generates the code using the specified code template file.
|
||
|
* 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)
|
||
|
{
|
||
|
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();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return string the code generation result log.
|
||
|
*/
|
||
|
public function renderResults()
|
||
|
{
|
||
|
$output = 'Generating code using template "' . $this->templatePath . "\"...\n";
|
||
|
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)) {
|
||
|
$output .= ' generated ' . $file->relativePath . "\n";
|
||
|
} elseif ($file->operation === CCodeFile::OP_OVERWRITE && $this->confirmed($file)) {
|
||
|
$output .= ' overwrote ' . $file->relativePath . "\n";
|
||
|
} else {
|
||
|
$output .= ' skipped ' . $file->relativePath . "\n";
|
||
|
}
|
||
|
}
|
||
|
$output .= "done!\n";
|
||
|
return $output;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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.
|
||
|
*/
|
||
|
public function loadStickyAttributes()
|
||
|
{
|
||
|
$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;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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)
|
||
|
{
|
||
|
$value = $this->$attribute;
|
||
|
if (in_array(strtolower($value), self::$keywords)) {
|
||
|
$this->addError($attribute, $this->getAttributeLabel($attribute) . ' cannot take a reserved PHP keyword.');
|
||
|
}
|
||
|
}
|
||
|
}
|