diff --git a/src/Console/Command.php b/src/Console/Command.php index f1b117180..c8aaf0166 100644 --- a/src/Console/Command.php +++ b/src/Console/Command.php @@ -13,6 +13,23 @@ */ abstract class Command extends BaseCommand { + /** + * @var array List of commands that this command replaces (aliases) + */ + protected $replaces = []; + + /** + * Create a new command instance. + */ + public function __construct() + { + parent::__construct(); + + if (!empty($this->replaces)) { + $this->setAliases($this->replaces); + } + } + /** * Write a string in an alert box. * diff --git a/src/Foundation/Providers/ArtisanServiceProvider.php b/src/Foundation/Providers/ArtisanServiceProvider.php index 6f680fa27..7d23dc970 100644 --- a/src/Foundation/Providers/ArtisanServiceProvider.php +++ b/src/Foundation/Providers/ArtisanServiceProvider.php @@ -80,7 +80,9 @@ class ArtisanServiceProvider extends ArtisanServiceProviderBase // 'CastMake' => CastMakeCommand::class, // 'ChannelMake' => ChannelMakeCommand::class, // 'ComponentMake' => ComponentMakeCommand::class, - // 'ConsoleMake' => ConsoleMakeCommand::class, + + + // 'ControllerMake' => ControllerMakeCommand::class, // 'EventGenerate' => EventGenerateCommand::class, // 'EventMake' => EventMakeCommand::class, @@ -90,7 +92,20 @@ class ArtisanServiceProvider extends ArtisanServiceProviderBase // 'ListenerMake' => ListenerMakeCommand::class, // 'MailMake' => MailMakeCommand::class, // 'MiddlewareMake' => MiddlewareMakeCommand::class, + // 'ModelMake' => ModelMakeCommand::class, + + // MigrationServiceProvider + // 'Migrate' => MigrateCommand::class, + // 'MigrateFresh' => FreshCommand::class, + // 'MigrateInstall' => InstallCommand::class, + // 'MigrateRefresh' => RefreshCommand::class, + // 'MigrateReset' => ResetCommand::class, + // 'MigrateRollback' => RollbackCommand::class, + // 'MigrateStatus' => StatusCommand::class, + // 'MigrateMake' => MigrateMakeCommand::class, + + // 'NotificationMake' => NotificationMakeCommand::class, // 'NotificationTable' => NotificationTableCommand::class, // 'ObserverMake' => ObserverMakeCommand::class, diff --git a/src/Scaffold/GeneratorCommand.php b/src/Scaffold/GeneratorCommand.php index 94dfc245a..71d9d844b 100644 --- a/src/Scaffold/GeneratorCommand.php +++ b/src/Scaffold/GeneratorCommand.php @@ -19,6 +19,87 @@ abstract class GeneratorCommand extends Command */ protected $type; + /** + * @var string The argument that the generated class name comes from + */ + protected $nameFrom = 'name'; + + /** + * Reserved names that cannot be used for generation. + * + * @var string[] + */ + protected $reservedNames = [ + '__halt_compiler', + 'abstract', + 'and', + 'array', + 'as', + 'break', + 'callable', + 'case', + 'catch', + 'class', + 'clone', + 'const', + 'continue', + 'declare', + 'default', + 'die', + 'do', + 'echo', + 'else', + 'elseif', + 'empty', + 'enddeclare', + 'endfor', + 'endforeach', + 'endif', + 'endswitch', + 'endwhile', + 'eval', + 'exit', + 'extends', + 'final', + 'finally', + 'fn', + 'for', + 'foreach', + 'function', + 'global', + 'goto', + 'if', + 'implements', + 'include', + 'include_once', + 'instanceof', + 'insteadof', + 'interface', + 'isset', + 'list', + 'namespace', + 'new', + 'or', + 'print', + 'private', + 'protected', + 'public', + 'require', + 'require_once', + 'return', + 'static', + 'switch', + 'throw', + 'trait', + 'try', + 'unset', + 'use', + 'var', + 'while', + 'xor', + 'yield', + ]; + /** * @var array A mapping of stub to generated file. */ @@ -48,6 +129,15 @@ public function __construct() */ public function handle() { + // First we need to ensure that the given name is not a reserved word within the PHP + // language and that the class name will actually be valid. If it is not valid we + // can error now and prevent from polluting the filesystem using invalid files. + if ($this->isReservedName($this->getNameInput())) { + $this->error('The name "'.$this->getNameInput().'" is reserved by PHP.'); + + return false; + } + $this->vars = $this->processVars($this->prepareVars()); $this->makeStubs(); @@ -57,25 +147,39 @@ public function handle() /** * Prepare variables for stubs. - * - * @return array */ - abstract protected function prepareVars(); + abstract protected function prepareVars(): array; /** * Make all stubs. - * - * @return void */ - public function makeStubs() + public function makeStubs(): void { $stubs = array_keys($this->stubs); + // Make sure this command won't overwrite any existing files before running + if (!$this->option('force')) { + foreach ($stubs as $stub) { + $destinationFile = $this->getDestinationForStub($stub); + if ($this->files->exists($destinationFile)) { + throw new Exception("Cannot create the {$this->type}, $destinationFile already exists. Pass --force to overwrite existing files."); + } + } + } + foreach ($stubs as $stub) { $this->makeStub($stub); } } + /** + * Get the destination path for the provided stub name + */ + protected function getDestinationForStub(string $stubName): string + { + return $this->getDestinationPath() . '/' . $this->stubs[$stubName]; + } + /** * Make a single stub. * @@ -88,7 +192,7 @@ public function makeStub($stubName) } $sourceFile = $this->getSourcePath() . '/' . $stubName; - $destinationFile = $this->getDestinationPath() . '/' . $this->stubs[$stubName]; + $destinationFile = $this->getDestinationForStub($stubName); $destinationContent = $this->files->get($sourceFile); /* @@ -99,13 +203,6 @@ public function makeStub($stubName) $this->makeDirectory($destinationFile); - /* - * Make sure this file does not already exist - */ - if ($this->files->exists($destinationFile) && !$this->option('force')) { - throw new Exception('Stop everything!!! This file already exists: ' . $destinationFile); - } - $this->files->put($destinationFile, $destinationContent); $this->comment('File generated: ' . str_replace(base_path(), '', $destinationFile)); @@ -127,11 +224,8 @@ protected function makeDirectory($path) /** * Converts all variables to available modifier and case formats. * Syntax is CASE_MODIFIER_KEY, eg: lower_plural_xxx - * - * @param array $vars The collection of original variables - * @return array A collection of variables with modifiers added */ - protected function processVars($vars) + protected function processVars(array $vars): array { $cases = ['upper', 'lower', 'snake', 'studly', 'camel', 'title']; $modifiers = ['plural', 'singular', 'title']; @@ -187,27 +281,17 @@ protected function modifyString($type, $string) } /** - * Get the plugin path from the input. - * - * @return string + * Get the base path to output generated stubs to */ - protected function getDestinationPath() + protected function getDestinationPath(): string { - $plugin = $this->getPluginIdentifier(); - - $parts = explode('.', $plugin); - $name = array_pop($parts); - $author = array_pop($parts); - - return plugins_path(strtolower($author) . '/' . strtolower($name)); + return base_path(); } /** - * Get the source file path. - * - * @return string + * Get the base path to source stub files from */ - protected function getSourcePath() + protected function getSourcePath(): string { $className = get_class($this); $class = new ReflectionClass($className); @@ -216,12 +300,20 @@ protected function getSourcePath() } /** - * Get the desired plugin name from the input. - * - * @return string + * Get the desired class name from the input. + */ + protected function getNameInput(): string + { + return trim($this->argument($this->nameFrom)); + } + + /** + * Checks whether the given name is reserved. */ - protected function getPluginIdentifier() + protected function isReservedName(string $name): bool { - return $this->argument('plugin'); + $name = strtolower($name); + + return in_array($name, $this->reservedNames); } } diff --git a/tests/Scaffold/ScaffoldBaseTest.php b/tests/Scaffold/ScaffoldBaseTest.php index 27cf22ba3..7abb4ba22 100644 --- a/tests/Scaffold/ScaffoldBaseTest.php +++ b/tests/Scaffold/ScaffoldBaseTest.php @@ -8,7 +8,7 @@ public function __construct() { } - protected function prepareVars() + protected function prepareVars(): array { return []; }