summaryrefslogtreecommitdiff
path: root/framework/gii/CCodeModel.php
diff options
context:
space:
mode:
Diffstat (limited to 'framework/gii/CCodeModel.php')
-rw-r--r--framework/gii/CCodeModel.php482
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 &copy; 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