diff options
Diffstat (limited to 'framework/gii/CCodeModel.php')
| -rw-r--r-- | framework/gii/CCodeModel.php | 482 |
1 files changed, 482 insertions, 0 deletions
diff --git a/framework/gii/CCodeModel.php b/framework/gii/CCodeModel.php new file mode 100644 index 0000000..368c67b --- /dev/null +++ b/framework/gii/CCodeModel.php @@ -0,0 +1,482 @@ +<?php +/** + * CCodeModel class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2011 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CCodeModel is the base class for model classes that are used to generate code. + * + * Each code generator should have at least one code model class that extends from this class. + * The purpose of a code model is to represent user-supplied parameters and use them to + * generate customized code. + * + * Derived classes should implement the {@link prepare} method whose main task is to + * fill up the {@link files} property based on the user parameters. + * + * The {@link files} property should be filled with a set of {@link CCodeFile} instances, + * each representing a single code file to be generated. + * + * CCodeModel implements the feature of "sticky attributes". A sticky attribute is an attribute + * that can remember its last valid value, even if the user closes his browser window + * and reopen it. To declare an attribute is sticky, simply list it in a validation rule with + * the validator name being "sticky". + * + * @property array $templates A list of available code templates (name=>directory). + * @property string $templatePath The directory that contains the template files. + * @property string $stickyFile The file path that stores the sticky attribute values. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CCodeModel.php 3547 2012-01-24 10:07:28Z mdomba $ + * @package system.gii + * @since 1.1.2 + */ +abstract class CCodeModel extends CFormModel +{ + 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. + */ + abstract 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. + * @throw CException 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]; + else if(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 + */ + 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 + * @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"; + else if($file->operation===CCodeFile::OP_NEW && $this->confirmed($file)) + $output.=' generated '.$file->relativePath."\n"; + else if($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'; + } + + /** + * Converts a word to its plural form. + * Note that this is for English only! + * For example, 'apple' will become 'apples', and 'child' will become 'children'. + * @param string $name the word to be pluralized + * @return string the pluralized word + */ + public function pluralize($name) + { + $rules=array( + '/move$/i' => 'moves', + '/foot$/i' => 'feet', + '/child$/i' => 'children', + '/human$/i' => 'humans', + '/man$/i' => 'men', + '/tooth$/i' => 'teeth', + '/person$/i' => 'people', + '/([m|l])ouse$/i' => '\1ice', + '/(x|ch|ss|sh|us|as|is|os)$/i' => '\1es', + '/([^aeiouy]|qu)y$/i' => '\1ies', + '/(?:([^f])fe|([lr])f)$/i' => '\1\2ves', + '/(shea|lea|loa|thie)f$/i' => '\1ves', + '/([ti])um$/i' => '\1a', + '/(tomat|potat|ech|her|vet)o$/i' => '\1oes', + '/(bu)s$/i' => '\1ses', + '/(ax|test)is$/i' => '\1es', + '/s$/' => 's', + ); + foreach($rules as $rule=>$replacement) + { + if(preg_match($rule,$name)) + return preg_replace($rule,$replacement,$name); + } + return $name.'s'; + } + + /** + * Converts a class name into a HTML ID. + * For example, 'PostTag' will be converted as 'post-tag'. + * @param string $name the string to be converted + * @return string the resulting ID + */ + public function class2id($name) + { + return trim(strtolower(str_replace('_','-',preg_replace('/(?<![A-Z])[A-Z]/', '-\0', $name))),'-'); + } + + /** + * Converts a class name into space-separated words. + * For example, 'PostTag' will be converted as 'Post Tag'. + * @param string $name the string to be converted + * @param boolean $ucwords whether to capitalize the first letter in each word + * @return string the resulting words + */ + public function class2name($name,$ucwords=true) + { + $result=trim(strtolower(str_replace('_',' ',preg_replace('/(?<![A-Z])[A-Z]/', ' \0', $name)))); + return $ucwords ? ucwords($result) : $result; + } + + /** + * Converts a class name into a variable name with the first letter in lower case. + * This method is provided because lcfirst() PHP function is only available for PHP 5.3+. + * @param string $name the class name + * @return string the variable name converted from the class name + * @since 1.1.4 + */ + public function class2var($name) + { + $name[0]=strtolower($name[0]); + return $name; + } + + /** + * 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.'); + } +}
\ No newline at end of file |
