|  |  |  | <?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 ReflectionClass;
 | 
					
						
							|  |  |  | use yii\base\InvalidConfigException;
 | 
					
						
							|  |  |  | use yii\base\Model;
 | 
					
						
							|  |  |  | use yii\base\View;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * @author Qiang Xue <qiang.xue@gmail.com>
 | 
					
						
							|  |  |  |  * @since 2.0
 | 
					
						
							|  |  |  |  */
 | 
					
						
							|  |  |  | abstract class Generator extends Model
 | 
					
						
							|  |  |  | {
 | 
					
						
							|  |  |  | 	public $templates = array();
 | 
					
						
							|  |  |  | 	/**
 | 
					
						
							|  |  |  | 	 * @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;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/**
 | 
					
						
							|  |  |  | 	 * @return string name of the code generator
 | 
					
						
							|  |  |  | 	 */
 | 
					
						
							|  |  |  | 	abstract public function getName();
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	public function init()
 | 
					
						
							|  |  |  | 	{
 | 
					
						
							|  |  |  | 		parent::init();
 | 
					
						
							|  |  |  | 		if (!isset($this->templates['default'])) {
 | 
					
						
							|  |  |  | 			$this->templates['default'] = $this->defaultTemplate();
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/**
 | 
					
						
							|  |  |  | 	 * 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 array();
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/**
 | 
					
						
							|  |  |  | 	 * 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();
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	public function formView()
 | 
					
						
							|  |  |  | 	{
 | 
					
						
							|  |  |  | 		$class = new ReflectionClass($this);
 | 
					
						
							|  |  |  | 		return dirname($class->getFileName()) . '/views/form.php';
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	public function defaultTemplate()
 | 
					
						
							|  |  |  | 	{
 | 
					
						
							|  |  |  | 		$class = new ReflectionClass($this);
 | 
					
						
							|  |  |  | 		return dirname($class->getFileName()) . '/templates';
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	public function getDescription()
 | 
					
						
							|  |  |  | 	{
 | 
					
						
							|  |  |  | 		return '';
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/**
 | 
					
						
							|  |  |  | 	 * 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', 'message' => 'A code template must be selected.'),
 | 
					
						
							|  |  |  | 			array('template', 'validateTemplate', 'skipOnError' => true),
 | 
					
						
							|  |  |  | 		);
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/**
 | 
					
						
							|  |  |  | 	 * 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());
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/**
 | 
					
						
							|  |  |  | 	 * Saves the generated code into files.
 | 
					
						
							|  |  |  | 	 */
 | 
					
						
							|  |  |  | 	public function save($files, $answers = array())
 | 
					
						
							|  |  |  | 	{
 | 
					
						
							|  |  |  | 		$result = true;
 | 
					
						
							|  |  |  | 		foreach ($files as $file) {
 | 
					
						
							|  |  |  | 			if ($this->confirmed($file)) {
 | 
					
						
							|  |  |  | 				$result = $file->save() && $result;
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		return $result;
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/**
 | 
					
						
							|  |  |  | 	 * @return string the directory that contains the template files.
 | 
					
						
							|  |  |  | 	 * @throws InvalidConfigException if {@link templates} is empty or template selection is invalid
 | 
					
						
							|  |  |  | 	 */
 | 
					
						
							|  |  |  | 	public function getTemplatePath()
 | 
					
						
							|  |  |  | 	{
 | 
					
						
							|  |  |  | 		if (isset($this->templates[$this->template])) {
 | 
					
						
							|  |  |  | 			return $this->templates[$this->template];
 | 
					
						
							|  |  |  | 		} else {
 | 
					
						
							|  |  |  | 			throw new InvalidConfigException("Unknown template: {$this->template}");
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/**
 | 
					
						
							|  |  |  | 	 * @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 === CodeFile::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
 | 
					
						
							|  |  |  | 	 * @return string the generated code
 | 
					
						
							|  |  |  | 	 */
 | 
					
						
							|  |  |  | 	public function render($templateFile, $params = array())
 | 
					
						
							|  |  |  | 	{
 | 
					
						
							|  |  |  | 		$view = new View;
 | 
					
						
							|  |  |  | 		return $view->renderFile($templateFile, $params, $this);
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/**
 | 
					
						
							|  |  |  | 	 * @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 === CodeFile::OP_NEW && $this->confirmed($file)) {
 | 
					
						
							|  |  |  | 				$output .= ' generated ' . $file->relativePath . "\n";
 | 
					
						
							|  |  |  | 			} elseif ($file->operation === CodeFile::OP_OVERWRITE && $this->confirmed($file)) {
 | 
					
						
							|  |  |  | 				$output .= ' overwrote ' . $file->relativePath . "\n";
 | 
					
						
							|  |  |  | 			} else {
 | 
					
						
							|  |  |  | 				$output .= '   skipped ' . $file->relativePath . "\n";
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		$output .= "done!\n";
 | 
					
						
							|  |  |  | 		return $output;
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/**
 | 
					
						
							|  |  |  | 	 * 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 validateTemplate()
 | 
					
						
							|  |  |  | 	{
 | 
					
						
							|  |  |  | 		$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'.");
 | 
					
						
							|  |  |  | 				}
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/**
 | 
					
						
							|  |  |  | 	 * 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), $keywords)) {
 | 
					
						
							|  |  |  | 			$this->addError($attribute, $this->getAttributeLabel($attribute) . ' cannot take a reserved PHP keyword.');
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | }
 |