@ -15,15 +15,31 @@ use yii\base\View;
/**
* This is the base class for all generator classes.
*
* A generator instance is responsible for taking user inputs, validating them,
* and using them to generate the corresponding code based on a set of code template files.
*
* A generator class typically needs to implement the following methods:
*
* - [[getName()]]: returns the name of the generator
* - [[getDescription()]]: returns the detailed description of the generator
* - [[generate()]]: generates the code based on the current user input and the specified code template files.
* This is the place where main code generation code resides.
*
* @author Qiang Xue < qiang.xue @ gmail . com >
* @since 2.0
*/
abstract class Generator extends Model
{
/**
* @var array a list of available code templates. The array keys are the template names,
* and the array values are the corresponding template paths or path aliases.
*/
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}.
* The value of this property is internally managed by this class.
*/
public $template;
@ -31,41 +47,58 @@ abstract class Generator extends Model
* @return string name of the code generator
*/
abstract public function getName();
/**
* Generates the code based on the current user input and the specified code template files.
* This is the main method that child classes should implement.
* Please refer to [[\yii\gii\generators\controller\Generator::generate()]] as an example
* on how to implement this method.
* @return CodeFile[] a list of code files to be created.
*/
abstract public function generate();
/**
* @inheritdoc
*/
public function init()
{
parent::init();
if (!isset($this->templates['default'])) {
$this->templates['default'] = $this->defaultTemplate();
}
foreach ($this->templates as $i => $template) {
$this->templates[$i] = Yii::getAlias($template);
}
}
/**
* 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.
* Returns a list of code template files that are required.
* Derived classes usually should override this method if they require the existence of
* certain template files.
* @return array list of code template files that are required. They should be file paths
* relative to [[templatePath]].
*/
public function prepare()
public function requiredTemplates ()
{
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}.
* Returns the list of sticky attributes .
* A sticky attribute will remember its value and will initialize the attribute with this value
* when the generator is restarted.
* @return array list of sticky attributes
*/
public function requiredTemplates()
{
return array();
}
public function stickyAttributes()
{
return array('template');
}
/**
* Returns the list of hint messages.
* The array keys are the attribute names, and the array values are the corresponding hint messages.
* Hint messages will be displayed to end users when they are filling the form for the generator.
* @return array the list of hint messages
*/
public function hints()
{
return array();
@ -73,7 +106,7 @@ abstract class Generator extends Model
/**
* 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 .
* Child classes may override this method to customize the message .
* @return string the message to be displayed when the newly generated code is saved successfully.
*/
public function successMessage()
@ -81,32 +114,49 @@ abstract class Generator extends Model
return 'The code has been generated successfully.';
}
/**
* Returns the view file for the input form of the generator.
* The default implementation will return the "form.php" file under the "views" subdirectory of the
* directory containing the generator class file.
* @return string the view file for the input form of the generator.
*/
public function formView()
{
$class = new ReflectionClass($this);
return dirname($class->getFileName()) . '/views/form.php';
}
/**
* Returns the root path to the default code template files.
* The default implementation will return the "templates" subdirectory of the
* directory containing the generator class file.
* @return string the root path to the default code template files.
*/
public function defaultTemplate()
{
$class = new ReflectionClass($this);
return dirname($class->getFileName()) . '/templates';
}
/**
* @return string the detailed description of the generator.
*/
public function getDescription()
{
return '';
}
/**
* Declares the model validation rules.
* Child classes must override this method in the following format:
* < pre >
* @inheritdoc
*
* Child classes should override this method like the following so that the parent
* rules are included:
*
* ~~~
* return array_merge(parent::rules(), array(
* ...rules for the child class...
* ));
* < / pre >
* @return array validation rules
* ~~~
*/
public function rules()
{
@ -117,17 +167,8 @@ abstract class Generator extends Model
}
/**
* 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());
}
/**
* Loads sticky attributes from a file and populates them into the model.
* Loads sticky attributes from an internal file and populates them into the generator.
* @internal
*/
public function loadStickyAttributes()
{
@ -147,7 +188,8 @@ abstract class Generator extends Model
}
/**
* Saves sticky attributes into a file.
* Saves sticky attributes into an internal file.
* @internal
*/
public function saveStickyAttributes()
{
@ -164,6 +206,7 @@ abstract class Generator extends Model
/**
* @return string the file path that stores the sticky attribute values.
* @internal
*/
public function getStickyDataFile()
{
@ -172,36 +215,39 @@ abstract class Generator extends Model
/**
* Saves the generated code into files.
* @param CodeFile[] $files
* @param CodeFile[] $files the code files to be saved
* @param array $answers
* @param boolean $hasError
* @return string
* @param string $results this parameter receives a value from this method indicating the log messages
* generated while saving the code files.
* @return boolean whether there is any error while saving the code files.
*/
public function save($files, $answers, & $hasError )
public function save($files, $answers, & $results )
{
$lines = array('Generating code using template "' . $this->templatePath . '"...');
$lines = array('Generating code using template "' . $this->getTemplatePath() . '"...');
$hasError = false;
foreach ($files as $file) {;
$relativePath = $file->getRelativePath();
if (isset($answers[$file->id]) & & $file->operation !== CodeFile::OP_SKIP) {
$error = $file->save();
if (is_string($error)) {
$hasError = true;
$lines[] = "< span class = \"error\" > generating $relativePath< br > $error< / span > ";
} elseif ($file->operation === CodeFile::OP_NEW) {
$lines[] = " generated $relativePath";
} else {
$lines[] = " overwrote $relativePath";
$lines[] = $file->operation === CodeFile::OP_NEW ? " generated $relativePath" : " overwrote $relativePath";
}
} else {
$lines[] = " skipped $relativePath";
}
}
$lines[] = "done!\n";
return implode("\n", $lines);
$results = implode("\n", $lines);
return $hasError;
}
/**
* @return string the directory that contains the template files .
* @throws InvalidConfigException if {@link templates} is empty or template selection is invalid
* @return string the root path of the template files that are currently being used .
* @throws InvalidConfigException if [[template]] is invalid
*/
public function getTemplatePath()
{
@ -212,7 +258,14 @@ abstract class Generator extends Model
}
}
public function generateCode($template, $params = array())
/**
* Generates code using the specified code template and parameters.
* Note that the code template will be used as a PHP file.
* @param string $template the code template file
* @param array $params list of parameters to be passed to the template file.
* @return string the generated code
*/
public function render($template, $params = array())
{
$view = new View;
$params['generator'] = $this;
@ -222,7 +275,7 @@ abstract class Generator extends Model
/**
* 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} .
* and the template contains all required template files as specified in [[requiredTemplates()]] .
*/
public function validateTemplate()
{
@ -328,8 +381,11 @@ abstract class Generator extends Model
'xor',
);
$value = $this->$attribute;
if (in_array(strtolower($value), $keywords)) {
$this->addError($attribute, $this->getAttributeLabel($attribute) . ' cannot take a reserved PHP keyword.');
foreach (explode('\\', strtolower($value)) as $name) {
if (in_array($name, $keywords)) {
$this->addError($attribute, $this->getAttributeLabel($attribute) . ' cannot take a reserved PHP keyword.');
return;
}
}
}
}