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.
		
		
		
		
		
			
		
			
				
					
					
						
							428 lines
						
					
					
						
							11 KiB
						
					
					
				
			
		
		
	
	
							428 lines
						
					
					
						
							11 KiB
						
					
					
				<?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.'); | 
						|
		} | 
						|
	} | 
						|
}
 | 
						|
 |