Viewing file: GeneratorTrait.php (9.73 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
<?php
/** * This file is part of CodeIgniter 4 framework. * * (c) CodeIgniter Foundation <admin@codeigniter.com> * * For the full copyright and license information, please view * the LICENSE file that was distributed with this source code. */
namespace CodeIgniter\CLI;
use Config\Services; 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;
/** * Language string key for required class names. * * @var string */ protected $classNameLang = '';
/** * Whether to require class name. * * @internal * * @var bool */ private $hasClassName = true;
/** * Whether to sort class imports. * * @internal * * @var bool */ private $sortImports = true;
/** * Whether the `--suffix` option has any effect. * * @internal * * @var bool */ private $enabledSuffixing = true;
/** * The params array for easy access by other methods. * * @internal * * @var array */ private $params = [];
/** * Execute the command. */ protected function execute(array $params): void { $this->params = $params;
if ($this->getOption('namespace') === 'CodeIgniter') { // @codeCoverageIgnoreStart CLI::write(lang('CLI.generator.usingCINamespace'), 'yellow'); CLI::newLine();
if (CLI::prompt('Are you sure you want to continue?', ['y', 'n'], 'required') === 'n') { CLI::newLine(); CLI::write(lang('CLI.generator.cancelOperation'), 'yellow'); CLI::newLine();
return; }
CLI::newLine(); // @codeCoverageIgnoreEnd }
// Get the fully qualified class name from the input. $class = $this->qualifyClassName();
// Get the file path from class name. $path = $this->buildPath($class);
// Check if path is empty. if (empty($path)) { return; }
$isFile = is_file($path);
// Overwriting files unknowingly is a serious annoyance, So we'll check if // we are duplicating things, If 'force' option is not supplied, we bail. if (! $this->getOption('force') && $isFile) { CLI::error(lang('CLI.generator.fileExist', [clean_path($path)]), 'light_gray', '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))) { // @codeCoverageIgnoreStart CLI::error(lang('CLI.generator.fileError', [clean_path($path)]), 'light_gray', 'red'); CLI::newLine();
return; // @codeCoverageIgnoreEnd }
if ($this->getOption('force') && $isFile) { CLI::write(lang('CLI.generator.fileOverwrite', [clean_path($path)]), 'yellow'); CLI::newLine();
return; }
CLI::write(lang('CLI.generator.fileCreate', [clean_path($path)]), 'green'); CLI::newLine(); }
/** * Prepare options and do the necessary replacements. */ protected function prepare(string $class): string { return $this->parseTemplate($class); }
/** * Change file basename before saving. * * Useful for components where the file name has a date. */ protected function basename(string $filename): string { return basename($filename); }
/** * Parses the class name and checks if it is already qualified. */ protected function qualifyClassName(): string { // Gets the class name from input. $class = $this->params[0] ?? CLI::getSegment(2);
if ($class === null && $this->hasClassName) { // @codeCoverageIgnoreStart $nameLang = $this->classNameLang ?: 'CLI.generator.className.default'; $class = CLI::prompt(lang($nameLang), null, 'required'); CLI::newLine(); // @codeCoverageIgnoreEnd }
helper('inflector');
$component = singular($this->component);
/** * @see https://regex101.com/r/a5KNCR/1 */ $pattern = sprintf('/([a-z][a-z0-9_\/\\\\]+)(%s)/i', $component);
if (preg_match($pattern, $class, $matches) === 1) { $class = $matches[1] . ucfirst($matches[2]); }
if ($this->enabledSuffixing && $this->getOption('suffix') && ! strripos($class, $component)) { $class .= ucfirst($component); }
// 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 namespace from input. Don't forget the ending backslash! $namespace = trim(str_replace('/', '\\', $this->getOption('namespace') ?? APP_NAMESPACE), '\\') . '\\';
if (strncmp($class, $namespace, strlen($namespace)) === 0) { return $class; // @codeCoverageIgnore }
return $namespace . $this->directory . '\\' . str_replace('/', '\\', $class); }
/** * Gets the generator view as defined in the `Config\Generators::$views`, * with fallback to `$template` when the defined view does not exist. */ protected function renderTemplate(array $data = []): string { try { return view(config('Generators')->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 pseudo-variables contained within view file. */ protected function parseTemplate(string $class, array $search = [], array $replace = [], array $data = []): string { // Retrieves the namespace part from the fully qualified class name. $namespace = trim(implode('\\', array_slice(explode('\\', $class), 0, -1)), '\\'); $search[] = '<@php'; $search[] = '{namespace}'; $search[] = '{class}'; $replace[] = '<?php'; $replace[] = $namespace; $replace[] = 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. */ protected function buildContent(string $class): string { $template = $this->prepare($class);
if ($this->sortImports && 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; }
/** * Builds the file path from the class name. */ protected function buildPath(string $class): string { $namespace = trim(str_replace('/', '\\', $this->getOption('namespace') ?? APP_NAMESPACE), '\\');
// Check if the namespace is actually defined and we are not just typing gibberish. $base = Services::autoloader()->getNamespace($namespace);
if (! $base = reset($base)) { CLI::error(lang('CLI.namespaceNotDefined', [$namespace]), 'light_gray', 'red'); CLI::newLine();
return ''; }
$base = realpath($base) ?: $base; $file = $base . DIRECTORY_SEPARATOR . str_replace('\\', DIRECTORY_SEPARATOR, trim(str_replace($namespace . '\\', '', $class), '\\')) . '.php';
return implode(DIRECTORY_SEPARATOR, array_slice(explode(DIRECTORY_SEPARATOR, $file), 0, -1)) . DIRECTORY_SEPARATOR . $this->basename($file); }
/** * Allows child generators to modify the internal `$hasClassName` flag. * * @return $this */ protected function setHasClassName(bool $hasClassName) { $this->hasClassName = $hasClassName;
return $this; }
/** * Allows child generators to modify the internal `$sortImports` flag. * * @return $this */ protected function setSortImports(bool $sortImports) { $this->sortImports = $sortImports;
return $this; }
/** * Allows child generators to modify the internal `$enabledSuffixing` flag. * * @return $this */ protected function setEnabledSuffixing(bool $enabledSuffixing) { $this->enabledSuffixing = $enabledSuffixing;
return $this; }
/** * Gets a single command-line option. Returns TRUE if the option exists, * but doesn't have a value, and is simply acting as a flag. * * @return mixed */ protected function getOption(string $name) { if (! array_key_exists($name, $this->params)) { return CLI::getOption($name); }
return $this->params[$name] ?? true; } }
|