Commit 054b6f58 by Qiang Xue

Finished controller code generator.

parent 2b711ffd
......@@ -13,35 +13,47 @@ use yii\gii\components\TextDiff;
use yii\helpers\Html;
/**
* CodeFile represents a code file to be generated.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class CodeFile extends Object
{
/**
* The code file is new.
*/
const OP_NEW = 'new';
/**
* The code file already exists, and the new one may need to overwrite it.
*/
const OP_OVERWRITE = 'overwrite';
/**
* The new code file and the existing one are identical.
*/
const OP_SKIP = 'skip';
/**
* @var string an ID that uniquely identifies this code file.
*/
public $id;
/**
* @var string the file path that the new code should be saved to.
*/
public $path;
/**
* @var mixed the newly generated code. If this is null, it means {@link path}
* should be treated as a directory.
* @var string the newly generated code content
*/
public $content;
/**
* @var string the operation to be performed
* @var string the operation to be performed. This can be [[OP_NEW]], [[OP_OVERWRITE]] or [[OP_SKIP]].
*/
public $operation;
/**
* Constructor.
* @param string $path the file path that the new code should be saved to.
* @param string $content the newly generated code
* @param string $content the newly generated code content.
*/
public function __construct($path, $content)
{
......@@ -56,8 +68,8 @@ class CodeFile extends Object
}
/**
* Saves the code into the file {@link path}.
* @return string|boolean
* Saves the code into the file specified by [[path]].
* @return string|boolean the error occurred while saving the code file, or true if no error.
*/
public function save()
{
......@@ -65,9 +77,9 @@ class CodeFile extends Object
if ($this->operation === self::OP_NEW) {
$dir = dirname($this->path);
if (!is_dir($dir)) {
$oldmask = @umask(0);
$mask = @umask(0);
$result = @mkdir($dir, $module->newDirMode, true);
@umask($oldmask);
@umask($mask);
if (!$result) {
return "Unable to create the directory '$dir'.";
}
......@@ -76,9 +88,9 @@ class CodeFile extends Object
if (@file_put_contents($this->path, $this->content) === false) {
return "Unable to write the file '{$this->path}'.";
} else {
$oldmask = @umask(0);
$mask = @umask(0);
@chmod($this->path, $module->newFileMode);
@umask($oldmask);
@umask($mask);
}
return true;
}
......@@ -116,18 +128,18 @@ class CodeFile extends Object
}
if ($type === 'php') {
return '<div class="content">' . highlight_string($this->content, true) . '</div>';
} elseif(in_array($type, array('txt','js','css'))) {
return '<div class="content">' . nl2br(Html::encode($this->content)) . '</div>';
return highlight_string($this->content, true);
} elseif (!in_array($type, array('jpg', 'gif', 'png', 'exe'))) {
return nl2br(Html::encode($this->content));
} else {
return '<div class="error">Preview is not available for this file type.</div>';
return false;
}
}
public function diff()
{
$type = $this->getType();
if (!in_array($type, array('php', 'txt','js','css'))) {
$type = strtolower($this->getType());
if (in_array($type, array('jpg', 'gif', 'png', 'exe'))) {
return false;
} elseif ($this->operation === self::OP_OVERWRITE) {
return TextDiff::compare(file_get_contents($this->path), $this->content);
......
......@@ -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;
}
}
}
}
......@@ -10,18 +10,32 @@ namespace yii\gii;
use yii\web\AssetBundle;
/**
* This declares the asset files required by Gii.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class GiiAsset extends AssetBundle
{
/**
* @inheritdoc
*/
public $sourcePath = '@yii/gii/assets';
/**
* @inheritdoc
*/
public $css = array(
'main.css',
);
/**
* @inheritdoc
*/
public $js = array(
'gii.js',
);
/**
* @inheritdoc
*/
public $depends = array(
'yii\web\YiiAsset',
'yii\bootstrap\BootstrapAsset',
......
......@@ -11,11 +11,52 @@ use Yii;
use yii\web\HttpException;
/**
* This is the main module class for the Gii module.
*
* To use Gii, include it as a module in the application configuration like the following:
*
* ~~~
* return array(
* ......
* 'modules' => array(
* 'gii' => array(
* 'class' => 'yii\gii\Module',
* ),
* ),
* )
* ~~~
*
* Because Gii generates new code files on the server, you should only use it on your own
* development machine. To prevent other people from using this module, by default, Gii
* can only be accessed by localhost. You may configure its [[allowedIPs]] property if
* you want to make it accessible on other machines.
*
* With the above configuration, you will be able to access GiiModule in your browser using
* the URL `http://localhost/path/to/index.php?r=gii`
*
* If your application enables [[UrlManager::enablePrettyUrl|pretty URLs]], depending on your
* configuration, you may need to add the following URL rules in your application configuration
* in order to access Gii:
*
* ~~~
* 'rules'=>array(
* 'gii' => 'gii',
* 'gii/<controller>' => 'gii/<controller>',
* 'gii/<controller>/<action>' => 'gii/<controller>/<action>',
* ...
* ),
* ~~~
*
* You can then access Gii via URL: `http://localhost/path/to/index.php/gii`
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class Module extends \yii\base\Module
{
/**
* @inheritdoc
*/
public $controllerNamespace = 'yii\gii\controllers';
/**
* @var array the list of IPs that are allowed to access this module.
......@@ -26,9 +67,15 @@ class Module extends \yii\base\Module
*/
public $allowedIPs = array('127.0.0.1', '::1');
/**
* @var array a list of path aliases that refer to the directories containing code generators.
* The directory referred by a single path alias may contain multiple code generators, each stored
* under a sub-directory whose name is the generator name.
* @var array|Generator[] a list of generator configurations or instances. The array keys
* are the generator IDs (e.g. "crud"), and the array elements are the corresponding generator
* configurations or the instances.
*
* After the module is initialized, this property will become an array of generator instances
* which are created based on the configurations previously taken by this property.
*
* Newly assigned generators will be merged with the [[coreGenerators()|core ones]], and the former
* takes precedence in case when they have the same generator ID.
*/
public $generators = array();
/**
......@@ -46,7 +93,7 @@ class Module extends \yii\base\Module
/**
* Initializes the gii module.
* @inheritdoc
*/
public function init()
{
......@@ -56,6 +103,9 @@ class Module extends \yii\base\Module
}
}
/**
* @inheritdoc
*/
public function beforeAction($action)
{
if ($this->checkAccess()) {
......@@ -65,6 +115,9 @@ class Module extends \yii\base\Module
}
}
/**
* @return boolean whether the module can be accessed by the current user
*/
protected function checkAccess()
{
$ip = Yii::$app->getRequest()->getUserIP();
......@@ -76,6 +129,10 @@ class Module extends \yii\base\Module
return false;
}
/**
* Returns the list of the core code generator configurations.
* @return array the list of the core code generator configurations.
*/
protected function coreGenerators()
{
return array(
......
......@@ -39,10 +39,10 @@ class DefaultController extends Controller
if (isset($_POST['preview']) || isset($_POST['generate'])) {
if ($generator->validate()) {
$generator->saveStickyAttributes();
$files = $generator->prepare();
$files = $generator->generate();
if (isset($_POST['generate']) && !empty($_POST['answers'])) {
$params['results'] = $generator->save($files, (array)$_POST['answers'], $hasError);
$params['hasError'] = $hasError;
$params['hasError'] = $generator->save($files, (array)$_POST['answers'], $results);
$params['results'] = $results;
} else {
$params['files'] = $files;
$params['answers'] = isset($_POST['answers']) ? $_POST['answers'] : null;
......@@ -57,9 +57,14 @@ class DefaultController extends Controller
{
$generator = $this->loadGenerator($id);
if ($generator->validate()) {
foreach ($generator->prepare() as $f) {
foreach ($generator->generate() as $f) {
if ($f->id === $file) {
return $f->preview();
$content = $f->preview();
if ($content !== false) {
return '<div class="content">' . $content . '</content>';
} else {
return '<div class="error">Preview is not available for this file type.</div>';
}
}
}
}
......@@ -70,7 +75,7 @@ class DefaultController extends Controller
{
$generator = $this->loadGenerator($id);
if ($generator->validate()) {
foreach ($generator->prepare() as $f) {
foreach ($generator->generate() as $f) {
if ($f->id === $file) {
return $this->renderPartial('diff', array(
'diff' => $f->diff(),
......
......@@ -10,43 +10,69 @@ namespace yii\gii\generators\controller;
use Yii;
use yii\gii\CodeFile;
use yii\helpers\Html;
use yii\helpers\Inflector;
/**
* This generator will generate a controller and one or a few action view files.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class Generator extends \yii\gii\Generator
{
/**
* @var string the controller ID
*/
public $controller;
/**
* @var string the base class of the controller
*/
public $baseClass = 'yii\web\Controller';
/**
* @var string the namespace of the controller class
*/
public $ns = 'app\controllers';
/**
* @var string list of action IDs separated by commas or spaces
*/
public $actions = 'index';
/**
* @inheritdoc
*/
public function getName()
{
return 'Controller Generator';
}
/**
* @inheritdoc
*/
public function getDescription()
{
return 'This generator helps you to quickly generate a new controller class,
one or several controller actions and their corresponding views.';
}
/**
* @inheritdoc
*/
public function rules()
{
return array_merge(parent::rules(), array(
array('controller, actions, baseClass, ns', 'filter', 'filter' => 'trim'),
array('controller, baseClass', 'required'),
array('controller', 'match', 'pattern' => '/^[\w+\\/]*$/', 'message' => 'Only word characters and slashes are allowed.'),
array('actions', 'match', 'pattern' => '/^\w+[\w\s,]*$/', 'message' => 'Only word characters, spaces and commas are allowed.'),
array('baseClass', 'match', 'pattern' => '/^[a-zA-Z_][\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'),
array('controller', 'match', 'pattern' => '/^[a-z\\-\\/]*$/', 'message' => 'Only a-z, dashes (-) and slashes (/) are allowed.'),
array('actions', 'match', 'pattern' => '/^[a-z\\-,\\s]*$/', 'message' => 'Only a-z, dashes (-), spaces and commas are allowed.'),
array('baseClass', 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'),
array('baseClass', 'validateReservedWord'),
array('ns', 'match', 'pattern' => '/^[a-zA-Z_][\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'),
array('ns', 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'),
));
}
/**
* @inheritdoc
*/
public function attributeLabels()
{
return array(
......@@ -57,6 +83,9 @@ class Generator extends \yii\gii\Generator
);
}
/**
* @inheritdoc
*/
public function requiredTemplates()
{
return array(
......@@ -65,26 +94,40 @@ class Generator extends \yii\gii\Generator
);
}
/**
* @inheritdoc
*/
public function stickyAttributes()
{
return array('ns', 'baseClass');
}
/**
* @inheritdoc
*/
public function hints()
{
return array(
'controller' => 'Controller ID should be in lower case and may contain module ID(s). For example:
'controller' => 'Controller ID should be in lower case and may contain module ID(s) separated by slashes. For example:
<ul>
<li><code>order</code> generates <code>OrderController.php</code></li>
<li><code>order-item</code> generates <code>OrderItemController.php</code></li>
<li><code>admin/user</code> generates <code>UserController.php</code> within the <code>admin</code> module.</li>
</ul>',
'actions' => 'Provide one or multiple action IDs to generate empty action method(s) in the controller. Separate multiple action IDs with commas or spaces.',
'actions' => 'Provide one or multiple action IDs to generate empty action method(s) in the controller. Separate multiple action IDs with commas or spaces.
Action IDs should be in lower case. For example:
<ul>
<li><code>index</code> generates <code>actionIndex()</code></li>
<li><code>create-order</code> generates <code>actionCreateOrder()</code></li>
</ul>',
'ns' => 'This is the namespace that the new controller class will should use.',
'baseClass' => 'This is the class that the new controller class will extend from. Please make sure the class exists and can be autoloaded.',
);
}
/**
* @inheritdoc
*/
public function successMessage()
{
$actions = $this->getActionIDs();
......@@ -97,7 +140,10 @@ class Generator extends \yii\gii\Generator
return "The controller has been generated successfully. You may $link.";
}
public function prepare()
/**
* @inheritdoc
*/
public function generate()
{
$files = array();
......@@ -105,19 +151,23 @@ class Generator extends \yii\gii\Generator
$files[] = new CodeFile(
$this->getControllerFile(),
$this->generateCode($templatePath . '/controller.php')
$this->render($templatePath . '/controller.php')
);
foreach ($this->getActionIDs() as $action) {
$files[] = new CodeFile(
$this->getViewFile($action),
$this->generateCode($templatePath . '/view.php', array('action' => $action))
$this->render($templatePath . '/view.php', array('action' => $action))
);
}
return $files;
}
/**
* Normalizes [[actions]] into an array of action IDs.
* @return array an array of action IDs entered by the user
*/
public function getActionIDs()
{
$actions = array_unique(preg_split('/[\s,]+/', $this->actions, -1, PREG_SPLIT_NO_EMPTY));
......@@ -127,10 +177,15 @@ class Generator extends \yii\gii\Generator
public function getControllerClass()
{
return Inflector::id2camel($this->getControllerID()) . 'Controller';
}
public function getControllerID()
{
if (($pos = strrpos($this->controller, '/')) !== false) {
return ucfirst(substr($this->controller, $pos + 1)) . 'Controller';
return substr($this->controller, $pos + 1);
} else {
return ucfirst($this->controller) . 'Controller';
return $this->controller;
}
}
......@@ -145,36 +200,15 @@ class Generator extends \yii\gii\Generator
return Yii::$app;
}
public function getControllerID()
{
if ($this->getModule() !== Yii::$app) {
$id = substr($this->controller, strpos($this->controller, '/') + 1);
} else {
$id = $this->controller;
}
if (($pos = strrpos($id, '/')) !== false) {
$id[$pos + 1] = strtolower($id[$pos + 1]);
} else {
$id[0] = strtolower($id[0]);
}
return $id;
}
public function getControllerFile()
{
$module = $this->getModule();
$id = $this->getControllerID();
if (($pos = strrpos($id, '/')) !== false) {
$id[$pos + 1] = strtoupper($id[$pos + 1]);
} else {
$id[0] = strtoupper($id[0]);
}
return $module->getControllerPath() . '/' . $id . 'Controller.php';
return $module->getControllerPath() . '/' . $this->getControllerClass() . '.php';
}
public function getViewFile($action)
{
$module=$this->getModule();
return $module->getViewPath().'/'.$this->getControllerID().'/'.$action.'.php';
$module = $this->getModule();
return $module->getViewPath() . '/' . $this->getControllerID() . '/' . $action . '.php';
}
}
<?php
use yii\helpers\Inflector;
/**
* This is the template for generating a controller class file.
*
......@@ -12,10 +15,10 @@
namespace <?php echo $generator->ns; ?>;
<?php endif; ?>
class <?php echo $generator->controllerClass; ?> extends <?php echo '\\' . ltrim($generator->baseClass, '\\') . "\n"; ?>
class <?php echo $generator->getControllerClass(); ?> extends <?php echo '\\' . trim($generator->baseClass, '\\') . "\n"; ?>
{
<?php foreach($generator->getActionIDs() as $action): ?>
public function action<?php echo ucfirst($action); ?>()
public function action<?php echo Inflector::id2camel($action); ?>()
{
return $this->render('<?php echo $action; ?>');
}
......
......@@ -13,7 +13,7 @@
*/
<?php echo "?>"; ?>
<h1><?php echo $action; ?></h1>
<h1><?php echo $generator->getControllerID() . '/' . $action; ?></h1>
<p>
You may change the content of this page by modifying
......
......@@ -24,4 +24,12 @@ class Generator extends \yii\gii\Generator
return 'This generator generates a controller and views that implement CRUD (Create, Read, Update, Delete)
operations for the specified data model.';
}
/**
* @inheritdoc
*/
public function generate()
{
return array();
}
}
......@@ -23,4 +23,12 @@ class Generator extends \yii\gii\Generator
{
return 'This generator generates a view script file that displays a form to collect input for the specified model class.';
}
/**
* @inheritdoc
*/
public function generate()
{
return array();
}
}
......@@ -23,4 +23,12 @@ class Generator extends \yii\gii\Generator
{
return 'This generator generates a model class for the specified database table.';
}
/**
* @inheritdoc
*/
public function generate()
{
return array();
}
}
......@@ -23,4 +23,12 @@ class Generator extends \yii\gii\Generator
{
return 'This generator helps you to generate the skeleton code needed by a Yii module.';
}
/**
* @inheritdoc
*/
public function generate()
{
return array();
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment