Awesome
Muse - The PHP Code Generator
A powerful php scaffolding framework, help developers generate their code by custom templates.
Installation via Composer
Add this dependency in your composer.json
.
{
"require": {
"asika/muse": "1.*",
"windwalker/console": "~2.0"
}
}
Or just create a project:
php composer.phar create-project asika/muse muse 1.*
Getting Started
Muse is a command line based program, we will do everything though CLI. Please type:
php bin/muse
You will get this help message:
Muse - The Muse - version: 1
------------------------------------------------------------
[muse Help]
Muse console application.
Usage:
muse <command> [option]
Options:
-h | --help Display this help message.
-q | --quiet Do not output any message.
-v | --verbose Increase the verbosity of messages.
--ansi Set 'off' to suppress ANSI colors on unsupported terminals.
-p | --path Dest path.
-t | --tmpl Sub template name.
Commands:
gen Genarate operation.
tmpl-init Init a new template.
tmpl-convert Convert a directory and files back to a template.
Generate code by Acme Template
Acme template is a default template in Muse, generating code is very easy, please type:
php bin/muse gen acme test/MyApp
Now you will see message like below:
$ php bin/muse gen acme test/MyApp
File created: /var/www/muse/test/MyApp/admin/article/edit.twig
File created: /var/www/muse/test/MyApp/admin/article/index.twig
File created: /var/www/muse/test/MyApp/admin/category/edit.twig
File created: /var/www/muse/test/MyApp/admin/category/index.twig
File created: /var/www/muse/test/MyApp/article.twig
File created: /var/www/muse/test/MyApp/global/index.html
File created: /var/www/muse/test/MyApp/index.html
File created: /var/www/muse/test/MyApp/index.twig
Put your SubTemplate to Acme Template
Now you can put your code to src/AcmeTemplate/Template/mytmpl
.
And using this command to generate your sub template:
php bin/muse gen acme test/MyApp2 -t mytmpl
Create your project template
Now everything is very easy, but how can we create our own template? We have to write some code to configure paths and variables.
Init a sample template
Using this command to init a new template.
php bin/muse tmpl-init flower
File created: /var/www/muse/src/FlowerTemplate/Action/ConvertAction.php
File created: /var/www/muse/src/FlowerTemplate/Action/CopyAllAction.php
File created: /var/www/muse/src/FlowerTemplate/Task/Convert.php
File created: /var/www/muse/src/FlowerTemplate/Task/Generate.php
File created: /var/www/muse/src/FlowerTemplate/Template/default/DefaultClass.php
File created: /var/www/muse/src/FlowerTemplate/FlowerTemplate.php
OK, we created a sample template named flower
, this template will locate at src/FlowerTemplate
with an entry class FlowerTemplate
,
actually you can create it manually, but this will be a little complex, so we are better using the sample first.
Configure Variable and Paths
Open FlowerTemplate
, you can set replaced string and copy path here:
Register replacing variables
protected $tagVariable = array('{@', '@}');
protected function registerReplaces($io, $replace = array())
{
$item = $io->getOption('n', 'sakura');
/*
* Replace with your code name.
*/
// Set item name, default is sakura
$replace['item.lower'] = strtolower($item);
$replace['item.upper'] = strtoupper($item);
$replace['item.cap'] = ucfirst($item);
// Set project name
$replace['project.class'] = 'Muse';
return $replace;
}
This example means we can type -n {item}
to be a variable name. And in template code,
the {@item.lower@}
/{@item.upper@}
/{@item.cap@}
will be replace to the item name.
sakura
is the default value if you don't give the -n
param. This is an example that if -n
not found,
just exit and notice user type this param:
$item = $io->getOption('n') ? : exit('Please give me item using "-n {item_name}"');
You can add many string to $replace
array, remember you will need each lower, upper and capital cases, and don't forget to return it.
Register Config & Paths
protected function registerConfig($io, $config)
{
/*
* Replace with your project path.
*/
$subTemplate = $io->getOption('t', 'default');
$dest = $io->getArgument(1) ? : 'generated';
$config['path.src'] = __DIR__ . '/Template/' . $subTemplate;
$config['path.dest'] = GENERATOR_PATH . '/' . $dest;
return $config;
}
You can set some useful config in this method, the most important is path.src
and path.dest
. These two config tell Muse
where code from and where code copied to.
GENERATOR_PATH
is root path of Muse, and the $io->getArgument(1)
means get second argument of your command(First is 0).
Task & Action
We have two default task controller, Generate
and Convert
.
Generate task does the code generate action, and Convert task can help us convert code back to a template.
In task controller we can using doAction()
to execute some different action to do something we want to do.
The Generate
controller class:
namespace FlowerTemplate\Task;
use FlowerTemplate\Action;
use Muse\Controller\AbstractTaskController;
class Generate extends AbstractTaskController
{
public function execute()
{
$this->doAction(new Action\CopyAllAction);
}
}
The CopyAllAction
class
namespace FlowerTemplate\Action;
use Muse\Action\AbstractAction;
use Muse\FileOperator\CopyOperator;
class CopyAllAction extends AbstractAction
{
protected function doExecute()
{
$copyOperator = new CopyOperator($this->io, (array) $this->config['tag.variable']);
$copyOperator->copy($this->config['path.src'], $this->config['path.dest'], $this->config['replace']);
}
}
These two class all very simple and follows single responsibility principle, we can organize our multiple actions in one controller like below:
class Generate extends AbstractTaskController
{
public function execute()
{
$this->doAction(new Action\CopyAllAction);
$this->doAction(new Action\ImportSqlAction);
$this->doAction(new Action\Github\CloneSomeRepoAction);
$this->doAction(new Action\User\CreateNewUserAction);
}
}
The benefit of single action class is that we can re-use every classes in different task.
File Operation
Operator classes
We provides two operators now, copyOperator
help us copy codes and replace tag to variables,
convertOperator
help us copy code too, but replace variable by tags.
Just new an instance and using copy method:
$copyOperator = new CopyOperator($this->io, array('{@', '@}'));
$copyOperator->copy($src, $dest, $replaceArray);
There will be more operator(eg: databaseOperator
, gitOperator
) in the future.
Filesystem
There are three filesystem classes: Path
, File
and Folder
, which extends from Windwalker Filesystem package,
please see: https://github.com/ventoviro/windwalker-filesystem
Simple usage:
namespace Muse\Filesystem;
Filesystem\Folder::copy($src, $dest);
Filesystem\Folder::move($src, $dest);
Filesystem\Folder::create($path);
Filesystem\Folder::delete($path);
Filesystem\File::copy($src, $dest);
Filesystem\File::move($src, $dest);
Filesystem\File::write($path, $buffer);
Filesystem\File::delete($path);
// Replace / and \ to DIRECTORY_SEPARATOR
$path = Filesystem\Path::clean($path);
So you can using Filesystem classes in Action class to help you operate files and directories.
Create a new Task
If you want a new task controller, this will need some steps to create a task. The process not very easy, we will make the process easier in the future.
(1) Create a new Command
Create a command class in src/Muse/Windwalker/Command/MyTask/MyTask.php
namespace Muse\Windwalker\Command\MyTask;
use Muse\Controller\GeneratorController;
use Muse\Windwalker\IO;
use Windwalker\Console\Command\Command;
class MyTask extends Command
{
protected $name = 'mytask';
protected $description = 'Desc of my task.';
protected $usage = 'mytask <cmd><tmpl-name></cmd> <option>[option]</option>';
public function configure()
{
parent::configure();
}
protected function doExecute()
{
$controller = new GeneratorController(new IO($this));
$controller->setTask('mytask')->execute();
}
}
How to use Windwalker Console and Command? See: https://github.com/ventoviro/windwalker-console
(2) Register your command to application
Register this command in src/Muse/Windwalker/Application::registerCommands()
protected function registerCommands()
{
$this->addCommand(new Command\Generate\Generate);
$this->addCommand(new Command\Init\Init);
$this->addCommand(new Command\Convert\Convert);
// Add here
$this->addCommand(new Command\MyTask\Task);
}
You will get new help like this:
Available commands:
gen Genarate operation.
tmpl-init Init a new extension.
tmpl-convert Convert a code folder back to a template.
mytask Desc of my task.
(3) Create a new Task controller
Create a class in src/FlowerTemplate/Task/MyTask.php
namespace FlowerTemplate\Task;
use FlowerTemplate\Action;
use Muse\Controller\TaskController;
class MyTask extends TaskController
{
public function execute()
{
$this->doAction(new Action\CopyAllAction);
}
}
Now you can do some actions here.
(4) Test your task
Typing this command and you can go into your task controller:
php bin/muse mytask <arguments>
Integrate To Your Project or Framework
Muse can integrate to any framework instead default Windwalker Console Application. Just create an IO
class
to help Muse input and output some information:
use Muse\IO\IOInterface;
class MyIOAdapter implements IOInterface
{
// Implement this interface
}
Then use GeneratorController
in your project entry (For example: Symfony Console):
$controller = new GeneratorController(new MyIOAdapter($input, $output));
$controller->setTask($input->getArgument('task'))->execute();
OK it's very easy, have a good time in your code recipe.
Todo
- DatabaseOperator
- GitOperator
- FtpOperator
- UnitTest
- Completed docblock
- Easy to add task controller and command