-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
659be22
commit ec762b6
Showing
47 changed files
with
1,968 additions
and
1,657 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,285 @@ | ||
<?php | ||
|
||
/** | ||
* This file is part of the CodeIgniter 4 framework. | ||
* | ||
* (c) CodeIgniter Foundation <[email protected]> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace CodeIgniter\CLI; | ||
|
||
use Config\Generators; | ||
use Config\Services; | ||
use RuntimeException; | ||
use Throwable; | ||
|
||
/** | ||
* GeneratorTrait contains a collection of methods | ||
* to build the commands that generates a file. | ||
*/ | ||
trait GeneratorTrait | ||
{ | ||
/** | ||
* Component Name | ||
* | ||
* @var string | ||
*/ | ||
protected $component; | ||
|
||
/** | ||
* File directory | ||
* | ||
* @var string | ||
*/ | ||
protected $directory; | ||
|
||
/** | ||
* View template name | ||
* | ||
* @var string | ||
*/ | ||
protected $template; | ||
|
||
/** | ||
* Fixed class name | ||
* | ||
* @var string | ||
*/ | ||
protected $className; | ||
|
||
/** | ||
* View template name | ||
* | ||
* @var string | ||
*/ | ||
protected $timestamp = false; | ||
|
||
/** | ||
* Execute the command. | ||
* | ||
* @param array $params | ||
* | ||
* @return void | ||
*/ | ||
public function execute(array $params): void | ||
{ | ||
if (CLI::getOption('namespace') === 'CodeIgniter') | ||
{ | ||
CLI::error(lang('CLI.generator.invalidNamespace')); | ||
CLI::newLine(); | ||
|
||
return; | ||
} | ||
|
||
// Get the fully qualified class name from the input. | ||
$class = $this->qualifyClassName(); | ||
|
||
// Try to get the file path from this. | ||
$path = $this->buildPath($class); | ||
|
||
// Overwriting files unknowingly is a serious annoyance. | ||
// So we'll check if we are duplicating things. | ||
// If the 'force' option is not supplied, we bail. | ||
if (! CLI::getOption('force') && file_exists($path)) | ||
{ | ||
CLI::write(lang('CLI.generator.fileExists', [CLI::color(clean_path($path), 'red')])); | ||
CLI::newLine(); | ||
|
||
return; | ||
} | ||
|
||
// Check if the directory to save the file is existing. | ||
$dir = dirname($path); | ||
|
||
if (! is_dir($dir)) | ||
{ | ||
mkdir($dir, 0755, true); | ||
} | ||
|
||
helper('filesystem'); | ||
|
||
// Build the class based on the details we have. | ||
// We'll be getting our file contents from the template, | ||
// and then we'll do the necessary replacements. | ||
if (! write_file($path, $this->buildContent($class))) | ||
{ | ||
CLI::error(lang('CLI.generator.fileError', [clean_path($path)]), 'light_gray', 'red'); | ||
CLI::newLine(); | ||
|
||
return; | ||
} | ||
|
||
if (CLI::getOption('force') && file_exists($path)) | ||
{ | ||
CLI::write(lang('CLI.generator.fileOverwrite', [CLI::color(clean_path($path), 'yellow')])); | ||
CLI::newLine(); | ||
|
||
return; | ||
} | ||
|
||
CLI::write(lang('CLI.generator.fileCreate', [CLI::color(clean_path($path), 'green')])); | ||
CLI::newLine(); | ||
} | ||
|
||
/** | ||
* Parses the class name and checks if it is already qualified. | ||
* | ||
* @return string | ||
*/ | ||
protected function qualifyClassName(): string | ||
{ | ||
// Gets the class name from input. This can be overridden | ||
// if name is really required by providing a prompt. | ||
$class = $this->className ?? CLI::getSegment(2) ?? ''; | ||
|
||
if (empty($class)) | ||
{ | ||
$class = CLI::prompt('Class name', null, 'required'); | ||
} | ||
|
||
helper('inflector'); | ||
|
||
$logic = singular(strtolower($this->component)); | ||
$class = strtolower($class); | ||
$class = ucfirst(strpos($class, $logic) ? str_replace($logic, '', $class) : $class); | ||
|
||
if (! CLI::getOption('prevent')) | ||
{ | ||
$class .= ucfirst($logic); | ||
} | ||
|
||
// Trims input, normalize separators, and ensure that all | ||
// paths are in Pascalcase. | ||
$class = ltrim(implode('\\', array_map( | ||
'pascalize', explode('\\', str_replace('/', '\\', trim($class))) | ||
)), '\\/'); | ||
|
||
// Gets the root namespace from input. | ||
$root = trim(str_replace('/', '\\', CLI::getOption('namespace') ?? APP_NAMESPACE), '\\'); | ||
|
||
if (strncmp($class, $root, strlen($root)) === 0) | ||
{ | ||
return $class; | ||
} | ||
|
||
return $root . '\\' . $this->directory . '\\' . str_replace('/', '\\', $class); | ||
} | ||
|
||
/** | ||
* Builds the file path from the class name. | ||
* | ||
* @param string $class | ||
* | ||
* @return string | ||
*/ | ||
protected function buildPath(string $class): string | ||
{ | ||
$root = trim(str_replace('/', '\\', CLI::getOption('namespace') ?? APP_NAMESPACE), '\\'); | ||
|
||
// Check if the namespace is actually defined and we are not just typing gibberish. | ||
$base = Services::autoloader()->getNamespace($root); | ||
|
||
if (! $base = reset($base)) | ||
{ | ||
throw new RuntimeException(lang('CLI.namespaceNotDefined', [$root])); | ||
} | ||
|
||
$base = realpath($base) ?: $base; | ||
$file = $base . DIRECTORY_SEPARATOR . str_replace('\\', DIRECTORY_SEPARATOR, trim(str_replace($root, '', $class), '\\')) . '.php'; | ||
|
||
// Change the file's basename before saving. | ||
$filename = basename($file); | ||
|
||
// Useful for migrations files that the name has a date. | ||
if ($this->timestamp) | ||
{ | ||
$filename = gmdate(config('Migrations')->timestampFormat) . $filename; | ||
} | ||
|
||
return implode(DIRECTORY_SEPARATOR, array_slice(explode(DIRECTORY_SEPARATOR, $file), 0, -1)) . DIRECTORY_SEPARATOR . $filename; | ||
} | ||
|
||
/** | ||
* Gets the generator view as defined in the `Config\Generators::$views`, | ||
* with fallback to `$template` when the defined view does not exist. | ||
* | ||
* @param array $data Data to be passed to the view. | ||
* | ||
* @return string | ||
*/ | ||
protected function renderTemplate(array $data = []): string | ||
{ | ||
try | ||
{ | ||
return view($this->config->views[$this->name], $data, ['debug' => false]); | ||
} | ||
catch (Throwable $e) | ||
{ | ||
log_message('error', $e->getMessage()); | ||
|
||
return view("CodeIgniter\Commands\Generators\Views\\{$this->template}", $data, [ | ||
'debug' => false, | ||
]); | ||
} | ||
} | ||
|
||
/** | ||
* Performs the necessary replacements. | ||
* | ||
* @param string $class | ||
* | ||
* @return string | ||
*/ | ||
protected function setReplacements(string $class): string | ||
{ | ||
return $this->replacement($class); | ||
} | ||
|
||
/** | ||
* Actually do the necessary replacements. | ||
* | ||
* @param string $class | ||
* @param array $search | ||
* @param array $replace | ||
* @param array $data | ||
* | ||
* @return string | ||
*/ | ||
protected function replacement(string $class, array $search = [], array $replace = [], $data = []): string | ||
{ | ||
// Retrieves the namespace part from the fully qualified class name. | ||
$namespace = trim(implode('\\', array_slice(explode('\\', $class), 0, -1)), '\\'); | ||
|
||
array_push($search, '<@php', '{namespace}', '{class}'); | ||
array_push($replace, '<?php', $namespace, str_replace($namespace . '\\', '', $class)); | ||
|
||
return str_replace($search, $replace, $this->renderTemplate($data)); | ||
} | ||
|
||
/** | ||
* Builds the contents for class being generated, doing all | ||
* the replacements necessary, and alphabetically sorts the | ||
* imports for a given template. | ||
* | ||
* @param string $class | ||
* | ||
* @return string | ||
*/ | ||
protected function buildContent(string $class): string | ||
{ | ||
$template = $this->setReplacements($class); | ||
|
||
if (preg_match('/(?P<imports>(?:^use [^;]+;$\n?)+)/m', $template, $match)) | ||
{ | ||
$imports = explode("\n", trim($match['imports'])); | ||
sort($imports); | ||
|
||
return str_replace(trim($match['imports']), implode("\n", $imports), $template); | ||
} | ||
|
||
return $template; | ||
} | ||
} |
Oops, something went wrong.