first commit
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* 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\Commands\Cache;
|
||||
|
||||
use CodeIgniter\Cache\CacheFactory;
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
use Config\Cache;
|
||||
|
||||
/**
|
||||
* Clears current cache.
|
||||
*/
|
||||
class ClearCache extends BaseCommand
|
||||
{
|
||||
/**
|
||||
* Command grouping.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'Cache';
|
||||
|
||||
/**
|
||||
* The Command's name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'cache:clear';
|
||||
|
||||
/**
|
||||
* the Command's short description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Clears the current system caches.';
|
||||
|
||||
/**
|
||||
* the Command's usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'cache:clear [<driver>]';
|
||||
|
||||
/**
|
||||
* the Command's Arguments
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $arguments = [
|
||||
'driver' => 'The cache driver to use',
|
||||
];
|
||||
|
||||
/**
|
||||
* Clears the cache
|
||||
*/
|
||||
public function run(array $params)
|
||||
{
|
||||
$config = config(Cache::class);
|
||||
$handler = $params[0] ?? $config->handler;
|
||||
|
||||
if (! array_key_exists($handler, $config->validHandlers)) {
|
||||
CLI::error($handler . ' is not a valid cache handler.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$config->handler = $handler;
|
||||
$cache = CacheFactory::getHandler($config);
|
||||
|
||||
if (! $cache->clean()) {
|
||||
// @codeCoverageIgnoreStart
|
||||
CLI::error('Error while clearing the cache.');
|
||||
|
||||
return;
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
CLI::write(CLI::color('Cache cleared.', 'green'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* 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\Commands\Cache;
|
||||
|
||||
use CodeIgniter\Cache\CacheFactory;
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
use CodeIgniter\I18n\Time;
|
||||
use Config\Cache;
|
||||
|
||||
/**
|
||||
* Shows information on the cache.
|
||||
*/
|
||||
class InfoCache extends BaseCommand
|
||||
{
|
||||
/**
|
||||
* Command grouping.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'Cache';
|
||||
|
||||
/**
|
||||
* The Command's name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'cache:info';
|
||||
|
||||
/**
|
||||
* the Command's short description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Shows file cache information in the current system.';
|
||||
|
||||
/**
|
||||
* the Command's usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'cache:info';
|
||||
|
||||
/**
|
||||
* Clears the cache
|
||||
*/
|
||||
public function run(array $params)
|
||||
{
|
||||
$config = config(Cache::class);
|
||||
helper('number');
|
||||
|
||||
if ($config->handler !== 'file') {
|
||||
CLI::error('This command only supports the file cache handler.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$cache = CacheFactory::getHandler($config);
|
||||
$caches = $cache->getCacheInfo();
|
||||
$tbody = [];
|
||||
|
||||
foreach ($caches as $key => $field) {
|
||||
$tbody[] = [
|
||||
$key,
|
||||
clean_path($field['server_path']),
|
||||
number_to_size($field['size']),
|
||||
Time::createFromTimestamp($field['date']),
|
||||
];
|
||||
}
|
||||
|
||||
$thead = [
|
||||
CLI::color('Name', 'green'),
|
||||
CLI::color('Server Path', 'green'),
|
||||
CLI::color('Size', 'green'),
|
||||
CLI::color('Date', 'green'),
|
||||
];
|
||||
|
||||
CLI::table($tbody, $thead);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* 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\Commands\Database;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
use CodeIgniter\Config\Factories;
|
||||
use CodeIgniter\Database\SQLite3\Connection;
|
||||
use Config\Database;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Creates a new database.
|
||||
*/
|
||||
class CreateDatabase extends BaseCommand
|
||||
{
|
||||
/**
|
||||
* The group the command is lumped under
|
||||
* when listing commands.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'Database';
|
||||
|
||||
/**
|
||||
* The Command's name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'db:create';
|
||||
|
||||
/**
|
||||
* the Command's short description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create a new database schema.';
|
||||
|
||||
/**
|
||||
* the Command's usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'db:create <db_name> [options]';
|
||||
|
||||
/**
|
||||
* The Command's arguments
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $arguments = [
|
||||
'db_name' => 'The database name to use',
|
||||
];
|
||||
|
||||
/**
|
||||
* The Command's options
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $options = [
|
||||
'--ext' => 'File extension of the database file for SQLite3. Can be `db` or `sqlite`. Defaults to `db`.',
|
||||
];
|
||||
|
||||
/**
|
||||
* Creates a new database.
|
||||
*/
|
||||
public function run(array $params)
|
||||
{
|
||||
$name = array_shift($params);
|
||||
|
||||
if (empty($name)) {
|
||||
$name = CLI::prompt('Database name', null, 'required'); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
try {
|
||||
$config = config(Database::class);
|
||||
|
||||
// Set to an empty database to prevent connection errors.
|
||||
$group = ENVIRONMENT === 'testing' ? 'tests' : $config->defaultGroup;
|
||||
|
||||
$config->{$group}['database'] = '';
|
||||
|
||||
$db = Database::connect();
|
||||
|
||||
// Special SQLite3 handling
|
||||
if ($db instanceof Connection) {
|
||||
$ext = $params['ext'] ?? CLI::getOption('ext') ?? 'db';
|
||||
|
||||
if (! in_array($ext, ['db', 'sqlite'], true)) {
|
||||
$ext = CLI::prompt('Please choose a valid file extension', ['db', 'sqlite']); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
if ($name !== ':memory:') {
|
||||
$name = str_replace(['.db', '.sqlite'], '', $name) . ".{$ext}";
|
||||
}
|
||||
|
||||
$config->{$group}['DBDriver'] = 'SQLite3';
|
||||
$config->{$group}['database'] = $name;
|
||||
|
||||
if ($name !== ':memory:') {
|
||||
$dbName = ! str_contains($name, DIRECTORY_SEPARATOR) ? WRITEPATH . $name : $name;
|
||||
|
||||
if (is_file($dbName)) {
|
||||
CLI::error("Database \"{$dbName}\" already exists.", 'light_gray', 'red');
|
||||
CLI::newLine();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
unset($dbName);
|
||||
}
|
||||
|
||||
// Connect to new SQLite3 to create new database
|
||||
$db = Database::connect(null, false);
|
||||
$db->connect();
|
||||
|
||||
if (! is_file($db->getDatabase()) && $name !== ':memory:') {
|
||||
// @codeCoverageIgnoreStart
|
||||
CLI::error('Database creation failed.', 'light_gray', 'red');
|
||||
CLI::newLine();
|
||||
|
||||
return;
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
} elseif (! Database::forge()->createDatabase($name)) {
|
||||
// @codeCoverageIgnoreStart
|
||||
CLI::error('Database creation failed.', 'light_gray', 'red');
|
||||
CLI::newLine();
|
||||
|
||||
return;
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
CLI::write("Database \"{$name}\" successfully created.", 'green');
|
||||
CLI::newLine();
|
||||
} catch (Throwable $e) {
|
||||
$this->showError($e);
|
||||
} finally {
|
||||
Factories::reset('config');
|
||||
Database::connect(null, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* 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\Commands\Database;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Runs all new migrations.
|
||||
*/
|
||||
class Migrate extends BaseCommand
|
||||
{
|
||||
/**
|
||||
* The group the command is lumped under
|
||||
* when listing commands.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'Database';
|
||||
|
||||
/**
|
||||
* The Command's name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'migrate';
|
||||
|
||||
/**
|
||||
* the Command's short description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Locates and runs all new migrations against the database.';
|
||||
|
||||
/**
|
||||
* the Command's usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'migrate [options]';
|
||||
|
||||
/**
|
||||
* the Command's Options
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $options = [
|
||||
'-n' => 'Set migration namespace',
|
||||
'-g' => 'Set database group',
|
||||
'--all' => 'Set for all namespaces, will ignore (-n) option',
|
||||
];
|
||||
|
||||
/**
|
||||
* Ensures that all migrations have been run.
|
||||
*/
|
||||
public function run(array $params)
|
||||
{
|
||||
$runner = service('migrations');
|
||||
$runner->clearCliMessages();
|
||||
|
||||
CLI::write(lang('Migrations.latest'), 'yellow');
|
||||
|
||||
$namespace = $params['n'] ?? CLI::getOption('n');
|
||||
$group = $params['g'] ?? CLI::getOption('g');
|
||||
|
||||
try {
|
||||
if (array_key_exists('all', $params) || CLI::getOption('all')) {
|
||||
$runner->setNamespace(null);
|
||||
} elseif ($namespace) {
|
||||
$runner->setNamespace($namespace);
|
||||
}
|
||||
|
||||
if (! $runner->latest($group)) {
|
||||
CLI::error(lang('Migrations.generalFault'), 'light_gray', 'red'); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$messages = $runner->getCliMessages();
|
||||
|
||||
foreach ($messages as $message) {
|
||||
CLI::write($message);
|
||||
}
|
||||
|
||||
CLI::write(lang('Migrations.migrated'), 'green');
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
} catch (Throwable $e) {
|
||||
$this->showError($e);
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* 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\Commands\Database;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
|
||||
/**
|
||||
* Does a rollback followed by a latest to refresh the current state
|
||||
* of the database.
|
||||
*/
|
||||
class MigrateRefresh extends BaseCommand
|
||||
{
|
||||
/**
|
||||
* The group the command is lumped under
|
||||
* when listing commands.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'Database';
|
||||
|
||||
/**
|
||||
* The Command's name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'migrate:refresh';
|
||||
|
||||
/**
|
||||
* the Command's short description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Does a rollback followed by a latest to refresh the current state of the database.';
|
||||
|
||||
/**
|
||||
* the Command's usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'migrate:refresh [options]';
|
||||
|
||||
/**
|
||||
* the Command's Options
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $options = [
|
||||
'-n' => 'Set migration namespace',
|
||||
'-g' => 'Set database group',
|
||||
'--all' => 'Set latest for all namespace, will ignore (-n) option',
|
||||
'-f' => 'Force command - this option allows you to bypass the confirmation question when running this command in a production environment',
|
||||
];
|
||||
|
||||
/**
|
||||
* Does a rollback followed by a latest to refresh the current state
|
||||
* of the database.
|
||||
*/
|
||||
public function run(array $params)
|
||||
{
|
||||
$params['b'] = 0;
|
||||
|
||||
if (ENVIRONMENT === 'production') {
|
||||
// @codeCoverageIgnoreStart
|
||||
$force = array_key_exists('f', $params) || CLI::getOption('f');
|
||||
|
||||
if (! $force && CLI::prompt(lang('Migrations.refreshConfirm'), ['y', 'n']) === 'n') {
|
||||
return;
|
||||
}
|
||||
|
||||
$params['f'] = null;
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
$this->call('migrate:rollback', $params);
|
||||
$this->call('migrate', $params);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* 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\Commands\Database;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
use CodeIgniter\Database\MigrationRunner;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Runs all of the migrations in reverse order, until they have
|
||||
* all been unapplied.
|
||||
*/
|
||||
class MigrateRollback extends BaseCommand
|
||||
{
|
||||
/**
|
||||
* The group the command is lumped under
|
||||
* when listing commands.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'Database';
|
||||
|
||||
/**
|
||||
* The Command's name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'migrate:rollback';
|
||||
|
||||
/**
|
||||
* the Command's short description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Runs the "down" method for all migrations in the last batch.';
|
||||
|
||||
/**
|
||||
* the Command's usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'migrate:rollback [options]';
|
||||
|
||||
/**
|
||||
* the Command's Options
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $options = [
|
||||
'-b' => 'Specify a batch to roll back to; e.g. "3" to return to batch #3',
|
||||
'-f' => 'Force command - this option allows you to bypass the confirmation question when running this command in a production environment',
|
||||
];
|
||||
|
||||
/**
|
||||
* Runs all of the migrations in reverse order, until they have
|
||||
* all been unapplied.
|
||||
*/
|
||||
public function run(array $params)
|
||||
{
|
||||
if (ENVIRONMENT === 'production') {
|
||||
// @codeCoverageIgnoreStart
|
||||
$force = array_key_exists('f', $params) || CLI::getOption('f');
|
||||
|
||||
if (! $force && CLI::prompt(lang('Migrations.rollBackConfirm'), ['y', 'n']) === 'n') {
|
||||
return;
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
/** @var MigrationRunner $runner */
|
||||
$runner = service('migrations');
|
||||
|
||||
try {
|
||||
$batch = $params['b'] ?? CLI::getOption('b') ?? $runner->getLastBatch() - 1;
|
||||
|
||||
if (is_string($batch)) {
|
||||
if (! ctype_digit($batch)) {
|
||||
CLI::error('Invalid batch number: ' . $batch, 'light_gray', 'red');
|
||||
CLI::newLine();
|
||||
|
||||
return EXIT_ERROR;
|
||||
}
|
||||
|
||||
$batch = (int) $batch;
|
||||
}
|
||||
|
||||
CLI::write(lang('Migrations.rollingBack') . ' ' . $batch, 'yellow');
|
||||
|
||||
if (! $runner->regress($batch)) {
|
||||
CLI::error(lang('Migrations.generalFault'), 'light_gray', 'red'); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$messages = $runner->getCliMessages();
|
||||
|
||||
foreach ($messages as $message) {
|
||||
CLI::write($message);
|
||||
}
|
||||
|
||||
CLI::write('Done rolling back migrations.', 'green');
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
} catch (Throwable $e) {
|
||||
$this->showError($e);
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* 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\Commands\Database;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
|
||||
/**
|
||||
* Displays a list of all migrations and whether they've been run or not.
|
||||
*
|
||||
* @see \CodeIgniter\Commands\Database\MigrateStatusTest
|
||||
*/
|
||||
class MigrateStatus extends BaseCommand
|
||||
{
|
||||
/**
|
||||
* The group the command is lumped under
|
||||
* when listing commands.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'Database';
|
||||
|
||||
/**
|
||||
* The Command's name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'migrate:status';
|
||||
|
||||
/**
|
||||
* the Command's short description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Displays a list of all migrations and whether they\'ve been run or not.';
|
||||
|
||||
/**
|
||||
* the Command's usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'migrate:status [options]';
|
||||
|
||||
/**
|
||||
* the Command's Options
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $options = [
|
||||
'-g' => 'Set database group',
|
||||
];
|
||||
|
||||
/**
|
||||
* Namespaces to ignore when looking for migrations.
|
||||
*
|
||||
* @var list<string>
|
||||
*/
|
||||
protected $ignoredNamespaces = [
|
||||
'CodeIgniter',
|
||||
'Config',
|
||||
'Kint',
|
||||
'Laminas\ZendFrameworkBridge',
|
||||
'Laminas\Escaper',
|
||||
'Psr\Log',
|
||||
];
|
||||
|
||||
/**
|
||||
* Displays a list of all migrations and whether they've been run or not.
|
||||
*
|
||||
* @param array<string, mixed> $params
|
||||
*/
|
||||
public function run(array $params)
|
||||
{
|
||||
$runner = service('migrations');
|
||||
$paramGroup = $params['g'] ?? CLI::getOption('g');
|
||||
|
||||
// Get all namespaces
|
||||
$namespaces = service('autoloader')->getNamespace();
|
||||
|
||||
// Collection of migration status
|
||||
$status = [];
|
||||
|
||||
foreach (array_keys($namespaces) as $namespace) {
|
||||
if (ENVIRONMENT !== 'testing') {
|
||||
// Make Tests\\Support discoverable for testing
|
||||
$this->ignoredNamespaces[] = 'Tests\Support'; // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
if (in_array($namespace, $this->ignoredNamespaces, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (APP_NAMESPACE !== 'App' && $namespace === 'App') {
|
||||
continue; // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$migrations = $runner->findNamespaceMigrations($namespace);
|
||||
|
||||
if (empty($migrations)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$runner->setNamespace($namespace);
|
||||
$history = $runner->getHistory((string) $paramGroup);
|
||||
ksort($migrations);
|
||||
|
||||
foreach ($migrations as $uid => $migration) {
|
||||
$migrations[$uid]->name = mb_substr($migration->name, (int) mb_strpos($migration->name, $uid . '_'));
|
||||
|
||||
$date = '---';
|
||||
$group = '---';
|
||||
$batch = '---';
|
||||
|
||||
foreach ($history as $row) {
|
||||
// @codeCoverageIgnoreStart
|
||||
if ($runner->getObjectUid($row) !== $migration->uid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$date = date('Y-m-d H:i:s', (int) $row->time);
|
||||
$group = $row->group;
|
||||
$batch = $row->batch;
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
$status[] = [
|
||||
$namespace,
|
||||
$migration->version,
|
||||
$migration->name,
|
||||
$group,
|
||||
$date,
|
||||
$batch,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if ($status === []) {
|
||||
// @codeCoverageIgnoreStart
|
||||
CLI::error(lang('Migrations.noneFound'), 'light_gray', 'red');
|
||||
CLI::newLine();
|
||||
|
||||
return;
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
$headers = [
|
||||
CLI::color(lang('Migrations.namespace'), 'yellow'),
|
||||
CLI::color(lang('Migrations.version'), 'yellow'),
|
||||
CLI::color(lang('Migrations.filename'), 'yellow'),
|
||||
CLI::color(lang('Migrations.group'), 'yellow'),
|
||||
CLI::color(str_replace(': ', '', lang('Migrations.on')), 'yellow'),
|
||||
CLI::color(lang('Migrations.batch'), 'yellow'),
|
||||
];
|
||||
|
||||
CLI::table($status, $headers);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* 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\Commands\Database;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
use CodeIgniter\Database\Seeder;
|
||||
use Config\Database;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Runs the specified Seeder file to populate the database
|
||||
* with some data.
|
||||
*/
|
||||
class Seed extends BaseCommand
|
||||
{
|
||||
/**
|
||||
* The group the command is lumped under
|
||||
* when listing commands.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'Database';
|
||||
|
||||
/**
|
||||
* The Command's name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'db:seed';
|
||||
|
||||
/**
|
||||
* the Command's short description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Runs the specified seeder to populate known data into the database.';
|
||||
|
||||
/**
|
||||
* the Command's usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'db:seed <seeder_name>';
|
||||
|
||||
/**
|
||||
* the Command's Arguments
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $arguments = [
|
||||
'seeder_name' => 'The seeder name to run',
|
||||
];
|
||||
|
||||
/**
|
||||
* Passes to Seeder to populate the database.
|
||||
*/
|
||||
public function run(array $params)
|
||||
{
|
||||
$seeder = new Seeder(new Database());
|
||||
$seedName = array_shift($params);
|
||||
|
||||
if (empty($seedName)) {
|
||||
$seedName = CLI::prompt(lang('Migrations.migSeeder'), null, 'required'); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
try {
|
||||
$seeder->call($seedName);
|
||||
} catch (Throwable $e) {
|
||||
$this->showError($e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,345 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* 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\Commands\Database;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
use CodeIgniter\Database\BaseConnection;
|
||||
use Config\Database;
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Get table data if it exists in the database.
|
||||
*
|
||||
* @see \CodeIgniter\Commands\Database\ShowTableInfoTest
|
||||
*/
|
||||
class ShowTableInfo extends BaseCommand
|
||||
{
|
||||
/**
|
||||
* The group the command is lumped under
|
||||
* when listing commands.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'Database';
|
||||
|
||||
/**
|
||||
* The Command's name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'db:table';
|
||||
|
||||
/**
|
||||
* the Command's short description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Retrieves information on the selected table.';
|
||||
|
||||
/**
|
||||
* the Command's usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = <<<'EOL'
|
||||
db:table [<table_name>] [options]
|
||||
|
||||
Examples:
|
||||
db:table --show
|
||||
db:table --metadata
|
||||
db:table my_table --metadata
|
||||
db:table my_table
|
||||
db:table my_table --limit-rows 5 --limit-field-value 10 --desc
|
||||
EOL;
|
||||
|
||||
/**
|
||||
* The Command's arguments
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $arguments = [
|
||||
'table_name' => 'The table name to show info',
|
||||
];
|
||||
|
||||
/**
|
||||
* The Command's options
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $options = [
|
||||
'--show' => 'Lists the names of all database tables.',
|
||||
'--metadata' => 'Retrieves list containing field information.',
|
||||
'--desc' => 'Sorts the table rows in DESC order.',
|
||||
'--limit-rows' => 'Limits the number of rows. Default: 10.',
|
||||
'--limit-field-value' => 'Limits the length of field values. Default: 15.',
|
||||
'--dbgroup' => 'Database group to show.',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var list<list<int|string>> Table Data.
|
||||
*/
|
||||
private array $tbody;
|
||||
|
||||
private ?BaseConnection $db = null;
|
||||
|
||||
/**
|
||||
* @var bool Sort the table rows in DESC order or not.
|
||||
*/
|
||||
private bool $sortDesc = false;
|
||||
|
||||
private string $DBPrefix;
|
||||
|
||||
public function run(array $params)
|
||||
{
|
||||
$dbGroup = $params['dbgroup'] ?? CLI::getOption('dbgroup');
|
||||
|
||||
try {
|
||||
$this->db = Database::connect($dbGroup);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
CLI::error($e->getMessage());
|
||||
|
||||
return EXIT_ERROR;
|
||||
}
|
||||
|
||||
$this->DBPrefix = $this->db->getPrefix();
|
||||
|
||||
$this->showDBConfig();
|
||||
|
||||
$tables = $this->db->listTables();
|
||||
|
||||
if (array_key_exists('desc', $params)) {
|
||||
$this->sortDesc = true;
|
||||
}
|
||||
|
||||
if ($tables === []) {
|
||||
CLI::error('Database has no tables!', 'light_gray', 'red');
|
||||
CLI::newLine();
|
||||
|
||||
return EXIT_ERROR;
|
||||
}
|
||||
|
||||
if (array_key_exists('show', $params)) {
|
||||
$this->showAllTables($tables);
|
||||
|
||||
return EXIT_ERROR;
|
||||
}
|
||||
|
||||
$tableName = $params[0] ?? null;
|
||||
$limitRows = (int) ($params['limit-rows'] ?? 10);
|
||||
$limitFieldValue = (int) ($params['limit-field-value'] ?? 15);
|
||||
|
||||
while (! in_array($tableName, $tables, true)) {
|
||||
$tableNameNo = CLI::promptByKey(
|
||||
['Here is the list of your database tables:', 'Which table do you want to see?'],
|
||||
$tables,
|
||||
'required'
|
||||
);
|
||||
CLI::newLine();
|
||||
|
||||
$tableName = $tables[$tableNameNo] ?? null;
|
||||
}
|
||||
|
||||
if (array_key_exists('metadata', $params)) {
|
||||
$this->showFieldMetaData($tableName);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
$this->showDataOfTable($tableName, $limitRows, $limitFieldValue);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
private function showDBConfig(): void
|
||||
{
|
||||
$data = [[
|
||||
'hostname' => $this->db->hostname,
|
||||
'database' => $this->db->getDatabase(),
|
||||
'username' => $this->db->username,
|
||||
'DBDriver' => $this->db->getPlatform(),
|
||||
'DBPrefix' => $this->DBPrefix,
|
||||
'port' => $this->db->port,
|
||||
]];
|
||||
CLI::table(
|
||||
$data,
|
||||
['hostname', 'database', 'username', 'DBDriver', 'DBPrefix', 'port']
|
||||
);
|
||||
}
|
||||
|
||||
private function removeDBPrefix(): void
|
||||
{
|
||||
$this->db->setPrefix('');
|
||||
}
|
||||
|
||||
private function restoreDBPrefix(): void
|
||||
{
|
||||
$this->db->setPrefix($this->DBPrefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show Data of Table
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function showDataOfTable(string $tableName, int $limitRows, int $limitFieldValue)
|
||||
{
|
||||
CLI::write("Data of Table \"{$tableName}\":", 'black', 'yellow');
|
||||
CLI::newLine();
|
||||
|
||||
$this->removeDBPrefix();
|
||||
$thead = $this->db->getFieldNames($tableName);
|
||||
$this->restoreDBPrefix();
|
||||
|
||||
// If there is a field named `id`, sort by it.
|
||||
$sortField = null;
|
||||
if (in_array('id', $thead, true)) {
|
||||
$sortField = 'id';
|
||||
}
|
||||
|
||||
$this->tbody = $this->makeTableRows($tableName, $limitRows, $limitFieldValue, $sortField);
|
||||
CLI::table($this->tbody, $thead);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show All Tables
|
||||
*
|
||||
* @param list<string> $tables
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function showAllTables(array $tables)
|
||||
{
|
||||
CLI::write('The following is a list of the names of all database tables:', 'black', 'yellow');
|
||||
CLI::newLine();
|
||||
|
||||
$thead = ['ID', 'Table Name', 'Num of Rows', 'Num of Fields'];
|
||||
$this->tbody = $this->makeTbodyForShowAllTables($tables);
|
||||
|
||||
CLI::table($this->tbody, $thead);
|
||||
CLI::newLine();
|
||||
}
|
||||
|
||||
/**
|
||||
* Make body for table
|
||||
*
|
||||
* @param list<string> $tables
|
||||
*
|
||||
* @return list<list<int|string>>
|
||||
*/
|
||||
private function makeTbodyForShowAllTables(array $tables): array
|
||||
{
|
||||
$this->removeDBPrefix();
|
||||
|
||||
foreach ($tables as $id => $tableName) {
|
||||
$table = $this->db->protectIdentifiers($tableName);
|
||||
$db = $this->db->query("SELECT * FROM {$table}");
|
||||
|
||||
$this->tbody[] = [
|
||||
$id + 1,
|
||||
$tableName,
|
||||
$db->getNumRows(),
|
||||
$db->getFieldCount(),
|
||||
];
|
||||
}
|
||||
|
||||
$this->restoreDBPrefix();
|
||||
|
||||
if ($this->sortDesc) {
|
||||
krsort($this->tbody);
|
||||
}
|
||||
|
||||
return $this->tbody;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make table rows
|
||||
*
|
||||
* @return list<list<int|string>>
|
||||
*/
|
||||
private function makeTableRows(
|
||||
string $tableName,
|
||||
int $limitRows,
|
||||
int $limitFieldValue,
|
||||
?string $sortField = null
|
||||
): array {
|
||||
$this->tbody = [];
|
||||
|
||||
$this->removeDBPrefix();
|
||||
$builder = $this->db->table($tableName);
|
||||
$builder->limit($limitRows);
|
||||
if ($sortField !== null) {
|
||||
$builder->orderBy($sortField, $this->sortDesc ? 'DESC' : 'ASC');
|
||||
}
|
||||
$rows = $builder->get()->getResultArray();
|
||||
$this->restoreDBPrefix();
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$row = array_map(
|
||||
static fn ($item): string => mb_strlen((string) $item) > $limitFieldValue
|
||||
? mb_substr((string) $item, 0, $limitFieldValue) . '...'
|
||||
: (string) $item,
|
||||
$row
|
||||
);
|
||||
$this->tbody[] = $row;
|
||||
}
|
||||
|
||||
if ($sortField === null && $this->sortDesc) {
|
||||
krsort($this->tbody);
|
||||
}
|
||||
|
||||
return $this->tbody;
|
||||
}
|
||||
|
||||
private function showFieldMetaData(string $tableName): void
|
||||
{
|
||||
CLI::write("List of Metadata Information in Table \"{$tableName}\":", 'black', 'yellow');
|
||||
CLI::newLine();
|
||||
|
||||
$thead = ['Field Name', 'Type', 'Max Length', 'Nullable', 'Default', 'Primary Key'];
|
||||
|
||||
$this->removeDBPrefix();
|
||||
$fields = $this->db->getFieldData($tableName);
|
||||
$this->restoreDBPrefix();
|
||||
|
||||
foreach ($fields as $row) {
|
||||
$this->tbody[] = [
|
||||
$row->name,
|
||||
$row->type,
|
||||
$row->max_length,
|
||||
isset($row->nullable) ? $this->setYesOrNo($row->nullable) : 'n/a',
|
||||
$row->default,
|
||||
isset($row->primary_key) ? $this->setYesOrNo($row->primary_key) : 'n/a',
|
||||
];
|
||||
}
|
||||
|
||||
if ($this->sortDesc) {
|
||||
krsort($this->tbody);
|
||||
}
|
||||
|
||||
CLI::table($this->tbody, $thead);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool|int|string|null $fieldValue
|
||||
*/
|
||||
private function setYesOrNo($fieldValue): string
|
||||
{
|
||||
if ((bool) $fieldValue) {
|
||||
return CLI::color('Yes', 'green');
|
||||
}
|
||||
|
||||
return CLI::color('No', 'red');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* 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\Commands\Encryption;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
use CodeIgniter\Config\DotEnv;
|
||||
use CodeIgniter\Encryption\Encryption;
|
||||
|
||||
/**
|
||||
* Generates a new encryption key.
|
||||
*/
|
||||
class GenerateKey extends BaseCommand
|
||||
{
|
||||
/**
|
||||
* The Command's group.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'Encryption';
|
||||
|
||||
/**
|
||||
* The Command's name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'key:generate';
|
||||
|
||||
/**
|
||||
* The Command's usage.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'key:generate [options]';
|
||||
|
||||
/**
|
||||
* The Command's short description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Generates a new encryption key and writes it in an `.env` file.';
|
||||
|
||||
/**
|
||||
* The command's options
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $options = [
|
||||
'--force' => 'Force overwrite existing key in `.env` file.',
|
||||
'--length' => 'The length of the random string that should be returned in bytes. Defaults to 32.',
|
||||
'--prefix' => 'Prefix to prepend to encoded key (either hex2bin or base64). Defaults to hex2bin.',
|
||||
'--show' => 'Shows the generated key in the terminal instead of storing in the `.env` file.',
|
||||
];
|
||||
|
||||
/**
|
||||
* Actually execute the command.
|
||||
*/
|
||||
public function run(array $params)
|
||||
{
|
||||
$prefix = $params['prefix'] ?? CLI::getOption('prefix');
|
||||
|
||||
if (in_array($prefix, [null, true], true)) {
|
||||
$prefix = 'hex2bin';
|
||||
} elseif (! in_array($prefix, ['hex2bin', 'base64'], true)) {
|
||||
$prefix = CLI::prompt('Please provide a valid prefix to use.', ['hex2bin', 'base64'], 'required'); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$length = $params['length'] ?? CLI::getOption('length');
|
||||
|
||||
if (in_array($length, [null, true], true)) {
|
||||
$length = 32;
|
||||
}
|
||||
|
||||
$encodedKey = $this->generateRandomKey($prefix, $length);
|
||||
|
||||
if (array_key_exists('show', $params) || (bool) CLI::getOption('show')) {
|
||||
CLI::write($encodedKey, 'yellow');
|
||||
CLI::newLine();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (! $this->setNewEncryptionKey($encodedKey, $params)) {
|
||||
CLI::write('Error in setting new encryption key to .env file.', 'light_gray', 'red');
|
||||
CLI::newLine();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// force DotEnv to reload the new env vars
|
||||
putenv('encryption.key');
|
||||
unset($_ENV['encryption.key'], $_SERVER['encryption.key']);
|
||||
$dotenv = new DotEnv(ROOTPATH);
|
||||
$dotenv->load();
|
||||
|
||||
CLI::write('Application\'s new encryption key was successfully set.', 'green');
|
||||
CLI::newLine();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a key and encodes it.
|
||||
*/
|
||||
protected function generateRandomKey(string $prefix, int $length): string
|
||||
{
|
||||
$key = Encryption::createKey($length);
|
||||
|
||||
if ($prefix === 'hex2bin') {
|
||||
return 'hex2bin:' . bin2hex($key);
|
||||
}
|
||||
|
||||
return 'base64:' . base64_encode($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the new encryption key in your .env file.
|
||||
*
|
||||
* @param array<int|string, string|null> $params
|
||||
*/
|
||||
protected function setNewEncryptionKey(string $key, array $params): bool
|
||||
{
|
||||
$currentKey = env('encryption.key', '');
|
||||
|
||||
if ($currentKey !== '' && ! $this->confirmOverwrite($params)) {
|
||||
// Not yet testable since it requires keyboard input
|
||||
return false; // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
return $this->writeNewEncryptionKeyToFile($currentKey, $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether to overwrite existing encryption key.
|
||||
*
|
||||
* @param array<int|string, string|null> $params
|
||||
*/
|
||||
protected function confirmOverwrite(array $params): bool
|
||||
{
|
||||
return (array_key_exists('force', $params) || CLI::getOption('force')) || CLI::prompt('Overwrite existing key?', ['n', 'y']) === 'y';
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the new encryption key to .env file.
|
||||
*/
|
||||
protected function writeNewEncryptionKeyToFile(string $oldKey, string $newKey): bool
|
||||
{
|
||||
$baseEnv = ROOTPATH . 'env';
|
||||
$envFile = ROOTPATH . '.env';
|
||||
|
||||
if (! is_file($envFile)) {
|
||||
if (! is_file($baseEnv)) {
|
||||
CLI::write('Both default shipped `env` file and custom `.env` are missing.', 'yellow');
|
||||
CLI::write('Here\'s your new key instead: ' . CLI::color($newKey, 'yellow'));
|
||||
CLI::newLine();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
copy($baseEnv, $envFile);
|
||||
}
|
||||
|
||||
$oldFileContents = (string) file_get_contents($envFile);
|
||||
$replacementKey = "\nencryption.key = {$newKey}";
|
||||
|
||||
if (! str_contains($oldFileContents, 'encryption.key')) {
|
||||
return file_put_contents($envFile, $replacementKey, FILE_APPEND) !== false;
|
||||
}
|
||||
|
||||
$newFileContents = preg_replace($this->keyPattern($oldKey), $replacementKey, $oldFileContents);
|
||||
|
||||
if ($newFileContents === $oldFileContents) {
|
||||
$newFileContents = preg_replace(
|
||||
'/^[#\s]*encryption.key[=\s]*(?:hex2bin\:[a-f0-9]{64}|base64\:(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)?)$/m',
|
||||
$replacementKey,
|
||||
$oldFileContents
|
||||
);
|
||||
}
|
||||
|
||||
return file_put_contents($envFile, $newFileContents) !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the regex of the current encryption key.
|
||||
*/
|
||||
protected function keyPattern(string $oldKey): string
|
||||
{
|
||||
$escaped = preg_quote($oldKey, '/');
|
||||
|
||||
if ($escaped !== '') {
|
||||
$escaped = "[{$escaped}]*";
|
||||
}
|
||||
|
||||
return "/^[#\\s]*encryption.key[=\\s]*{$escaped}$/m";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* 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\Commands\Generators;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\GeneratorTrait;
|
||||
use Config\Generators;
|
||||
|
||||
/**
|
||||
* Generates a skeleton Cell and its view.
|
||||
*/
|
||||
class CellGenerator extends BaseCommand
|
||||
{
|
||||
use GeneratorTrait;
|
||||
|
||||
/**
|
||||
* The Command's Group
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'Generators';
|
||||
|
||||
/**
|
||||
* The Command's Name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'make:cell';
|
||||
|
||||
/**
|
||||
* The Command's Description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Generates a new Controlled Cell file and its view.';
|
||||
|
||||
/**
|
||||
* The Command's Usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'make:cell <name> [options]';
|
||||
|
||||
/**
|
||||
* The Command's Arguments
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $arguments = [
|
||||
'name' => 'The Controlled Cell class name.',
|
||||
];
|
||||
|
||||
/**
|
||||
* The Command's Options
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $options = [
|
||||
'--namespace' => 'Set root namespace. Default: "APP_NAMESPACE".',
|
||||
'--force' => 'Force overwrite existing file.',
|
||||
];
|
||||
|
||||
/**
|
||||
* Actually execute a command.
|
||||
*/
|
||||
public function run(array $params)
|
||||
{
|
||||
$this->component = 'Cell';
|
||||
$this->directory = 'Cells';
|
||||
|
||||
$params = array_merge($params, ['suffix' => null]);
|
||||
|
||||
$this->templatePath = config(Generators::class)->views[$this->name]['class'];
|
||||
$this->template = 'cell.tpl.php';
|
||||
$this->classNameLang = 'CLI.generator.className.cell';
|
||||
|
||||
$this->generateClass($params);
|
||||
|
||||
$this->templatePath = config(Generators::class)->views[$this->name]['view'];
|
||||
$this->template = 'cell_view.tpl.php';
|
||||
$this->classNameLang = 'CLI.generator.viewName.cell';
|
||||
|
||||
$className = $this->qualifyClassName();
|
||||
$viewName = decamelize(class_basename($className));
|
||||
$viewName = preg_replace(
|
||||
'/([a-z][a-z0-9_\/\\\\]+)(_cell)$/i',
|
||||
'$1',
|
||||
$viewName
|
||||
) ?? $viewName;
|
||||
$namespace = substr($className, 0, strrpos($className, '\\') + 1);
|
||||
|
||||
$this->generateView($namespace . $viewName, $params);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* 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\Commands\Generators;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
use CodeIgniter\CLI\GeneratorTrait;
|
||||
|
||||
/**
|
||||
* Generates a skeleton command file.
|
||||
*/
|
||||
class CommandGenerator extends BaseCommand
|
||||
{
|
||||
use GeneratorTrait;
|
||||
|
||||
/**
|
||||
* The Command's Group
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'Generators';
|
||||
|
||||
/**
|
||||
* The Command's Name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'make:command';
|
||||
|
||||
/**
|
||||
* The Command's Description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Generates a new spark command.';
|
||||
|
||||
/**
|
||||
* The Command's Usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'make:command <name> [options]';
|
||||
|
||||
/**
|
||||
* The Command's Arguments
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $arguments = [
|
||||
'name' => 'The command class name.',
|
||||
];
|
||||
|
||||
/**
|
||||
* The Command's Options
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $options = [
|
||||
'--command' => 'The command name. Default: "command:name"',
|
||||
'--type' => 'The command type. Options [basic, generator]. Default: "basic".',
|
||||
'--group' => 'The command group. Default: [basic -> "App", generator -> "Generators"].',
|
||||
'--namespace' => 'Set root namespace. Default: "APP_NAMESPACE".',
|
||||
'--suffix' => 'Append the component title to the class name (e.g. User => UserCommand).',
|
||||
'--force' => 'Force overwrite existing file.',
|
||||
];
|
||||
|
||||
/**
|
||||
* Actually execute a command.
|
||||
*/
|
||||
public function run(array $params)
|
||||
{
|
||||
$this->component = 'Command';
|
||||
$this->directory = 'Commands';
|
||||
$this->template = 'command.tpl.php';
|
||||
|
||||
$this->classNameLang = 'CLI.generator.className.command';
|
||||
$this->generateClass($params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare options and do the necessary replacements.
|
||||
*/
|
||||
protected function prepare(string $class): string
|
||||
{
|
||||
$command = $this->getOption('command');
|
||||
$group = $this->getOption('group');
|
||||
$type = $this->getOption('type');
|
||||
|
||||
$command = is_string($command) ? $command : 'command:name';
|
||||
$type = is_string($type) ? $type : 'basic';
|
||||
|
||||
if (! in_array($type, ['basic', 'generator'], true)) {
|
||||
// @codeCoverageIgnoreStart
|
||||
$type = CLI::prompt(lang('CLI.generator.commandType'), ['basic', 'generator'], 'required');
|
||||
CLI::newLine();
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
if (! is_string($group)) {
|
||||
$group = $type === 'generator' ? 'Generators' : 'App';
|
||||
}
|
||||
|
||||
return $this->parseTemplate(
|
||||
$class,
|
||||
['{group}', '{command}'],
|
||||
[$group, $command],
|
||||
['type' => $type]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* 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\Commands\Generators;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\GeneratorTrait;
|
||||
|
||||
/**
|
||||
* Generates a skeleton config file.
|
||||
*/
|
||||
class ConfigGenerator extends BaseCommand
|
||||
{
|
||||
use GeneratorTrait;
|
||||
|
||||
/**
|
||||
* The Command's Group
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'Generators';
|
||||
|
||||
/**
|
||||
* The Command's Name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'make:config';
|
||||
|
||||
/**
|
||||
* The Command's Description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Generates a new config file.';
|
||||
|
||||
/**
|
||||
* The Command's Usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'make:config <name> [options]';
|
||||
|
||||
/**
|
||||
* The Command's Arguments
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $arguments = [
|
||||
'name' => 'The config class name.',
|
||||
];
|
||||
|
||||
/**
|
||||
* The Command's Options
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $options = [
|
||||
'--namespace' => 'Set root namespace. Default: "APP_NAMESPACE".',
|
||||
'--suffix' => 'Append the component title to the class name (e.g. User => UserConfig).',
|
||||
'--force' => 'Force overwrite existing file.',
|
||||
];
|
||||
|
||||
/**
|
||||
* Actually execute a command.
|
||||
*/
|
||||
public function run(array $params)
|
||||
{
|
||||
$this->component = 'Config';
|
||||
$this->directory = 'Config';
|
||||
$this->template = 'config.tpl.php';
|
||||
|
||||
$this->classNameLang = 'CLI.generator.className.config';
|
||||
$this->generateClass($params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare options and do the necessary replacements.
|
||||
*/
|
||||
protected function prepare(string $class): string
|
||||
{
|
||||
$namespace = $this->getOption('namespace') ?? APP_NAMESPACE;
|
||||
|
||||
if ($namespace === APP_NAMESPACE) {
|
||||
$class = substr($class, strlen($namespace . '\\'));
|
||||
}
|
||||
|
||||
return $this->parseTemplate($class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* 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\Commands\Generators;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
use CodeIgniter\CLI\GeneratorTrait;
|
||||
use CodeIgniter\Controller;
|
||||
use CodeIgniter\RESTful\ResourceController;
|
||||
use CodeIgniter\RESTful\ResourcePresenter;
|
||||
|
||||
/**
|
||||
* Generates a skeleton controller file.
|
||||
*/
|
||||
class ControllerGenerator extends BaseCommand
|
||||
{
|
||||
use GeneratorTrait;
|
||||
|
||||
/**
|
||||
* The Command's Group
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'Generators';
|
||||
|
||||
/**
|
||||
* The Command's Name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'make:controller';
|
||||
|
||||
/**
|
||||
* The Command's Description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Generates a new controller file.';
|
||||
|
||||
/**
|
||||
* The Command's Usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'make:controller <name> [options]';
|
||||
|
||||
/**
|
||||
* The Command's Arguments
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $arguments = [
|
||||
'name' => 'The controller class name.',
|
||||
];
|
||||
|
||||
/**
|
||||
* The Command's Options
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $options = [
|
||||
'--bare' => 'Extends from CodeIgniter\Controller instead of BaseController.',
|
||||
'--restful' => 'Extends from a RESTful resource, Options: [controller, presenter]. Default: "controller".',
|
||||
'--namespace' => 'Set root namespace. Default: "APP_NAMESPACE".',
|
||||
'--suffix' => 'Append the component title to the class name (e.g. User => UserController).',
|
||||
'--force' => 'Force overwrite existing file.',
|
||||
];
|
||||
|
||||
/**
|
||||
* Actually execute a command.
|
||||
*/
|
||||
public function run(array $params)
|
||||
{
|
||||
$this->component = 'Controller';
|
||||
$this->directory = 'Controllers';
|
||||
$this->template = 'controller.tpl.php';
|
||||
|
||||
$this->classNameLang = 'CLI.generator.className.controller';
|
||||
$this->generateClass($params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare options and do the necessary replacements.
|
||||
*/
|
||||
protected function prepare(string $class): string
|
||||
{
|
||||
$bare = $this->getOption('bare');
|
||||
$rest = $this->getOption('restful');
|
||||
|
||||
$useStatement = trim(APP_NAMESPACE, '\\') . '\Controllers\BaseController';
|
||||
$extends = 'BaseController';
|
||||
|
||||
// Gets the appropriate parent class to extend.
|
||||
if ($bare || $rest) {
|
||||
if ($bare) {
|
||||
$useStatement = Controller::class;
|
||||
$extends = 'Controller';
|
||||
} elseif ($rest) {
|
||||
$rest = is_string($rest) ? $rest : 'controller';
|
||||
|
||||
if (! in_array($rest, ['controller', 'presenter'], true)) {
|
||||
// @codeCoverageIgnoreStart
|
||||
$rest = CLI::prompt(lang('CLI.generator.parentClass'), ['controller', 'presenter'], 'required');
|
||||
CLI::newLine();
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
if ($rest === 'controller') {
|
||||
$useStatement = ResourceController::class;
|
||||
$extends = 'ResourceController';
|
||||
} elseif ($rest === 'presenter') {
|
||||
$useStatement = ResourcePresenter::class;
|
||||
$extends = 'ResourcePresenter';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->parseTemplate(
|
||||
$class,
|
||||
['{useStatement}', '{extends}'],
|
||||
[$useStatement, $extends],
|
||||
['type' => $rest]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* 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\Commands\Generators;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\GeneratorTrait;
|
||||
|
||||
/**
|
||||
* Generates a skeleton Entity file.
|
||||
*/
|
||||
class EntityGenerator extends BaseCommand
|
||||
{
|
||||
use GeneratorTrait;
|
||||
|
||||
/**
|
||||
* The Command's Group
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'Generators';
|
||||
|
||||
/**
|
||||
* The Command's Name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'make:entity';
|
||||
|
||||
/**
|
||||
* The Command's Description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Generates a new entity file.';
|
||||
|
||||
/**
|
||||
* The Command's Usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'make:entity <name> [options]';
|
||||
|
||||
/**
|
||||
* The Command's Arguments
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $arguments = [
|
||||
'name' => 'The entity class name.',
|
||||
];
|
||||
|
||||
/**
|
||||
* The Command's Options
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $options = [
|
||||
'--namespace' => 'Set root namespace. Default: "APP_NAMESPACE".',
|
||||
'--suffix' => 'Append the component title to the class name (e.g. User => UserEntity).',
|
||||
'--force' => 'Force overwrite existing file.',
|
||||
];
|
||||
|
||||
/**
|
||||
* Actually execute a command.
|
||||
*/
|
||||
public function run(array $params)
|
||||
{
|
||||
$this->component = 'Entity';
|
||||
$this->directory = 'Entities';
|
||||
$this->template = 'entity.tpl.php';
|
||||
|
||||
$this->classNameLang = 'CLI.generator.className.entity';
|
||||
$this->generateClass($params);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* 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\Commands\Generators;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\GeneratorTrait;
|
||||
|
||||
/**
|
||||
* Generates a skeleton Filter file.
|
||||
*/
|
||||
class FilterGenerator extends BaseCommand
|
||||
{
|
||||
use GeneratorTrait;
|
||||
|
||||
/**
|
||||
* The Command's Group
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'Generators';
|
||||
|
||||
/**
|
||||
* The Command's Name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'make:filter';
|
||||
|
||||
/**
|
||||
* The Command's Description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Generates a new filter file.';
|
||||
|
||||
/**
|
||||
* The Command's Usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'make:filter <name> [options]';
|
||||
|
||||
/**
|
||||
* The Command's Arguments
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $arguments = [
|
||||
'name' => 'The filter class name.',
|
||||
];
|
||||
|
||||
/**
|
||||
* The Command's Options
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $options = [
|
||||
'--namespace' => 'Set root namespace. Default: "APP_NAMESPACE".',
|
||||
'--suffix' => 'Append the component title to the class name (e.g. User => UserFilter).',
|
||||
'--force' => 'Force overwrite existing file.',
|
||||
];
|
||||
|
||||
/**
|
||||
* Actually execute a command.
|
||||
*/
|
||||
public function run(array $params)
|
||||
{
|
||||
$this->component = 'Filter';
|
||||
$this->directory = 'Filters';
|
||||
$this->template = 'filter.tpl.php';
|
||||
|
||||
$this->classNameLang = 'CLI.generator.className.filter';
|
||||
$this->generateClass($params);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* 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\Commands\Generators;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
use CodeIgniter\CLI\GeneratorTrait;
|
||||
use Config\Database;
|
||||
use Config\Migrations;
|
||||
use Config\Session as SessionConfig;
|
||||
|
||||
/**
|
||||
* Generates a skeleton migration file.
|
||||
*/
|
||||
class MigrationGenerator extends BaseCommand
|
||||
{
|
||||
use GeneratorTrait;
|
||||
|
||||
/**
|
||||
* The Command's Group
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'Generators';
|
||||
|
||||
/**
|
||||
* The Command's Name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'make:migration';
|
||||
|
||||
/**
|
||||
* The Command's Description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Generates a new migration file.';
|
||||
|
||||
/**
|
||||
* The Command's Usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'make:migration <name> [options]';
|
||||
|
||||
/**
|
||||
* The Command's Arguments
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $arguments = [
|
||||
'name' => 'The migration class name.',
|
||||
];
|
||||
|
||||
/**
|
||||
* The Command's Options
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $options = [
|
||||
'--session' => 'Generates the migration file for database sessions.',
|
||||
'--table' => 'Table name to use for database sessions. Default: "ci_sessions".',
|
||||
'--dbgroup' => 'Database group to use for database sessions. Default: "default".',
|
||||
'--namespace' => 'Set root namespace. Default: "APP_NAMESPACE".',
|
||||
'--suffix' => 'Append the component title to the class name (e.g. User => UserMigration).',
|
||||
];
|
||||
|
||||
/**
|
||||
* Actually execute a command.
|
||||
*/
|
||||
public function run(array $params)
|
||||
{
|
||||
$this->component = 'Migration';
|
||||
$this->directory = 'Database\Migrations';
|
||||
$this->template = 'migration.tpl.php';
|
||||
|
||||
if (array_key_exists('session', $params) || CLI::getOption('session')) {
|
||||
$table = $params['table'] ?? CLI::getOption('table') ?? 'ci_sessions';
|
||||
$params[0] = "_create_{$table}_table";
|
||||
}
|
||||
|
||||
$this->classNameLang = 'CLI.generator.className.migration';
|
||||
$this->generateClass($params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare options and do the necessary replacements.
|
||||
*/
|
||||
protected function prepare(string $class): string
|
||||
{
|
||||
$data = [];
|
||||
$data['session'] = false;
|
||||
|
||||
if ($this->getOption('session')) {
|
||||
$table = $this->getOption('table');
|
||||
$DBGroup = $this->getOption('dbgroup');
|
||||
|
||||
$data['session'] = true;
|
||||
$data['table'] = is_string($table) ? $table : 'ci_sessions';
|
||||
$data['DBGroup'] = is_string($DBGroup) ? $DBGroup : 'default';
|
||||
$data['DBDriver'] = config(Database::class)->{$data['DBGroup']}['DBDriver'];
|
||||
|
||||
/** @var SessionConfig|null $session */
|
||||
$session = config(SessionConfig::class);
|
||||
|
||||
$data['matchIP'] = $session->matchIP;
|
||||
}
|
||||
|
||||
return $this->parseTemplate($class, [], [], $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change file basename before saving.
|
||||
*/
|
||||
protected function basename(string $filename): string
|
||||
{
|
||||
return gmdate(config(Migrations::class)->timestampFormat) . basename($filename);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* 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\Commands\Generators;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
use CodeIgniter\CLI\GeneratorTrait;
|
||||
|
||||
/**
|
||||
* Generates a skeleton Model file.
|
||||
*/
|
||||
class ModelGenerator extends BaseCommand
|
||||
{
|
||||
use GeneratorTrait;
|
||||
|
||||
/**
|
||||
* The Command's Group
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'Generators';
|
||||
|
||||
/**
|
||||
* The Command's Name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'make:model';
|
||||
|
||||
/**
|
||||
* The Command's Description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Generates a new model file.';
|
||||
|
||||
/**
|
||||
* The Command's Usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'make:model <name> [options]';
|
||||
|
||||
/**
|
||||
* The Command's Arguments
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $arguments = [
|
||||
'name' => 'The model class name.',
|
||||
];
|
||||
|
||||
/**
|
||||
* The Command's Options
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $options = [
|
||||
'--table' => 'Supply a table name. Default: "the lowercased plural of the class name".',
|
||||
'--dbgroup' => 'Database group to use. Default: "default".',
|
||||
'--return' => 'Return type, Options: [array, object, entity]. Default: "array".',
|
||||
'--namespace' => 'Set root namespace. Default: "APP_NAMESPACE".',
|
||||
'--suffix' => 'Append the component title to the class name (e.g. User => UserModel).',
|
||||
'--force' => 'Force overwrite existing file.',
|
||||
];
|
||||
|
||||
/**
|
||||
* Actually execute a command.
|
||||
*/
|
||||
public function run(array $params)
|
||||
{
|
||||
$this->component = 'Model';
|
||||
$this->directory = 'Models';
|
||||
$this->template = 'model.tpl.php';
|
||||
|
||||
$this->classNameLang = 'CLI.generator.className.model';
|
||||
$this->generateClass($params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare options and do the necessary replacements.
|
||||
*/
|
||||
protected function prepare(string $class): string
|
||||
{
|
||||
$table = $this->getOption('table');
|
||||
$dbGroup = $this->getOption('dbgroup');
|
||||
$return = $this->getOption('return');
|
||||
|
||||
$baseClass = class_basename($class);
|
||||
|
||||
if (preg_match('/^(\S+)Model$/i', $baseClass, $match) === 1) {
|
||||
$baseClass = $match[1];
|
||||
}
|
||||
|
||||
$table = is_string($table) ? $table : plural(strtolower($baseClass));
|
||||
$return = is_string($return) ? $return : 'array';
|
||||
|
||||
if (! in_array($return, ['array', 'object', 'entity'], true)) {
|
||||
// @codeCoverageIgnoreStart
|
||||
$return = CLI::prompt(lang('CLI.generator.returnType'), ['array', 'object', 'entity'], 'required');
|
||||
CLI::newLine();
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
if ($return === 'entity') {
|
||||
$return = str_replace('Models', 'Entities', $class);
|
||||
|
||||
if (preg_match('/^(\S+)Model$/i', $return, $match) === 1) {
|
||||
$return = $match[1];
|
||||
|
||||
if ($this->getOption('suffix')) {
|
||||
$return .= 'Entity';
|
||||
}
|
||||
}
|
||||
|
||||
$return = '\\' . trim($return, '\\') . '::class';
|
||||
$this->call('make:entity', array_merge([$baseClass], $this->params));
|
||||
} else {
|
||||
$return = "'{$return}'";
|
||||
}
|
||||
|
||||
return $this->parseTemplate($class, ['{dbGroup}', '{table}', '{return}'], [$dbGroup, $table, $return], compact('dbGroup'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* 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\Commands\Generators;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
use CodeIgniter\CLI\GeneratorTrait;
|
||||
|
||||
/**
|
||||
* Generates a complete set of scaffold files.
|
||||
*/
|
||||
class ScaffoldGenerator extends BaseCommand
|
||||
{
|
||||
use GeneratorTrait;
|
||||
|
||||
/**
|
||||
* The Command's Group
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'Generators';
|
||||
|
||||
/**
|
||||
* The Command's Name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'make:scaffold';
|
||||
|
||||
/**
|
||||
* The Command's Description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Generates a complete set of scaffold files.';
|
||||
|
||||
/**
|
||||
* The Command's Usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'make:scaffold <name> [options]';
|
||||
|
||||
/**
|
||||
* The Command's Arguments
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $arguments = [
|
||||
'name' => 'The class name',
|
||||
];
|
||||
|
||||
/**
|
||||
* The Command's Options
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $options = [
|
||||
'--bare' => 'Add the "--bare" option to controller component.',
|
||||
'--restful' => 'Add the "--restful" option to controller component.',
|
||||
'--table' => 'Add the "--table" option to the model component.',
|
||||
'--dbgroup' => 'Add the "--dbgroup" option to model component.',
|
||||
'--return' => 'Add the "--return" option to the model component.',
|
||||
'--namespace' => 'Set root namespace. Default: "APP_NAMESPACE".',
|
||||
'--suffix' => 'Append the component title to the class name.',
|
||||
'--force' => 'Force overwrite existing file.',
|
||||
];
|
||||
|
||||
/**
|
||||
* Actually execute a command.
|
||||
*/
|
||||
public function run(array $params)
|
||||
{
|
||||
$this->params = $params;
|
||||
|
||||
$options = [];
|
||||
|
||||
if ($this->getOption('namespace')) {
|
||||
$options['namespace'] = $this->getOption('namespace');
|
||||
}
|
||||
|
||||
if ($this->getOption('suffix')) {
|
||||
$options['suffix'] = null;
|
||||
}
|
||||
|
||||
if ($this->getOption('force')) {
|
||||
$options['force'] = null;
|
||||
}
|
||||
|
||||
$controllerOpts = [];
|
||||
|
||||
if ($this->getOption('bare')) {
|
||||
$controllerOpts['bare'] = null;
|
||||
} elseif ($this->getOption('restful')) {
|
||||
$controllerOpts['restful'] = $this->getOption('restful');
|
||||
}
|
||||
|
||||
$modelOpts = [
|
||||
'table' => $this->getOption('table'),
|
||||
'dbgroup' => $this->getOption('dbgroup'),
|
||||
'return' => $this->getOption('return'),
|
||||
];
|
||||
|
||||
$class = $params[0] ?? CLI::getSegment(2);
|
||||
|
||||
// Call those commands!
|
||||
$this->call('make:controller', array_merge([$class], $controllerOpts, $options));
|
||||
$this->call('make:model', array_merge([$class], $modelOpts, $options));
|
||||
$this->call('make:migration', array_merge([$class], $options));
|
||||
$this->call('make:seeder', array_merge([$class], $options));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* 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\Commands\Generators;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\GeneratorTrait;
|
||||
|
||||
/**
|
||||
* Generates a skeleton seeder file.
|
||||
*/
|
||||
class SeederGenerator extends BaseCommand
|
||||
{
|
||||
use GeneratorTrait;
|
||||
|
||||
/**
|
||||
* The Command's Group
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'Generators';
|
||||
|
||||
/**
|
||||
* The Command's Name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'make:seeder';
|
||||
|
||||
/**
|
||||
* The Command's Description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Generates a new seeder file.';
|
||||
|
||||
/**
|
||||
* The Command's Usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'make:seeder <name> [options]';
|
||||
|
||||
/**
|
||||
* The Command's Arguments
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $arguments = [
|
||||
'name' => 'The seeder class name.',
|
||||
];
|
||||
|
||||
/**
|
||||
* The Command's Options
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $options = [
|
||||
'--namespace' => 'Set root namespace. Default: "APP_NAMESPACE".',
|
||||
'--suffix' => 'Append the component title to the class name (e.g. User => UserSeeder).',
|
||||
'--force' => 'Force overwrite existing file.',
|
||||
];
|
||||
|
||||
/**
|
||||
* Actually execute a command.
|
||||
*/
|
||||
public function run(array $params)
|
||||
{
|
||||
$this->component = 'Seeder';
|
||||
$this->directory = 'Database\Seeds';
|
||||
$this->template = 'seeder.tpl.php';
|
||||
|
||||
$this->classNameLang = 'CLI.generator.className.seeder';
|
||||
$this->generateClass($params);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* 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\Commands\Generators;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
use CodeIgniter\CLI\GeneratorTrait;
|
||||
|
||||
/**
|
||||
* Generates a skeleton command file.
|
||||
*/
|
||||
class TestGenerator extends BaseCommand
|
||||
{
|
||||
use GeneratorTrait;
|
||||
|
||||
/**
|
||||
* The Command's Group
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'Generators';
|
||||
|
||||
/**
|
||||
* The Command's Name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'make:test';
|
||||
|
||||
/**
|
||||
* The Command's Description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Generates a new test file.';
|
||||
|
||||
/**
|
||||
* The Command's Usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'make:test <name> [options]';
|
||||
|
||||
/**
|
||||
* The Command's Arguments
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $arguments = [
|
||||
'name' => 'The test class name.',
|
||||
];
|
||||
|
||||
/**
|
||||
* The Command's Options
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $options = [
|
||||
'--namespace' => 'Set root namespace. Default: "Tests".',
|
||||
'--force' => 'Force overwrite existing file.',
|
||||
];
|
||||
|
||||
/**
|
||||
* Actually execute a command.
|
||||
*/
|
||||
public function run(array $params)
|
||||
{
|
||||
$this->component = 'Test';
|
||||
$this->template = 'test.tpl.php';
|
||||
|
||||
$this->classNameLang = 'CLI.generator.className.test';
|
||||
|
||||
$autoload = service('autoloader');
|
||||
$autoload->addNamespace('CodeIgniter', TESTPATH . 'system');
|
||||
$autoload->addNamespace('Tests', ROOTPATH . 'tests');
|
||||
|
||||
$this->generateClass($params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the namespace from input or the default namespace.
|
||||
*/
|
||||
protected function getNamespace(): string
|
||||
{
|
||||
if ($this->namespace !== null) {
|
||||
return $this->namespace;
|
||||
}
|
||||
|
||||
if ($this->getOption('namespace') !== null) {
|
||||
return trim(
|
||||
str_replace(
|
||||
'/',
|
||||
'\\',
|
||||
$this->getOption('namespace')
|
||||
),
|
||||
'\\'
|
||||
);
|
||||
}
|
||||
|
||||
$class = $this->normalizeInputClassName();
|
||||
$classPaths = explode('\\', $class);
|
||||
|
||||
$namespaces = service('autoloader')->getNamespace();
|
||||
|
||||
while ($classPaths !== []) {
|
||||
array_pop($classPaths);
|
||||
$namespace = implode('\\', $classPaths);
|
||||
|
||||
foreach (array_keys($namespaces) as $prefix) {
|
||||
if ($prefix === $namespace) {
|
||||
// The input classname is FQCN, and use the namespace.
|
||||
return $namespace;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 'Tests';
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the test file path from the class name.
|
||||
*
|
||||
* @param string $class namespaced classname.
|
||||
*/
|
||||
protected function buildPath(string $class): string
|
||||
{
|
||||
$namespace = $this->getNamespace();
|
||||
|
||||
$base = $this->searchTestFilePath($namespace);
|
||||
|
||||
if ($base === null) {
|
||||
CLI::error(
|
||||
lang('CLI.namespaceNotDefined', [$namespace]),
|
||||
'light_gray',
|
||||
'red'
|
||||
);
|
||||
CLI::newLine();
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
$realpath = realpath($base);
|
||||
$base = ($realpath !== false) ? $realpath : $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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns test file path for the namespace.
|
||||
*/
|
||||
private function searchTestFilePath(string $namespace): ?string
|
||||
{
|
||||
$bases = service('autoloader')->getNamespace($namespace);
|
||||
|
||||
$base = null;
|
||||
|
||||
foreach ($bases as $candidate) {
|
||||
if (str_contains($candidate, '/tests/')) {
|
||||
$base = $candidate;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $base;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* 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\Commands\Generators;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\GeneratorTrait;
|
||||
|
||||
/**
|
||||
* Generates a skeleton Validation file.
|
||||
*/
|
||||
class ValidationGenerator extends BaseCommand
|
||||
{
|
||||
use GeneratorTrait;
|
||||
|
||||
/**
|
||||
* The Command's Group
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'Generators';
|
||||
|
||||
/**
|
||||
* The Command's Name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'make:validation';
|
||||
|
||||
/**
|
||||
* The Command's Description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Generates a new validation file.';
|
||||
|
||||
/**
|
||||
* The Command's Usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'make:validation <name> [options]';
|
||||
|
||||
/**
|
||||
* The Command's Arguments
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $arguments = [
|
||||
'name' => 'The validation class name.',
|
||||
];
|
||||
|
||||
/**
|
||||
* The Command's Options
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $options = [
|
||||
'--namespace' => 'Set root namespace. Default: "APP_NAMESPACE".',
|
||||
'--suffix' => 'Append the component title to the class name (e.g. User => UserValidation).',
|
||||
'--force' => 'Force overwrite existing file.',
|
||||
];
|
||||
|
||||
/**
|
||||
* Actually execute a command.
|
||||
*/
|
||||
public function run(array $params)
|
||||
{
|
||||
$this->component = 'Validation';
|
||||
$this->directory = 'Validation';
|
||||
$this->template = 'validation.tpl.php';
|
||||
|
||||
$this->classNameLang = 'CLI.generator.className.validation';
|
||||
$this->generateClass($params);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<@php
|
||||
|
||||
namespace {namespace};
|
||||
|
||||
use CodeIgniter\View\Cells\Cell;
|
||||
|
||||
class {class} extends Cell
|
||||
{
|
||||
//
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<div>
|
||||
<!-- Your HTML here -->
|
||||
</div>
|
||||
@@ -0,0 +1,76 @@
|
||||
<@php
|
||||
|
||||
namespace {namespace};
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
<?php if ($type === 'generator'): ?>
|
||||
use CodeIgniter\CLI\GeneratorTrait;
|
||||
<?php endif ?>
|
||||
|
||||
class {class} extends BaseCommand
|
||||
{
|
||||
<?php if ($type === 'generator'): ?>
|
||||
use GeneratorTrait;
|
||||
|
||||
<?php endif ?>
|
||||
/**
|
||||
* The Command's Group
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = '{group}';
|
||||
|
||||
/**
|
||||
* The Command's Name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = '{command}';
|
||||
|
||||
/**
|
||||
* The Command's Description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = '';
|
||||
|
||||
/**
|
||||
* The Command's Usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = '{command} [arguments] [options]';
|
||||
|
||||
/**
|
||||
* The Command's Arguments
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $arguments = [];
|
||||
|
||||
/**
|
||||
* The Command's Options
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $options = [];
|
||||
|
||||
/**
|
||||
* Actually execute a command.
|
||||
*
|
||||
* @param array $params
|
||||
*/
|
||||
public function run(array $params)
|
||||
{
|
||||
<?php if ($type === 'generator'): ?>
|
||||
$this->component = 'Command';
|
||||
$this->directory = 'Commands';
|
||||
$this->template = 'command.tpl.php';
|
||||
|
||||
$this->execute($params);
|
||||
<?php else: ?>
|
||||
//
|
||||
<?php endif ?>
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<@php
|
||||
|
||||
namespace {namespace};
|
||||
|
||||
use CodeIgniter\Config\BaseConfig;
|
||||
|
||||
class {class} extends BaseConfig
|
||||
{
|
||||
//
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
<@php
|
||||
|
||||
namespace {namespace};
|
||||
|
||||
use {useStatement};
|
||||
use CodeIgniter\HTTP\ResponseInterface;
|
||||
|
||||
class {class} extends {extends}
|
||||
{
|
||||
<?php if ($type === 'controller'): ?>
|
||||
/**
|
||||
* Return an array of resource objects, themselves in array format.
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the properties of a resource object.
|
||||
*
|
||||
* @param int|string|null $id
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function show($id = null)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new resource object, with default properties.
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function new()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new resource object, from "posted" parameters.
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the editable properties of a resource object.
|
||||
*
|
||||
* @param int|string|null $id
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function edit($id = null)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Add or update a model resource, from "posted" properties.
|
||||
*
|
||||
* @param int|string|null $id
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function update($id = null)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the designated resource object from the model.
|
||||
*
|
||||
* @param int|string|null $id
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function delete($id = null)
|
||||
{
|
||||
//
|
||||
}
|
||||
<?php elseif ($type === 'presenter'): ?>
|
||||
/**
|
||||
* Present a view of resource objects.
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Present a view to present a specific resource object.
|
||||
*
|
||||
* @param int|string|null $id
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function show($id = null)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Present a view to present a new single resource object.
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function new()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the creation/insertion of a new resource object.
|
||||
* This should be a POST.
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Present a view to edit the properties of a specific resource object.
|
||||
*
|
||||
* @param int|string|null $id
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function edit($id = null)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the updating, full or partial, of a specific resource object.
|
||||
* This should be a POST.
|
||||
*
|
||||
* @param int|string|null $id
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function update($id = null)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Present a view to confirm the deletion of a specific resource object.
|
||||
*
|
||||
* @param int|string|null $id
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function remove($id = null)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the deletion of a specific resource object.
|
||||
*
|
||||
* @param int|string|null $id
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function delete($id = null)
|
||||
{
|
||||
//
|
||||
}
|
||||
<?php else: ?>
|
||||
public function index()
|
||||
{
|
||||
//
|
||||
}
|
||||
<?php endif ?>
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<@php
|
||||
|
||||
namespace {namespace};
|
||||
|
||||
use CodeIgniter\Entity\Entity;
|
||||
|
||||
class {class} extends Entity
|
||||
{
|
||||
protected $datamap = [];
|
||||
protected $dates = ['created_at', 'updated_at', 'deleted_at'];
|
||||
protected $casts = [];
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<@php
|
||||
|
||||
namespace {namespace};
|
||||
|
||||
use CodeIgniter\Filters\FilterInterface;
|
||||
use CodeIgniter\HTTP\RequestInterface;
|
||||
use CodeIgniter\HTTP\ResponseInterface;
|
||||
|
||||
class {class} implements FilterInterface
|
||||
{
|
||||
/**
|
||||
* Do whatever processing this filter needs to do.
|
||||
* By default it should not return anything during
|
||||
* normal execution. However, when an abnormal state
|
||||
* is found, it should return an instance of
|
||||
* CodeIgniter\HTTP\Response. If it does, script
|
||||
* execution will end and that Response will be
|
||||
* sent back to the client, allowing for error pages,
|
||||
* redirects, etc.
|
||||
*
|
||||
* @param RequestInterface $request
|
||||
* @param array|null $arguments
|
||||
*
|
||||
* @return RequestInterface|ResponseInterface|string|void
|
||||
*/
|
||||
public function before(RequestInterface $request, $arguments = null)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows After filters to inspect and modify the response
|
||||
* object as needed. This method does not allow any way
|
||||
* to stop execution of other after filters, short of
|
||||
* throwing an Exception or Error.
|
||||
*
|
||||
* @param RequestInterface $request
|
||||
* @param ResponseInterface $response
|
||||
* @param array|null $arguments
|
||||
*
|
||||
* @return ResponseInterface|void
|
||||
*/
|
||||
public function after(RequestInterface $request, ResponseInterface $response, $arguments = null)
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<@php
|
||||
|
||||
namespace {namespace};
|
||||
|
||||
use CodeIgniter\Database\Migration;
|
||||
|
||||
class {class} extends Migration
|
||||
{
|
||||
<?php if ($session): ?>
|
||||
protected $DBGroup = '<?= $DBGroup ?>';
|
||||
|
||||
public function up()
|
||||
{
|
||||
$this->forge->addField([
|
||||
'id' => ['type' => 'VARCHAR', 'constraint' => 128, 'null' => false],
|
||||
<?php if ($DBDriver === 'MySQLi'): ?>
|
||||
'ip_address' => ['type' => 'VARCHAR', 'constraint' => 45, 'null' => false],
|
||||
'timestamp timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL',
|
||||
'data' => ['type' => 'BLOB', 'null' => false],
|
||||
<?php elseif ($DBDriver === 'Postgre'): ?>
|
||||
'ip_address inet NOT NULL',
|
||||
'timestamp timestamptz DEFAULT CURRENT_TIMESTAMP NOT NULL',
|
||||
"data bytea DEFAULT '' NOT NULL",
|
||||
<?php endif; ?>
|
||||
]);
|
||||
<?php if ($matchIP) : ?>
|
||||
$this->forge->addKey(['id', 'ip_address'], true);
|
||||
<?php else: ?>
|
||||
$this->forge->addKey('id', true);
|
||||
<?php endif ?>
|
||||
$this->forge->addKey('timestamp');
|
||||
$this->forge->createTable('<?= $table ?>', true);
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
$this->forge->dropTable('<?= $table ?>', true);
|
||||
}
|
||||
<?php else: ?>
|
||||
public function up()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
}
|
||||
<?php endif ?>
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<@php
|
||||
|
||||
namespace {namespace};
|
||||
|
||||
use CodeIgniter\Model;
|
||||
|
||||
class {class} extends Model
|
||||
{
|
||||
<?php if (is_string($dbGroup)): ?>
|
||||
protected $DBGroup = '{dbGroup}';
|
||||
<?php endif; ?>
|
||||
protected $table = '{table}';
|
||||
protected $primaryKey = 'id';
|
||||
protected $useAutoIncrement = true;
|
||||
protected $returnType = {return};
|
||||
protected $useSoftDeletes = false;
|
||||
protected $protectFields = true;
|
||||
protected $allowedFields = [];
|
||||
|
||||
protected bool $allowEmptyInserts = false;
|
||||
protected bool $updateOnlyChanged = true;
|
||||
|
||||
protected array $casts = [];
|
||||
protected array $castHandlers = [];
|
||||
|
||||
// Dates
|
||||
protected $useTimestamps = false;
|
||||
protected $dateFormat = 'datetime';
|
||||
protected $createdField = 'created_at';
|
||||
protected $updatedField = 'updated_at';
|
||||
protected $deletedField = 'deleted_at';
|
||||
|
||||
// Validation
|
||||
protected $validationRules = [];
|
||||
protected $validationMessages = [];
|
||||
protected $skipValidation = false;
|
||||
protected $cleanValidationRules = true;
|
||||
|
||||
// Callbacks
|
||||
protected $allowCallbacks = true;
|
||||
protected $beforeInsert = [];
|
||||
protected $afterInsert = [];
|
||||
protected $beforeUpdate = [];
|
||||
protected $afterUpdate = [];
|
||||
protected $beforeFind = [];
|
||||
protected $afterFind = [];
|
||||
protected $beforeDelete = [];
|
||||
protected $afterDelete = [];
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<@php
|
||||
|
||||
namespace {namespace};
|
||||
|
||||
use CodeIgniter\Database\Seeder;
|
||||
|
||||
class {class} extends Seeder
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<@php
|
||||
|
||||
namespace {namespace};
|
||||
|
||||
use CodeIgniter\Test\CIUnitTestCase;
|
||||
|
||||
class {class} extends CIUnitTestCase
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
public function testExample(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<@php
|
||||
|
||||
namespace {namespace};
|
||||
|
||||
class {class}
|
||||
{
|
||||
// public function custom_rule(): bool
|
||||
// {
|
||||
// return true;
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* 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\Commands;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
|
||||
/**
|
||||
* CI Help command for the spark script.
|
||||
*
|
||||
* Lists the basic usage information for the spark script,
|
||||
* and provides a way to list help for other commands.
|
||||
*/
|
||||
class Help extends BaseCommand
|
||||
{
|
||||
/**
|
||||
* The group the command is lumped under
|
||||
* when listing commands.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'CodeIgniter';
|
||||
|
||||
/**
|
||||
* The Command's name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'help';
|
||||
|
||||
/**
|
||||
* the Command's short description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Displays basic usage information.';
|
||||
|
||||
/**
|
||||
* the Command's usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'help [<command_name>]';
|
||||
|
||||
/**
|
||||
* the Command's Arguments
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $arguments = [
|
||||
'command_name' => 'The command name [default: "help"]',
|
||||
];
|
||||
|
||||
/**
|
||||
* the Command's Options
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $options = [];
|
||||
|
||||
/**
|
||||
* Displays the help for spark commands.
|
||||
*/
|
||||
public function run(array $params)
|
||||
{
|
||||
$command = array_shift($params);
|
||||
$command ??= 'help';
|
||||
$commands = $this->commands->getCommands();
|
||||
|
||||
if (! $this->commands->verifyCommand($command, $commands)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$class = new $commands[$command]['class']($this->logger, $this->commands);
|
||||
$class->showHelp();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* 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\Commands\Housekeeping;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
|
||||
/**
|
||||
* ClearDebugbar Command
|
||||
*/
|
||||
class ClearDebugbar extends BaseCommand
|
||||
{
|
||||
/**
|
||||
* The group the command is lumped under
|
||||
* when listing commands.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'Housekeeping';
|
||||
|
||||
/**
|
||||
* The Command's name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'debugbar:clear';
|
||||
|
||||
/**
|
||||
* The Command's usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'debugbar:clear';
|
||||
|
||||
/**
|
||||
* The Command's short description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Clears all debugbar JSON files.';
|
||||
|
||||
/**
|
||||
* Actually runs the command.
|
||||
*/
|
||||
public function run(array $params)
|
||||
{
|
||||
helper('filesystem');
|
||||
|
||||
if (! delete_files(WRITEPATH . 'debugbar', false, true)) {
|
||||
// @codeCoverageIgnoreStart
|
||||
CLI::error('Error deleting the debugbar JSON files.');
|
||||
CLI::newLine();
|
||||
|
||||
return;
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
CLI::write('Debugbar cleared.', 'green');
|
||||
CLI::newLine();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* 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\Commands\Housekeeping;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
|
||||
/**
|
||||
* ClearLogs command.
|
||||
*/
|
||||
class ClearLogs extends BaseCommand
|
||||
{
|
||||
/**
|
||||
* The group the command is lumped under
|
||||
* when listing commands.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'Housekeeping';
|
||||
|
||||
/**
|
||||
* The Command's name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'logs:clear';
|
||||
|
||||
/**
|
||||
* The Command's short description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Clears all log files.';
|
||||
|
||||
/**
|
||||
* The Command's usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'logs:clear [option';
|
||||
|
||||
/**
|
||||
* The Command's options
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $options = [
|
||||
'--force' => 'Force delete of all logs files without prompting.',
|
||||
];
|
||||
|
||||
/**
|
||||
* Actually execute a command.
|
||||
*/
|
||||
public function run(array $params)
|
||||
{
|
||||
$force = array_key_exists('force', $params) || CLI::getOption('force');
|
||||
|
||||
if (! $force && CLI::prompt('Are you sure you want to delete the logs?', ['n', 'y']) === 'n') {
|
||||
// @codeCoverageIgnoreStart
|
||||
CLI::error('Deleting logs aborted.', 'light_gray', 'red');
|
||||
CLI::error('If you want, use the "-force" option to force delete all log files.', 'light_gray', 'red');
|
||||
CLI::newLine();
|
||||
|
||||
return;
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
helper('filesystem');
|
||||
|
||||
if (! delete_files(WRITEPATH . 'logs', false, true)) {
|
||||
// @codeCoverageIgnoreStart
|
||||
CLI::error('Error in deleting the logs files.', 'light_gray', 'red');
|
||||
CLI::newLine();
|
||||
|
||||
return;
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
CLI::write('Logs cleared.', 'green');
|
||||
CLI::newLine();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* 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\Commands;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
|
||||
/**
|
||||
* CI Help command for the spark script.
|
||||
*
|
||||
* Lists the basic usage information for the spark script,
|
||||
* and provides a way to list help for other commands.
|
||||
*/
|
||||
class ListCommands extends BaseCommand
|
||||
{
|
||||
/**
|
||||
* The group the command is lumped under
|
||||
* when listing commands.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'CodeIgniter';
|
||||
|
||||
/**
|
||||
* The Command's name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'list';
|
||||
|
||||
/**
|
||||
* the Command's short description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Lists the available commands.';
|
||||
|
||||
/**
|
||||
* the Command's usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'list';
|
||||
|
||||
/**
|
||||
* the Command's Arguments
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $arguments = [];
|
||||
|
||||
/**
|
||||
* the Command's Options
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $options = [
|
||||
'--simple' => 'Prints a list of the commands with no other info',
|
||||
];
|
||||
|
||||
/**
|
||||
* Displays the help for the spark cli script itself.
|
||||
*/
|
||||
public function run(array $params)
|
||||
{
|
||||
$commands = $this->commands->getCommands();
|
||||
ksort($commands);
|
||||
|
||||
// Check for 'simple' format
|
||||
return array_key_exists('simple', $params) || CLI::getOption('simple')
|
||||
? $this->listSimple($commands)
|
||||
: $this->listFull($commands);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists the commands with accompanying info.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function listFull(array $commands)
|
||||
{
|
||||
// Sort into buckets by group
|
||||
$groups = [];
|
||||
|
||||
foreach ($commands as $title => $command) {
|
||||
if (! isset($groups[$command['group']])) {
|
||||
$groups[$command['group']] = [];
|
||||
}
|
||||
|
||||
$groups[$command['group']][$title] = $command;
|
||||
}
|
||||
|
||||
$length = max(array_map(strlen(...), array_keys($commands)));
|
||||
|
||||
ksort($groups);
|
||||
|
||||
// Display it all...
|
||||
foreach ($groups as $group => $commands) {
|
||||
CLI::write($group, 'yellow');
|
||||
|
||||
foreach ($commands as $name => $command) {
|
||||
$name = $this->setPad($name, $length, 2, 2);
|
||||
$output = CLI::color($name, 'green');
|
||||
|
||||
if (isset($command['description'])) {
|
||||
$output .= CLI::wrap($command['description'], 125, strlen($name));
|
||||
}
|
||||
|
||||
CLI::write($output);
|
||||
}
|
||||
|
||||
if ($group !== array_key_last($groups)) {
|
||||
CLI::newLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists the commands only.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function listSimple(array $commands)
|
||||
{
|
||||
foreach (array_keys($commands) as $title) {
|
||||
CLI::write($title);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* 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\Commands\Server;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
|
||||
/**
|
||||
* Launch the PHP development server
|
||||
*
|
||||
* Not testable, as it throws phpunit for a loop :-/
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class Serve extends BaseCommand
|
||||
{
|
||||
/**
|
||||
* Group
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'CodeIgniter';
|
||||
|
||||
/**
|
||||
* Name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'serve';
|
||||
|
||||
/**
|
||||
* Description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Launches the CodeIgniter PHP-Development Server.';
|
||||
|
||||
/**
|
||||
* Usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'serve';
|
||||
|
||||
/**
|
||||
* Arguments
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $arguments = [];
|
||||
|
||||
/**
|
||||
* The current port offset.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $portOffset = 0;
|
||||
|
||||
/**
|
||||
* The max number of ports to attempt to serve from
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $tries = 10;
|
||||
|
||||
/**
|
||||
* Options
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $options = [
|
||||
'--php' => 'The PHP Binary [default: "PHP_BINARY"]',
|
||||
'--host' => 'The HTTP Host [default: "localhost"]',
|
||||
'--port' => 'The HTTP Host Port [default: "8080"]',
|
||||
];
|
||||
|
||||
/**
|
||||
* Run the server
|
||||
*/
|
||||
public function run(array $params)
|
||||
{
|
||||
// Collect any user-supplied options and apply them.
|
||||
$php = escapeshellarg(CLI::getOption('php') ?? PHP_BINARY);
|
||||
$host = CLI::getOption('host') ?? 'localhost';
|
||||
$port = (int) (CLI::getOption('port') ?? 8080) + $this->portOffset;
|
||||
|
||||
// Get the party started.
|
||||
CLI::write('CodeIgniter development server started on http://' . $host . ':' . $port, 'green');
|
||||
CLI::write('Press Control-C to stop.');
|
||||
|
||||
// Set the Front Controller path as Document Root.
|
||||
$docroot = escapeshellarg(FCPATH);
|
||||
|
||||
// Mimic Apache's mod_rewrite functionality with user settings.
|
||||
$rewrite = escapeshellarg(SYSTEMPATH . 'rewrite.php');
|
||||
|
||||
// Call PHP's built-in webserver, making sure to set our
|
||||
// base path to the public folder, and to use the rewrite file
|
||||
// to ensure our environment is set and it simulates basic mod_rewrite.
|
||||
passthru($php . ' -S ' . $host . ':' . $port . ' -t ' . $docroot . ' ' . $rewrite, $status);
|
||||
|
||||
if ($status && $this->portOffset < $this->tries) {
|
||||
$this->portOffset++;
|
||||
|
||||
$this->run($params);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,389 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* 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\Commands\Translation;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
use CodeIgniter\Helpers\Array\ArrayHelper;
|
||||
use Config\App;
|
||||
use Locale;
|
||||
use RecursiveDirectoryIterator;
|
||||
use RecursiveIteratorIterator;
|
||||
use SplFileInfo;
|
||||
|
||||
/**
|
||||
* @see \CodeIgniter\Commands\Translation\LocalizationFinderTest
|
||||
*/
|
||||
class LocalizationFinder extends BaseCommand
|
||||
{
|
||||
protected $group = 'Translation';
|
||||
protected $name = 'lang:find';
|
||||
protected $description = 'Find and save available phrases to translate.';
|
||||
protected $usage = 'lang:find [options]';
|
||||
protected $arguments = [];
|
||||
protected $options = [
|
||||
'--locale' => 'Specify locale (en, ru, etc.) to save files.',
|
||||
'--dir' => 'Directory to search for translations relative to APPPATH.',
|
||||
'--show-new' => 'Show only new translations in table. Does not write to files.',
|
||||
'--verbose' => 'Output detailed information.',
|
||||
];
|
||||
|
||||
/**
|
||||
* Flag for output detailed information
|
||||
*/
|
||||
private bool $verbose = false;
|
||||
|
||||
/**
|
||||
* Flag for showing only translations, without saving
|
||||
*/
|
||||
private bool $showNew = false;
|
||||
|
||||
private string $languagePath;
|
||||
|
||||
public function run(array $params)
|
||||
{
|
||||
$this->verbose = array_key_exists('verbose', $params);
|
||||
$this->showNew = array_key_exists('show-new', $params);
|
||||
$optionLocale = $params['locale'] ?? null;
|
||||
$optionDir = $params['dir'] ?? null;
|
||||
$currentLocale = Locale::getDefault();
|
||||
$currentDir = APPPATH;
|
||||
$this->languagePath = $currentDir . 'Language';
|
||||
|
||||
if (ENVIRONMENT === 'testing') {
|
||||
$currentDir = SUPPORTPATH . 'Services' . DIRECTORY_SEPARATOR;
|
||||
$this->languagePath = SUPPORTPATH . 'Language';
|
||||
}
|
||||
|
||||
if (is_string($optionLocale)) {
|
||||
if (! in_array($optionLocale, config(App::class)->supportedLocales, true)) {
|
||||
CLI::error(
|
||||
'Error: "' . $optionLocale . '" is not supported. Supported locales: '
|
||||
. implode(', ', config(App::class)->supportedLocales)
|
||||
);
|
||||
|
||||
return EXIT_USER_INPUT;
|
||||
}
|
||||
|
||||
$currentLocale = $optionLocale;
|
||||
}
|
||||
|
||||
if (is_string($optionDir)) {
|
||||
$tempCurrentDir = realpath($currentDir . $optionDir);
|
||||
|
||||
if ($tempCurrentDir === false) {
|
||||
CLI::error('Error: Directory must be located in "' . $currentDir . '"');
|
||||
|
||||
return EXIT_USER_INPUT;
|
||||
}
|
||||
|
||||
if ($this->isSubDirectory($tempCurrentDir, $this->languagePath)) {
|
||||
CLI::error('Error: Directory "' . $this->languagePath . '" restricted to scan.');
|
||||
|
||||
return EXIT_USER_INPUT;
|
||||
}
|
||||
|
||||
$currentDir = $tempCurrentDir;
|
||||
}
|
||||
|
||||
$this->process($currentDir, $currentLocale);
|
||||
|
||||
CLI::write('All operations done!');
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
private function process(string $currentDir, string $currentLocale): void
|
||||
{
|
||||
$tableRows = [];
|
||||
$countNewKeys = 0;
|
||||
|
||||
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($currentDir));
|
||||
$files = iterator_to_array($iterator, true);
|
||||
ksort($files);
|
||||
|
||||
[
|
||||
'foundLanguageKeys' => $foundLanguageKeys,
|
||||
'badLanguageKeys' => $badLanguageKeys,
|
||||
'countFiles' => $countFiles
|
||||
] = $this->findLanguageKeysInFiles($files);
|
||||
|
||||
ksort($foundLanguageKeys);
|
||||
|
||||
$languageDiff = [];
|
||||
$languageFoundGroups = array_unique(array_keys($foundLanguageKeys));
|
||||
|
||||
foreach ($languageFoundGroups as $langFileName) {
|
||||
$languageStoredKeys = [];
|
||||
$languageFilePath = $this->languagePath . DIRECTORY_SEPARATOR . $currentLocale . DIRECTORY_SEPARATOR . $langFileName . '.php';
|
||||
|
||||
if (is_file($languageFilePath)) {
|
||||
// Load old localization
|
||||
$languageStoredKeys = require $languageFilePath;
|
||||
}
|
||||
|
||||
$languageDiff = ArrayHelper::recursiveDiff($foundLanguageKeys[$langFileName], $languageStoredKeys);
|
||||
$countNewKeys += ArrayHelper::recursiveCount($languageDiff);
|
||||
|
||||
if ($this->showNew) {
|
||||
$tableRows = array_merge($this->arrayToTableRows($langFileName, $languageDiff), $tableRows);
|
||||
} else {
|
||||
$newLanguageKeys = array_replace_recursive($foundLanguageKeys[$langFileName], $languageStoredKeys);
|
||||
|
||||
if ($languageDiff !== []) {
|
||||
if (file_put_contents($languageFilePath, $this->templateFile($newLanguageKeys)) === false) {
|
||||
$this->writeIsVerbose('Lang file ' . $langFileName . ' (error write).', 'red');
|
||||
} else {
|
||||
$this->writeIsVerbose('Lang file "' . $langFileName . '" successful updated!', 'green');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->showNew && $tableRows !== []) {
|
||||
sort($tableRows);
|
||||
CLI::table($tableRows, ['File', 'Key']);
|
||||
}
|
||||
|
||||
if (! $this->showNew && $countNewKeys > 0) {
|
||||
CLI::write('Note: You need to run your linting tool to fix coding standards issues.', 'white', 'red');
|
||||
}
|
||||
|
||||
$this->writeIsVerbose('Files found: ' . $countFiles);
|
||||
$this->writeIsVerbose('New translates found: ' . $countNewKeys);
|
||||
$this->writeIsVerbose('Bad translates found: ' . count($badLanguageKeys));
|
||||
|
||||
if ($this->verbose && $badLanguageKeys !== []) {
|
||||
$tableBadRows = [];
|
||||
|
||||
foreach ($badLanguageKeys as $value) {
|
||||
$tableBadRows[] = [$value[1], $value[0]];
|
||||
}
|
||||
|
||||
ArrayHelper::sortValuesByNatural($tableBadRows, 0);
|
||||
|
||||
CLI::table($tableBadRows, ['Bad Key', 'Filepath']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SplFileInfo|string $file
|
||||
*
|
||||
* @return array<string, array>
|
||||
*/
|
||||
private function findTranslationsInFile($file): array
|
||||
{
|
||||
$foundLanguageKeys = [];
|
||||
$badLanguageKeys = [];
|
||||
|
||||
if (is_string($file) && is_file($file)) {
|
||||
$file = new SplFileInfo($file);
|
||||
}
|
||||
|
||||
$fileContent = file_get_contents($file->getRealPath());
|
||||
preg_match_all('/lang\(\'([._a-z0-9\-]+)\'\)/ui', $fileContent, $matches);
|
||||
|
||||
if ($matches[1] === []) {
|
||||
return compact('foundLanguageKeys', 'badLanguageKeys');
|
||||
}
|
||||
|
||||
foreach ($matches[1] as $phraseKey) {
|
||||
$phraseKeys = explode('.', $phraseKey);
|
||||
|
||||
// Language key not have Filename or Lang key
|
||||
if (count($phraseKeys) < 2) {
|
||||
$badLanguageKeys[] = [mb_substr($file->getRealPath(), mb_strlen(ROOTPATH)), $phraseKey];
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$languageFileName = array_shift($phraseKeys);
|
||||
$isEmptyNestedArray = ($languageFileName !== '' && $phraseKeys[0] === '')
|
||||
|| ($languageFileName === '' && $phraseKeys[0] !== '')
|
||||
|| ($languageFileName === '' && $phraseKeys[0] === '');
|
||||
|
||||
if ($isEmptyNestedArray) {
|
||||
$badLanguageKeys[] = [mb_substr($file->getRealPath(), mb_strlen(ROOTPATH)), $phraseKey];
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (count($phraseKeys) === 1) {
|
||||
$foundLanguageKeys[$languageFileName][$phraseKeys[0]] = $phraseKey;
|
||||
} else {
|
||||
$childKeys = $this->buildMultiArray($phraseKeys, $phraseKey);
|
||||
|
||||
$foundLanguageKeys[$languageFileName] = array_replace_recursive($foundLanguageKeys[$languageFileName] ?? [], $childKeys);
|
||||
}
|
||||
}
|
||||
|
||||
return compact('foundLanguageKeys', 'badLanguageKeys');
|
||||
}
|
||||
|
||||
private function isIgnoredFile(SplFileInfo $file): bool
|
||||
{
|
||||
if ($file->isDir() || $this->isSubDirectory($file->getRealPath(), $this->languagePath)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $file->getExtension() !== 'php';
|
||||
}
|
||||
|
||||
private function templateFile(array $language = []): string
|
||||
{
|
||||
if ($language !== []) {
|
||||
$languageArrayString = var_export($language, true);
|
||||
|
||||
$code = <<<PHP
|
||||
<?php
|
||||
|
||||
return {$languageArrayString};
|
||||
|
||||
PHP;
|
||||
|
||||
return $this->replaceArraySyntax($code);
|
||||
}
|
||||
|
||||
return <<<'PHP'
|
||||
<?php
|
||||
|
||||
return [];
|
||||
|
||||
PHP;
|
||||
}
|
||||
|
||||
private function replaceArraySyntax(string $code): string
|
||||
{
|
||||
$tokens = token_get_all($code);
|
||||
$newTokens = $tokens;
|
||||
|
||||
foreach ($tokens as $i => $token) {
|
||||
if (is_array($token)) {
|
||||
[$tokenId, $tokenValue] = $token;
|
||||
|
||||
// Replace "array ("
|
||||
if (
|
||||
$tokenId === T_ARRAY
|
||||
&& $tokens[$i + 1][0] === T_WHITESPACE
|
||||
&& $tokens[$i + 2] === '('
|
||||
) {
|
||||
$newTokens[$i][1] = '[';
|
||||
$newTokens[$i + 1][1] = '';
|
||||
$newTokens[$i + 2] = '';
|
||||
}
|
||||
|
||||
// Replace indent
|
||||
if ($tokenId === T_WHITESPACE && preg_match('/\n([ ]+)/u', $tokenValue, $matches)) {
|
||||
$newTokens[$i][1] = "\n{$matches[1]}{$matches[1]}";
|
||||
}
|
||||
} // Replace ")"
|
||||
elseif ($token === ')') {
|
||||
$newTokens[$i] = ']';
|
||||
}
|
||||
}
|
||||
|
||||
$output = '';
|
||||
|
||||
foreach ($newTokens as $token) {
|
||||
$output .= $token[1] ?? $token;
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create multidimensional array from another keys
|
||||
*/
|
||||
private function buildMultiArray(array $fromKeys, string $lastArrayValue = ''): array
|
||||
{
|
||||
$newArray = [];
|
||||
$lastIndex = array_pop($fromKeys);
|
||||
$current = &$newArray;
|
||||
|
||||
foreach ($fromKeys as $value) {
|
||||
$current[$value] = [];
|
||||
$current = &$current[$value];
|
||||
}
|
||||
|
||||
$current[$lastIndex] = $lastArrayValue;
|
||||
|
||||
return $newArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert multi arrays to specific CLI table rows (flat array)
|
||||
*/
|
||||
private function arrayToTableRows(string $langFileName, array $array): array
|
||||
{
|
||||
$rows = [];
|
||||
|
||||
foreach ($array as $value) {
|
||||
if (is_array($value)) {
|
||||
$rows = array_merge($rows, $this->arrayToTableRows($langFileName, $value));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_string($value)) {
|
||||
$rows[] = [$langFileName, $value];
|
||||
}
|
||||
}
|
||||
|
||||
return $rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show details in the console if the flag is set
|
||||
*/
|
||||
private function writeIsVerbose(string $text = '', ?string $foreground = null, ?string $background = null): void
|
||||
{
|
||||
if ($this->verbose) {
|
||||
CLI::write($text, $foreground, $background);
|
||||
}
|
||||
}
|
||||
|
||||
private function isSubDirectory(string $directory, string $rootDirectory): bool
|
||||
{
|
||||
return 0 === strncmp($directory, $rootDirectory, strlen($directory));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<SplFileInfo> $files
|
||||
*
|
||||
* @return array<string, array|int>
|
||||
* @phpstan-return array{'foundLanguageKeys': array<string, array<string, string>>, 'badLanguageKeys': array<int, array<int, string>>, 'countFiles': int}
|
||||
*/
|
||||
private function findLanguageKeysInFiles(array $files): array
|
||||
{
|
||||
$foundLanguageKeys = [];
|
||||
$badLanguageKeys = [];
|
||||
$countFiles = 0;
|
||||
|
||||
foreach ($files as $file) {
|
||||
if ($this->isIgnoredFile($file)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->writeIsVerbose('File found: ' . mb_substr($file->getRealPath(), mb_strlen(APPPATH)));
|
||||
$countFiles++;
|
||||
|
||||
$findInFile = $this->findTranslationsInFile($file);
|
||||
|
||||
$foundLanguageKeys = array_replace_recursive($findInFile['foundLanguageKeys'], $foundLanguageKeys);
|
||||
$badLanguageKeys = array_merge($findInFile['badLanguageKeys'], $badLanguageKeys);
|
||||
}
|
||||
|
||||
return compact('foundLanguageKeys', 'badLanguageKeys', 'countFiles');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* 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\Commands\Utilities;
|
||||
|
||||
use CodeIgniter\Cache\FactoriesCache;
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
use CodeIgniter\Config\BaseConfig;
|
||||
use Config\Optimize;
|
||||
use Kint\Kint;
|
||||
|
||||
/**
|
||||
* Check the Config values.
|
||||
*
|
||||
* @see \CodeIgniter\Commands\Utilities\ConfigCheckTest
|
||||
*/
|
||||
final class ConfigCheck extends BaseCommand
|
||||
{
|
||||
/**
|
||||
* The group the command is lumped under
|
||||
* when listing commands.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'CodeIgniter';
|
||||
|
||||
/**
|
||||
* The Command's name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'config:check';
|
||||
|
||||
/**
|
||||
* The Command's short description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Check your Config values.';
|
||||
|
||||
/**
|
||||
* The Command's usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'config:check <classname>';
|
||||
|
||||
/**
|
||||
* The Command's arguments
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $arguments = [
|
||||
'classname' => 'The config classname to check. Short classname or FQCN.',
|
||||
];
|
||||
|
||||
/**
|
||||
* The Command's options
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $options = [];
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function run(array $params)
|
||||
{
|
||||
if (! isset($params[0])) {
|
||||
CLI::error('You must specify a Config classname.');
|
||||
CLI::write(' Usage: ' . $this->usage);
|
||||
CLI::write('Example: config:check App');
|
||||
CLI::write(' config:check \'CodeIgniter\Shield\Config\Auth\'');
|
||||
|
||||
return EXIT_ERROR;
|
||||
}
|
||||
|
||||
/** @var class-string<BaseConfig> $class */
|
||||
$class = $params[0];
|
||||
|
||||
// Load Config cache if it is enabled.
|
||||
$configCacheEnabled = class_exists(Optimize::class)
|
||||
&& (new Optimize())->configCacheEnabled;
|
||||
if ($configCacheEnabled) {
|
||||
$factoriesCache = new FactoriesCache();
|
||||
$factoriesCache->load('config');
|
||||
}
|
||||
|
||||
$config = config($class);
|
||||
|
||||
if ($config === null) {
|
||||
CLI::error('No such Config class: ' . $class);
|
||||
|
||||
return EXIT_ERROR;
|
||||
}
|
||||
|
||||
if (defined('KINT_DIR') && Kint::$enabled_mode !== false) {
|
||||
CLI::write($this->getKintD($config));
|
||||
} else {
|
||||
CLI::write(
|
||||
CLI::color($this->getVarDump($config), 'cyan')
|
||||
);
|
||||
}
|
||||
|
||||
CLI::newLine();
|
||||
$state = CLI::color($configCacheEnabled ? 'Enabled' : 'Disabled', 'green');
|
||||
CLI::write('Config Caching: ' . $state);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets object dump by Kint d()
|
||||
*/
|
||||
private function getKintD(object $config): string
|
||||
{
|
||||
ob_start();
|
||||
d($config);
|
||||
$output = ob_get_clean();
|
||||
|
||||
$output = trim($output);
|
||||
|
||||
$lines = explode("\n", $output);
|
||||
array_splice($lines, 0, 3);
|
||||
array_splice($lines, -3);
|
||||
|
||||
return implode("\n", $lines);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets object dump by var_dump()
|
||||
*/
|
||||
private function getVarDump(object $config): string
|
||||
{
|
||||
ob_start();
|
||||
var_dump($config);
|
||||
$output = ob_get_clean();
|
||||
|
||||
return preg_replace(
|
||||
'!.*system/Commands/Utilities/ConfigCheck.php.*\n!u',
|
||||
'',
|
||||
$output
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* 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\Commands\Utilities;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
use CodeIgniter\Config\DotEnv;
|
||||
|
||||
/**
|
||||
* Command to display the current environment,
|
||||
* or set a new one in the `.env` file.
|
||||
*/
|
||||
final class Environment extends BaseCommand
|
||||
{
|
||||
/**
|
||||
* The group the command is lumped under
|
||||
* when listing commands.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'CodeIgniter';
|
||||
|
||||
/**
|
||||
* The Command's name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'env';
|
||||
|
||||
/**
|
||||
* The Command's short description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Retrieves the current environment, or set a new one.';
|
||||
|
||||
/**
|
||||
* The Command's usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'env [<environment>]';
|
||||
|
||||
/**
|
||||
* The Command's arguments
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $arguments = [
|
||||
'environment' => '[Optional] The new environment to set. If none is provided, this will print the current environment.',
|
||||
];
|
||||
|
||||
/**
|
||||
* The Command's options
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $options = [];
|
||||
|
||||
/**
|
||||
* Allowed values for environment. `testing` is excluded
|
||||
* since spark won't work on it.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
private static array $knownTypes = [
|
||||
'production',
|
||||
'development',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function run(array $params)
|
||||
{
|
||||
if ($params === []) {
|
||||
CLI::write(sprintf('Your environment is currently set as %s.', CLI::color($_SERVER['CI_ENVIRONMENT'] ?? ENVIRONMENT, 'green')));
|
||||
CLI::newLine();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$env = strtolower(array_shift($params));
|
||||
|
||||
if ($env === 'testing') {
|
||||
CLI::error('The "testing" environment is reserved for PHPUnit testing.', 'light_gray', 'red');
|
||||
CLI::error('You will not be able to run spark under a "testing" environment.', 'light_gray', 'red');
|
||||
CLI::newLine();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (! in_array($env, self::$knownTypes, true)) {
|
||||
CLI::error(sprintf('Invalid environment type "%s". Expected one of "%s".', $env, implode('" and "', self::$knownTypes)), 'light_gray', 'red');
|
||||
CLI::newLine();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (! $this->writeNewEnvironmentToEnvFile($env)) {
|
||||
CLI::error('Error in writing new environment to .env file.', 'light_gray', 'red');
|
||||
CLI::newLine();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// force DotEnv to reload the new environment
|
||||
// however we cannot redefine the ENVIRONMENT constant
|
||||
putenv('CI_ENVIRONMENT');
|
||||
unset($_ENV['CI_ENVIRONMENT'], $_SERVER['CI_ENVIRONMENT']);
|
||||
(new DotEnv(ROOTPATH))->load();
|
||||
|
||||
CLI::write(sprintf('Environment is successfully changed to "%s".', $env), 'green');
|
||||
CLI::write('The ENVIRONMENT constant will be changed in the next script execution.');
|
||||
CLI::newLine();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://regex101.com/r/4sSORp/1 for the regex in action
|
||||
*/
|
||||
private function writeNewEnvironmentToEnvFile(string $newEnv): bool
|
||||
{
|
||||
$baseEnv = ROOTPATH . 'env';
|
||||
$envFile = ROOTPATH . '.env';
|
||||
|
||||
if (! is_file($envFile)) {
|
||||
if (! is_file($baseEnv)) {
|
||||
CLI::write('Both default shipped `env` file and custom `.env` are missing.', 'yellow');
|
||||
CLI::write('It is impossible to write the new environment type.', 'yellow');
|
||||
CLI::newLine();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
copy($baseEnv, $envFile);
|
||||
}
|
||||
|
||||
$pattern = preg_quote($_SERVER['CI_ENVIRONMENT'] ?? ENVIRONMENT, '/');
|
||||
$pattern = sprintf('/^[#\s]*CI_ENVIRONMENT[=\s]+%s$/m', $pattern);
|
||||
|
||||
return file_put_contents(
|
||||
$envFile,
|
||||
preg_replace($pattern, "\nCI_ENVIRONMENT = {$newEnv}", file_get_contents($envFile), -1, $count)
|
||||
) !== false && $count > 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* 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\Commands\Utilities;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
use CodeIgniter\Commands\Utilities\Routes\FilterCollector;
|
||||
|
||||
/**
|
||||
* Check filters for a route.
|
||||
*/
|
||||
class FilterCheck extends BaseCommand
|
||||
{
|
||||
/**
|
||||
* The group the command is lumped under
|
||||
* when listing commands.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'CodeIgniter';
|
||||
|
||||
/**
|
||||
* The Command's name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'filter:check';
|
||||
|
||||
/**
|
||||
* the Command's short description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Check filters for a route.';
|
||||
|
||||
/**
|
||||
* the Command's usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'filter:check <HTTP method> <route>';
|
||||
|
||||
/**
|
||||
* the Command's Arguments
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $arguments = [
|
||||
'method' => 'The HTTP method. GET, POST, PUT, etc.',
|
||||
'route' => 'The route (URI path) to check filters.',
|
||||
];
|
||||
|
||||
/**
|
||||
* the Command's Options
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $options = [];
|
||||
|
||||
/**
|
||||
* @return int exit code
|
||||
*/
|
||||
public function run(array $params)
|
||||
{
|
||||
$tbody = [];
|
||||
if (! isset($params[0], $params[1])) {
|
||||
CLI::error('You must specify a HTTP verb and a route.');
|
||||
CLI::write(' Usage: ' . $this->usage);
|
||||
CLI::write('Example: filter:check GET /');
|
||||
CLI::write(' filter:check PUT products/1');
|
||||
|
||||
return EXIT_ERROR;
|
||||
}
|
||||
|
||||
$method = $params[0];
|
||||
$route = $params[1];
|
||||
|
||||
// Load Routes
|
||||
service('routes')->loadRoutes();
|
||||
|
||||
$filterCollector = new FilterCollector();
|
||||
|
||||
$filters = $filterCollector->get($method, $route);
|
||||
|
||||
// PageNotFoundException
|
||||
if ($filters['before'] === ['<unknown>']) {
|
||||
CLI::error(
|
||||
"Can't find a route: " .
|
||||
CLI::color(
|
||||
'"' . strtoupper($method) . ' ' . $route . '"',
|
||||
'black',
|
||||
'light_gray'
|
||||
),
|
||||
);
|
||||
|
||||
return EXIT_ERROR;
|
||||
}
|
||||
|
||||
$filters = $this->addRequiredFilters($filterCollector, $filters);
|
||||
|
||||
$tbody[] = [
|
||||
strtoupper($method),
|
||||
$route,
|
||||
implode(' ', $filters['before']),
|
||||
implode(' ', $filters['after']),
|
||||
];
|
||||
|
||||
$thead = [
|
||||
'Method',
|
||||
'Route',
|
||||
'Before Filters',
|
||||
'After Filters',
|
||||
];
|
||||
|
||||
CLI::table($tbody, $thead);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
private function addRequiredFilters(FilterCollector $filterCollector, array $filters): array
|
||||
{
|
||||
$output = [];
|
||||
|
||||
$required = $filterCollector->getRequiredFilters();
|
||||
|
||||
$colored = [];
|
||||
|
||||
foreach ($required['before'] as $filter) {
|
||||
$filter = CLI::color($filter, 'yellow');
|
||||
$colored[] = $filter;
|
||||
}
|
||||
$output['before'] = array_merge($colored, $filters['before']);
|
||||
|
||||
$colored = [];
|
||||
|
||||
foreach ($required['after'] as $filter) {
|
||||
$filter = CLI::color($filter, 'yellow');
|
||||
$colored[] = $filter;
|
||||
}
|
||||
$output['after'] = array_merge($filters['after'], $colored);
|
||||
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* 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\Commands\Utilities;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
use Config\Autoload;
|
||||
|
||||
/**
|
||||
* Lists namespaces set in Config\Autoload with their
|
||||
* full server path. Helps you to verify that you have
|
||||
* the namespaces setup correctly.
|
||||
*
|
||||
* @see \CodeIgniter\Commands\Utilities\NamespacesTest
|
||||
*/
|
||||
class Namespaces extends BaseCommand
|
||||
{
|
||||
/**
|
||||
* The group the command is lumped under
|
||||
* when listing commands.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'CodeIgniter';
|
||||
|
||||
/**
|
||||
* The Command's name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'namespaces';
|
||||
|
||||
/**
|
||||
* the Command's short description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Verifies your namespaces are setup correctly.';
|
||||
|
||||
/**
|
||||
* the Command's usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'namespaces';
|
||||
|
||||
/**
|
||||
* the Command's Arguments
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $arguments = [];
|
||||
|
||||
/**
|
||||
* the Command's Options
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $options = [
|
||||
'-c' => 'Show only CodeIgniter config namespaces.',
|
||||
'-r' => 'Show raw path strings.',
|
||||
'-m' => 'Specify max length of the path strings to output. Default: 60.',
|
||||
];
|
||||
|
||||
/**
|
||||
* Displays the help for the spark cli script itself.
|
||||
*/
|
||||
public function run(array $params)
|
||||
{
|
||||
$params['m'] = (int) ($params['m'] ?? 60);
|
||||
|
||||
$tbody = array_key_exists('c', $params) ? $this->outputCINamespaces($params) : $this->outputAllNamespaces($params);
|
||||
|
||||
$thead = [
|
||||
'Namespace',
|
||||
'Path',
|
||||
'Found?',
|
||||
];
|
||||
|
||||
CLI::table($tbody, $thead);
|
||||
}
|
||||
|
||||
private function outputAllNamespaces(array $params): array
|
||||
{
|
||||
$maxLength = $params['m'];
|
||||
|
||||
$autoloader = service('autoloader');
|
||||
|
||||
$tbody = [];
|
||||
|
||||
foreach ($autoloader->getNamespace() as $ns => $paths) {
|
||||
foreach ($paths as $path) {
|
||||
if (array_key_exists('r', $params)) {
|
||||
$pathOutput = $this->truncate($path, $maxLength);
|
||||
} else {
|
||||
$pathOutput = $this->truncate(clean_path($path), $maxLength);
|
||||
}
|
||||
|
||||
$tbody[] = [
|
||||
$ns,
|
||||
$pathOutput,
|
||||
is_dir($path) ? 'Yes' : 'MISSING',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $tbody;
|
||||
}
|
||||
|
||||
private function truncate(string $string, int $max): string
|
||||
{
|
||||
$length = strlen($string);
|
||||
|
||||
if ($length > $max) {
|
||||
return substr($string, 0, $max - 3) . '...';
|
||||
}
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
private function outputCINamespaces(array $params): array
|
||||
{
|
||||
$maxLength = $params['m'];
|
||||
|
||||
$config = new Autoload();
|
||||
|
||||
$tbody = [];
|
||||
|
||||
foreach ($config->psr4 as $ns => $paths) {
|
||||
foreach ((array) $paths as $path) {
|
||||
if (array_key_exists('r', $params)) {
|
||||
$pathOutput = $this->truncate($path, $maxLength);
|
||||
} else {
|
||||
$pathOutput = $this->truncate(clean_path($path), $maxLength);
|
||||
}
|
||||
|
||||
$path = realpath($path) ?: $path;
|
||||
|
||||
$tbody[] = [
|
||||
$ns,
|
||||
$pathOutput,
|
||||
is_dir($path) ? 'Yes' : 'MISSING',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $tbody;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* 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\Commands\Utilities;
|
||||
|
||||
use CodeIgniter\Autoloader\FileLocator;
|
||||
use CodeIgniter\Autoloader\FileLocatorCached;
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
use CodeIgniter\Publisher\Publisher;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Optimize for production.
|
||||
*/
|
||||
final class Optimize extends BaseCommand
|
||||
{
|
||||
/**
|
||||
* The group the command is lumped under
|
||||
* when listing commands.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'CodeIgniter';
|
||||
|
||||
/**
|
||||
* The Command's name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'optimize';
|
||||
|
||||
/**
|
||||
* The Command's short description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Optimize for production.';
|
||||
|
||||
/**
|
||||
* The Command's usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'optimize';
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function run(array $params)
|
||||
{
|
||||
try {
|
||||
$this->enableCaching();
|
||||
$this->clearCache();
|
||||
$this->removeDevPackages();
|
||||
} catch (RuntimeException) {
|
||||
CLI::error('The "spark optimize" failed.');
|
||||
|
||||
return EXIT_ERROR;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
private function clearCache(): void
|
||||
{
|
||||
$locator = new FileLocatorCached(new FileLocator(service('autoloader')));
|
||||
$locator->deleteCache();
|
||||
CLI::write('Removed FileLocatorCache.', 'green');
|
||||
|
||||
$cache = WRITEPATH . 'cache/FactoriesCache_config';
|
||||
$this->removeFile($cache);
|
||||
}
|
||||
|
||||
private function removeFile(string $cache): void
|
||||
{
|
||||
if (is_file($cache)) {
|
||||
$result = unlink($cache);
|
||||
|
||||
if ($result) {
|
||||
CLI::write('Removed "' . clean_path($cache) . '".', 'green');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
CLI::error('Error in removing file: ' . clean_path($cache));
|
||||
|
||||
throw new RuntimeException(__METHOD__);
|
||||
}
|
||||
}
|
||||
|
||||
private function enableCaching(): void
|
||||
{
|
||||
$publisher = new Publisher(APPPATH, APPPATH);
|
||||
|
||||
$config = APPPATH . 'Config/Optimize.php';
|
||||
|
||||
$result = $publisher->replace(
|
||||
$config,
|
||||
[
|
||||
'public bool $configCacheEnabled = false;' => 'public bool $configCacheEnabled = true;',
|
||||
'public bool $locatorCacheEnabled = false;' => 'public bool $locatorCacheEnabled = true;',
|
||||
]
|
||||
);
|
||||
|
||||
if ($result) {
|
||||
CLI::write(
|
||||
'Config Caching and FileLocator Caching are enabled in "app/Config/Optimize.php".',
|
||||
'green'
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
CLI::error('Error in updating file: ' . clean_path($config));
|
||||
|
||||
throw new RuntimeException(__METHOD__);
|
||||
}
|
||||
|
||||
private function removeDevPackages(): void
|
||||
{
|
||||
if (! defined('VENDORPATH')) {
|
||||
return;
|
||||
}
|
||||
|
||||
chdir(ROOTPATH);
|
||||
passthru('composer install --no-dev', $status);
|
||||
|
||||
if ($status === 0) {
|
||||
CLI::write('Removed Composer dev packages.', 'green');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
CLI::error('Error in removing Composer dev packages.');
|
||||
|
||||
throw new RuntimeException(__METHOD__);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* 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\Commands\Utilities;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\Security\CheckPhpIni;
|
||||
|
||||
/**
|
||||
* Check php.ini values.
|
||||
*/
|
||||
final class PhpIniCheck extends BaseCommand
|
||||
{
|
||||
/**
|
||||
* The group the command is lumped under
|
||||
* when listing commands.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'CodeIgniter';
|
||||
|
||||
/**
|
||||
* The Command's name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'phpini:check';
|
||||
|
||||
/**
|
||||
* The Command's short description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Check your php.ini values.';
|
||||
|
||||
/**
|
||||
* The Command's usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'phpini:check';
|
||||
|
||||
/**
|
||||
* The Command's arguments
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $arguments = [
|
||||
];
|
||||
|
||||
/**
|
||||
* The Command's options
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $options = [];
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function run(array $params)
|
||||
{
|
||||
CheckPhpIni::run();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* 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\Commands\Utilities;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
use CodeIgniter\Publisher\Publisher;
|
||||
|
||||
/**
|
||||
* Discovers all Publisher classes from the "Publishers/" directory
|
||||
* across namespaces. Executes `publish()` from each instance, parsing
|
||||
* each result.
|
||||
*/
|
||||
class Publish extends BaseCommand
|
||||
{
|
||||
/**
|
||||
* The group the command is lumped under
|
||||
* when listing commands.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'CodeIgniter';
|
||||
|
||||
/**
|
||||
* The Command's name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'publish';
|
||||
|
||||
/**
|
||||
* The Command's short description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Discovers and executes all predefined Publisher classes.';
|
||||
|
||||
/**
|
||||
* The Command's usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'publish [<directory>]';
|
||||
|
||||
/**
|
||||
* The Command's arguments
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $arguments = [
|
||||
'directory' => '[Optional] The directory to scan within each namespace. Default: "Publishers".',
|
||||
];
|
||||
|
||||
/**
|
||||
* the Command's Options
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $options = [];
|
||||
|
||||
/**
|
||||
* Displays the help for the spark cli script itself.
|
||||
*/
|
||||
public function run(array $params)
|
||||
{
|
||||
$directory = array_shift($params) ?? 'Publishers';
|
||||
|
||||
if ([] === $publishers = Publisher::discover($directory)) {
|
||||
CLI::write(lang('Publisher.publishMissing', [$directory]));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($publishers as $publisher) {
|
||||
if ($publisher->publish()) {
|
||||
CLI::write(lang('Publisher.publishSuccess', [
|
||||
$publisher::class,
|
||||
count($publisher->getPublished()),
|
||||
$publisher->getDestination(),
|
||||
]), 'green');
|
||||
} else {
|
||||
CLI::error(lang('Publisher.publishFailure', [
|
||||
$publisher::class,
|
||||
$publisher->getDestination(),
|
||||
]), 'light_gray', 'red');
|
||||
|
||||
foreach ($publisher->getErrors() as $file => $exception) {
|
||||
CLI::write($file);
|
||||
CLI::error($exception->getMessage());
|
||||
CLI::newLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* 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\Commands\Utilities;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
use CodeIgniter\Commands\Utilities\Routes\AutoRouteCollector;
|
||||
use CodeIgniter\Commands\Utilities\Routes\AutoRouterImproved\AutoRouteCollector as AutoRouteCollectorImproved;
|
||||
use CodeIgniter\Commands\Utilities\Routes\FilterCollector;
|
||||
use CodeIgniter\Commands\Utilities\Routes\SampleURIGenerator;
|
||||
use CodeIgniter\Router\DefinedRouteCollector;
|
||||
use CodeIgniter\Router\Router;
|
||||
use Config\Feature;
|
||||
use Config\Routing;
|
||||
|
||||
/**
|
||||
* Lists all the routes. This will include any Routes files
|
||||
* that can be discovered, and will include routes that are not defined
|
||||
* in routes files, but are instead discovered through auto-routing.
|
||||
*/
|
||||
class Routes extends BaseCommand
|
||||
{
|
||||
/**
|
||||
* The group the command is lumped under
|
||||
* when listing commands.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'CodeIgniter';
|
||||
|
||||
/**
|
||||
* The Command's name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'routes';
|
||||
|
||||
/**
|
||||
* the Command's short description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Displays all routes.';
|
||||
|
||||
/**
|
||||
* the Command's usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'routes';
|
||||
|
||||
/**
|
||||
* the Command's Arguments
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $arguments = [];
|
||||
|
||||
/**
|
||||
* the Command's Options
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $options = [
|
||||
'-h' => 'Sort by Handler.',
|
||||
'--host' => 'Specify hostname in request URI.',
|
||||
];
|
||||
|
||||
/**
|
||||
* Displays the help for the spark cli script itself.
|
||||
*/
|
||||
public function run(array $params)
|
||||
{
|
||||
$sortByHandler = array_key_exists('h', $params);
|
||||
$host = $params['host'] ?? null;
|
||||
|
||||
// Set HTTP_HOST
|
||||
if ($host) {
|
||||
$request = service('request');
|
||||
$_SERVER = $request->getServer();
|
||||
$_SERVER['HTTP_HOST'] = $host;
|
||||
$request->setGlobal('server', $_SERVER);
|
||||
}
|
||||
|
||||
$collection = service('routes')->loadRoutes();
|
||||
|
||||
// Reset HTTP_HOST
|
||||
if ($host) {
|
||||
unset($_SERVER['HTTP_HOST']);
|
||||
}
|
||||
|
||||
$methods = Router::HTTP_METHODS;
|
||||
|
||||
$tbody = [];
|
||||
$uriGenerator = new SampleURIGenerator();
|
||||
$filterCollector = new FilterCollector();
|
||||
|
||||
$definedRouteCollector = new DefinedRouteCollector($collection);
|
||||
|
||||
foreach ($definedRouteCollector->collect() as $route) {
|
||||
$sampleUri = $uriGenerator->get($route['route']);
|
||||
$filters = $filterCollector->get($route['method'], $sampleUri);
|
||||
|
||||
$routeName = ($route['route'] === $route['name']) ? '»' : $route['name'];
|
||||
|
||||
$tbody[] = [
|
||||
strtoupper($route['method']),
|
||||
$route['route'],
|
||||
$routeName,
|
||||
$route['handler'],
|
||||
implode(' ', array_map(class_basename(...), $filters['before'])),
|
||||
implode(' ', array_map(class_basename(...), $filters['after'])),
|
||||
];
|
||||
}
|
||||
|
||||
if ($collection->shouldAutoRoute()) {
|
||||
$autoRoutesImproved = config(Feature::class)->autoRoutesImproved ?? false;
|
||||
|
||||
if ($autoRoutesImproved) {
|
||||
$autoRouteCollector = new AutoRouteCollectorImproved(
|
||||
$collection->getDefaultNamespace(),
|
||||
$collection->getDefaultController(),
|
||||
$collection->getDefaultMethod(),
|
||||
$methods,
|
||||
$collection->getRegisteredControllers('*')
|
||||
);
|
||||
|
||||
$autoRoutes = $autoRouteCollector->get();
|
||||
|
||||
// Check for Module Routes.
|
||||
if ($routingConfig = config(Routing::class)) {
|
||||
foreach ($routingConfig->moduleRoutes as $uri => $namespace) {
|
||||
$autoRouteCollector = new AutoRouteCollectorImproved(
|
||||
$namespace,
|
||||
$collection->getDefaultController(),
|
||||
$collection->getDefaultMethod(),
|
||||
$methods,
|
||||
$collection->getRegisteredControllers('*'),
|
||||
$uri
|
||||
);
|
||||
|
||||
$autoRoutes = [...$autoRoutes, ...$autoRouteCollector->get()];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$autoRouteCollector = new AutoRouteCollector(
|
||||
$collection->getDefaultNamespace(),
|
||||
$collection->getDefaultController(),
|
||||
$collection->getDefaultMethod()
|
||||
);
|
||||
|
||||
$autoRoutes = $autoRouteCollector->get();
|
||||
|
||||
foreach ($autoRoutes as &$routes) {
|
||||
// There is no `AUTO` method, but it is intentional not to get route filters.
|
||||
$filters = $filterCollector->get('AUTO', $uriGenerator->get($routes[1]));
|
||||
|
||||
$routes[] = implode(' ', array_map(class_basename(...), $filters['before']));
|
||||
$routes[] = implode(' ', array_map(class_basename(...), $filters['after']));
|
||||
}
|
||||
}
|
||||
|
||||
$tbody = [...$tbody, ...$autoRoutes];
|
||||
}
|
||||
|
||||
$thead = [
|
||||
'Method',
|
||||
'Route',
|
||||
'Name',
|
||||
$sortByHandler ? 'Handler ↓' : 'Handler',
|
||||
'Before Filters',
|
||||
'After Filters',
|
||||
];
|
||||
|
||||
// Sort by Handler.
|
||||
if ($sortByHandler) {
|
||||
usort($tbody, static fn ($handler1, $handler2) => strcmp($handler1[3], $handler2[3]));
|
||||
}
|
||||
|
||||
if ($host) {
|
||||
CLI::write('Host: ' . $host);
|
||||
}
|
||||
|
||||
CLI::table($tbody, $thead);
|
||||
|
||||
$this->showRequiredFilters();
|
||||
}
|
||||
|
||||
private function showRequiredFilters(): void
|
||||
{
|
||||
$filterCollector = new FilterCollector();
|
||||
|
||||
$required = $filterCollector->getRequiredFilters();
|
||||
|
||||
$filters = [];
|
||||
|
||||
foreach ($required['before'] as $filter) {
|
||||
$filters[] = CLI::color($filter, 'yellow');
|
||||
}
|
||||
|
||||
CLI::write('Required Before Filters: ' . implode(', ', $filters));
|
||||
|
||||
$filters = [];
|
||||
|
||||
foreach ($required['after'] as $filter) {
|
||||
$filters[] = CLI::color($filter, 'yellow');
|
||||
}
|
||||
|
||||
CLI::write(' Required After Filters: ' . implode(', ', $filters));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* 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\Commands\Utilities\Routes;
|
||||
|
||||
/**
|
||||
* Collects data for auto route listing.
|
||||
*
|
||||
* @see \CodeIgniter\Commands\Utilities\Routes\AutoRouteCollectorTest
|
||||
*/
|
||||
final class AutoRouteCollector
|
||||
{
|
||||
/**
|
||||
* @param string $namespace namespace to search
|
||||
*/
|
||||
public function __construct(private readonly string $namespace, private readonly string $defaultController, private readonly string $defaultMethod)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<list<string>>
|
||||
*/
|
||||
public function get(): array
|
||||
{
|
||||
$finder = new ControllerFinder($this->namespace);
|
||||
$reader = new ControllerMethodReader($this->namespace);
|
||||
|
||||
$tbody = [];
|
||||
|
||||
foreach ($finder->find() as $class) {
|
||||
$output = $reader->read(
|
||||
$class,
|
||||
$this->defaultController,
|
||||
$this->defaultMethod
|
||||
);
|
||||
|
||||
foreach ($output as $item) {
|
||||
$tbody[] = [
|
||||
'auto',
|
||||
$item['route'],
|
||||
'',
|
||||
$item['handler'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $tbody;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* 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\Commands\Utilities\Routes\AutoRouterImproved;
|
||||
|
||||
use CodeIgniter\Commands\Utilities\Routes\ControllerFinder;
|
||||
use CodeIgniter\Commands\Utilities\Routes\FilterCollector;
|
||||
|
||||
/**
|
||||
* Collects data for Auto Routing Improved.
|
||||
*
|
||||
* @see \CodeIgniter\Commands\Utilities\Routes\AutoRouterImproved\AutoRouteCollectorTest
|
||||
*/
|
||||
final class AutoRouteCollector
|
||||
{
|
||||
/**
|
||||
* @param string $namespace namespace to search
|
||||
* @param list<class-string> $protectedControllers List of controllers in Defined
|
||||
* Routes that should not be accessed via Auto-Routing.
|
||||
* @param list<string> $httpMethods
|
||||
* @param string $prefix URI prefix for Module Routing
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly string $namespace,
|
||||
private readonly string $defaultController,
|
||||
private readonly string $defaultMethod,
|
||||
private readonly array $httpMethods,
|
||||
private readonly array $protectedControllers,
|
||||
private string $prefix = ''
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<list<string>>
|
||||
*/
|
||||
public function get(): array
|
||||
{
|
||||
$finder = new ControllerFinder($this->namespace);
|
||||
$reader = new ControllerMethodReader($this->namespace, $this->httpMethods);
|
||||
|
||||
$tbody = [];
|
||||
|
||||
foreach ($finder->find() as $class) {
|
||||
// Exclude controllers in Defined Routes.
|
||||
if (in_array('\\' . $class, $this->protectedControllers, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$routes = $reader->read(
|
||||
$class,
|
||||
$this->defaultController,
|
||||
$this->defaultMethod
|
||||
);
|
||||
|
||||
if ($routes === []) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$routes = $this->addFilters($routes);
|
||||
|
||||
foreach ($routes as $item) {
|
||||
$route = $item['route'] . $item['route_params'];
|
||||
|
||||
// For module routing
|
||||
if ($this->prefix !== '' && $route === '/') {
|
||||
$route = $this->prefix;
|
||||
} elseif ($this->prefix !== '') {
|
||||
$route = $this->prefix . '/' . $route;
|
||||
}
|
||||
|
||||
$tbody[] = [
|
||||
strtoupper($item['method']) . '(auto)',
|
||||
$route,
|
||||
'',
|
||||
$item['handler'],
|
||||
$item['before'],
|
||||
$item['after'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $tbody;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adding Filters
|
||||
*
|
||||
* @param list<array<string, array|string>> $routes
|
||||
*
|
||||
* @return list<array<string, array|string>>
|
||||
*/
|
||||
private function addFilters($routes)
|
||||
{
|
||||
$filterCollector = new FilterCollector(true);
|
||||
|
||||
foreach ($routes as &$route) {
|
||||
$routePath = $route['route'];
|
||||
|
||||
// For module routing
|
||||
if ($this->prefix !== '' && $route === '/') {
|
||||
$routePath = $this->prefix;
|
||||
} elseif ($this->prefix !== '') {
|
||||
$routePath = $this->prefix . '/' . $routePath;
|
||||
}
|
||||
|
||||
// Search filters for the URI with all params
|
||||
$sampleUri = $this->generateSampleUri($route);
|
||||
$filtersLongest = $filterCollector->get($route['method'], $routePath . $sampleUri);
|
||||
|
||||
// Search filters for the URI without optional params
|
||||
$sampleUri = $this->generateSampleUri($route, false);
|
||||
$filtersShortest = $filterCollector->get($route['method'], $routePath . $sampleUri);
|
||||
|
||||
// Get common array elements
|
||||
$filters['before'] = array_intersect($filtersLongest['before'], $filtersShortest['before']);
|
||||
$filters['after'] = array_intersect($filtersLongest['after'], $filtersShortest['after']);
|
||||
|
||||
$route['before'] = implode(' ', array_map(class_basename(...), $filters['before']));
|
||||
$route['after'] = implode(' ', array_map(class_basename(...), $filters['after']));
|
||||
}
|
||||
|
||||
return $routes;
|
||||
}
|
||||
|
||||
private function generateSampleUri(array $route, bool $longest = true): string
|
||||
{
|
||||
$sampleUri = '';
|
||||
|
||||
if (isset($route['params'])) {
|
||||
$i = 1;
|
||||
|
||||
foreach ($route['params'] as $required) {
|
||||
if ($longest && ! $required) {
|
||||
$sampleUri .= '/' . $i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $sampleUri;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* 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\Commands\Utilities\Routes\AutoRouterImproved;
|
||||
|
||||
use Config\Routing;
|
||||
use ReflectionClass;
|
||||
use ReflectionMethod;
|
||||
|
||||
/**
|
||||
* Reads a controller and returns a list of auto route listing.
|
||||
*
|
||||
* @see \CodeIgniter\Commands\Utilities\Routes\AutoRouterImproved\ControllerMethodReaderTest
|
||||
*/
|
||||
final class ControllerMethodReader
|
||||
{
|
||||
private readonly bool $translateURIDashes;
|
||||
private readonly bool $translateUriToCamelCase;
|
||||
|
||||
/**
|
||||
* @param string $namespace the default namespace
|
||||
* @param list<string> $httpMethods
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly string $namespace,
|
||||
private readonly array $httpMethods
|
||||
) {
|
||||
$config = config(Routing::class);
|
||||
$this->translateURIDashes = $config->translateURIDashes;
|
||||
$this->translateUriToCamelCase = $config->translateUriToCamelCase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns found route info in the controller.
|
||||
*
|
||||
* @param class-string $class
|
||||
*
|
||||
* @return list<array<string, array|string>>
|
||||
*/
|
||||
public function read(string $class, string $defaultController = 'Home', string $defaultMethod = 'index'): array
|
||||
{
|
||||
$reflection = new ReflectionClass($class);
|
||||
|
||||
if ($reflection->isAbstract()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$classname = $reflection->getName();
|
||||
$classShortname = $reflection->getShortName();
|
||||
|
||||
$output = [];
|
||||
$classInUri = $this->convertClassNameToUri($classname);
|
||||
|
||||
foreach ($reflection->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
|
||||
$methodName = $method->getName();
|
||||
|
||||
foreach ($this->httpMethods as $httpVerb) {
|
||||
if (str_starts_with($methodName, strtolower($httpVerb))) {
|
||||
// Remove HTTP verb prefix.
|
||||
$methodInUri = $this->convertMethodNameToUri($httpVerb, $methodName);
|
||||
|
||||
// Check if it is the default method.
|
||||
if ($methodInUri === $defaultMethod) {
|
||||
$routeForDefaultController = $this->getRouteForDefaultController(
|
||||
$classShortname,
|
||||
$defaultController,
|
||||
$classInUri,
|
||||
$classname,
|
||||
$methodName,
|
||||
$httpVerb,
|
||||
$method
|
||||
);
|
||||
|
||||
if ($routeForDefaultController !== []) {
|
||||
// The controller is the default controller. It only
|
||||
// has a route for the default method. Other methods
|
||||
// will not be routed even if they exist.
|
||||
$output = [...$output, ...$routeForDefaultController];
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
[$params, $routeParams] = $this->getParameters($method);
|
||||
|
||||
// Route for the default method.
|
||||
$output[] = [
|
||||
'method' => $httpVerb,
|
||||
'route' => $classInUri,
|
||||
'route_params' => $routeParams,
|
||||
'handler' => '\\' . $classname . '::' . $methodName,
|
||||
'params' => $params,
|
||||
];
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$route = $classInUri . '/' . $methodInUri;
|
||||
|
||||
[$params, $routeParams] = $this->getParameters($method);
|
||||
|
||||
// If it is the default controller, the method will not be
|
||||
// routed.
|
||||
if ($classShortname === $defaultController) {
|
||||
$route = 'x ' . $route;
|
||||
}
|
||||
|
||||
$output[] = [
|
||||
'method' => $httpVerb,
|
||||
'route' => $route,
|
||||
'route_params' => $routeParams,
|
||||
'handler' => '\\' . $classname . '::' . $methodName,
|
||||
'params' => $params,
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
private function getParameters(ReflectionMethod $method): array
|
||||
{
|
||||
$params = [];
|
||||
$routeParams = '';
|
||||
$refParams = $method->getParameters();
|
||||
|
||||
foreach ($refParams as $param) {
|
||||
$required = true;
|
||||
if ($param->isOptional()) {
|
||||
$required = false;
|
||||
|
||||
$routeParams .= '[/..]';
|
||||
} else {
|
||||
$routeParams .= '/..';
|
||||
}
|
||||
|
||||
// [variable_name => required?]
|
||||
$params[$param->getName()] = $required;
|
||||
}
|
||||
|
||||
return [$params, $routeParams];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param class-string $classname
|
||||
*
|
||||
* @return string URI path part from the folder(s) and controller
|
||||
*/
|
||||
private function convertClassNameToUri(string $classname): string
|
||||
{
|
||||
// remove the namespace
|
||||
$pattern = '/' . preg_quote($this->namespace, '/') . '/';
|
||||
$class = ltrim(preg_replace($pattern, '', $classname), '\\');
|
||||
|
||||
$classParts = explode('\\', $class);
|
||||
$classPath = '';
|
||||
|
||||
foreach ($classParts as $part) {
|
||||
// make the first letter lowercase, because auto routing makes
|
||||
// the URI path's first letter uppercase and search the controller
|
||||
$classPath .= lcfirst($part) . '/';
|
||||
}
|
||||
|
||||
$classUri = rtrim($classPath, '/');
|
||||
|
||||
return $this->translateToUri($classUri);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string URI path part from the method
|
||||
*/
|
||||
private function convertMethodNameToUri(string $httpVerb, string $methodName): string
|
||||
{
|
||||
$methodUri = lcfirst(substr($methodName, strlen($httpVerb)));
|
||||
|
||||
return $this->translateToUri($methodUri);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $string classname or method name
|
||||
*/
|
||||
private function translateToUri(string $string): string
|
||||
{
|
||||
if ($this->translateUriToCamelCase) {
|
||||
$string = strtolower(
|
||||
preg_replace('/([a-z\d])([A-Z])/', '$1-$2', $string)
|
||||
);
|
||||
} elseif ($this->translateURIDashes) {
|
||||
$string = str_replace('_', '-', $string);
|
||||
}
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a route for the default controller.
|
||||
*
|
||||
* @return list<array>
|
||||
*/
|
||||
private function getRouteForDefaultController(
|
||||
string $classShortname,
|
||||
string $defaultController,
|
||||
string $uriByClass,
|
||||
string $classname,
|
||||
string $methodName,
|
||||
string $httpVerb,
|
||||
ReflectionMethod $method
|
||||
): array {
|
||||
$output = [];
|
||||
|
||||
if ($classShortname === $defaultController) {
|
||||
$pattern = '#' . preg_quote(lcfirst($defaultController), '#') . '\z#';
|
||||
$routeWithoutController = rtrim(preg_replace($pattern, '', $uriByClass), '/');
|
||||
$routeWithoutController = $routeWithoutController ?: '/';
|
||||
|
||||
[$params, $routeParams] = $this->getParameters($method);
|
||||
|
||||
if ($routeWithoutController === '/' && $routeParams !== '') {
|
||||
$routeWithoutController = '';
|
||||
}
|
||||
|
||||
$output[] = [
|
||||
'method' => $httpVerb,
|
||||
'route' => $routeWithoutController,
|
||||
'route_params' => $routeParams,
|
||||
'handler' => '\\' . $classname . '::' . $methodName,
|
||||
'params' => $params,
|
||||
];
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* 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\Commands\Utilities\Routes;
|
||||
|
||||
use CodeIgniter\Autoloader\FileLocatorInterface;
|
||||
|
||||
/**
|
||||
* Finds all controllers in a namespace for auto route listing.
|
||||
*
|
||||
* @see \CodeIgniter\Commands\Utilities\Routes\ControllerFinderTest
|
||||
*/
|
||||
final class ControllerFinder
|
||||
{
|
||||
private readonly FileLocatorInterface $locator;
|
||||
|
||||
/**
|
||||
* @param string $namespace namespace to search
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly string $namespace
|
||||
) {
|
||||
$this->locator = service('locator');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<class-string>
|
||||
*/
|
||||
public function find(): array
|
||||
{
|
||||
$nsArray = explode('\\', trim($this->namespace, '\\'));
|
||||
$count = count($nsArray);
|
||||
$ns = '';
|
||||
$files = [];
|
||||
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$ns .= '\\' . array_shift($nsArray);
|
||||
$path = implode('\\', $nsArray);
|
||||
|
||||
$files = $this->locator->listNamespaceFiles($ns, $path);
|
||||
|
||||
if ($files !== []) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$classes = [];
|
||||
|
||||
foreach ($files as $file) {
|
||||
if (\is_file($file)) {
|
||||
$classnameOrEmpty = $this->locator->getClassname($file);
|
||||
|
||||
if ($classnameOrEmpty !== '') {
|
||||
/** @var class-string $classname */
|
||||
$classname = $classnameOrEmpty;
|
||||
|
||||
$classes[] = $classname;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $classes;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* 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\Commands\Utilities\Routes;
|
||||
|
||||
use ReflectionClass;
|
||||
use ReflectionMethod;
|
||||
|
||||
/**
|
||||
* Reads a controller and returns a list of auto route listing.
|
||||
*
|
||||
* @see \CodeIgniter\Commands\Utilities\Routes\ControllerMethodReaderTest
|
||||
*/
|
||||
final class ControllerMethodReader
|
||||
{
|
||||
/**
|
||||
* @param string $namespace the default namespace
|
||||
*/
|
||||
public function __construct(private readonly string $namespace)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param class-string $class
|
||||
*
|
||||
* @return list<array{route: string, handler: string}>
|
||||
*/
|
||||
public function read(string $class, string $defaultController = 'Home', string $defaultMethod = 'index'): array
|
||||
{
|
||||
$reflection = new ReflectionClass($class);
|
||||
|
||||
if ($reflection->isAbstract()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$classname = $reflection->getName();
|
||||
$classShortname = $reflection->getShortName();
|
||||
|
||||
$output = [];
|
||||
$uriByClass = $this->getUriByClass($classname);
|
||||
|
||||
if ($this->hasRemap($reflection)) {
|
||||
$methodName = '_remap';
|
||||
|
||||
$routeWithoutController = $this->getRouteWithoutController(
|
||||
$classShortname,
|
||||
$defaultController,
|
||||
$uriByClass,
|
||||
$classname,
|
||||
$methodName
|
||||
);
|
||||
$output = [...$output, ...$routeWithoutController];
|
||||
|
||||
$output[] = [
|
||||
'route' => $uriByClass . '[/...]',
|
||||
'handler' => '\\' . $classname . '::' . $methodName,
|
||||
];
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
foreach ($reflection->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
|
||||
$methodName = $method->getName();
|
||||
|
||||
$route = $uriByClass . '/' . $methodName;
|
||||
|
||||
// Exclude BaseController and initController
|
||||
// See system/Config/Routes.php
|
||||
if (preg_match('#\AbaseController.*#', $route) === 1) {
|
||||
continue;
|
||||
}
|
||||
if (preg_match('#.*/initController\z#', $route) === 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($methodName === $defaultMethod) {
|
||||
$routeWithoutController = $this->getRouteWithoutController(
|
||||
$classShortname,
|
||||
$defaultController,
|
||||
$uriByClass,
|
||||
$classname,
|
||||
$methodName
|
||||
);
|
||||
$output = [...$output, ...$routeWithoutController];
|
||||
|
||||
$output[] = [
|
||||
'route' => $uriByClass,
|
||||
'handler' => '\\' . $classname . '::' . $methodName,
|
||||
];
|
||||
}
|
||||
|
||||
$output[] = [
|
||||
'route' => $route . '[/...]',
|
||||
'handler' => '\\' . $classname . '::' . $methodName,
|
||||
];
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the class has a _remap() method.
|
||||
*/
|
||||
private function hasRemap(ReflectionClass $class): bool
|
||||
{
|
||||
if ($class->hasMethod('_remap')) {
|
||||
$remap = $class->getMethod('_remap');
|
||||
|
||||
return $remap->isPublic();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param class-string $classname
|
||||
*
|
||||
* @return string URI path part from the folder(s) and controller
|
||||
*/
|
||||
private function getUriByClass(string $classname): string
|
||||
{
|
||||
// remove the namespace
|
||||
$pattern = '/' . preg_quote($this->namespace, '/') . '/';
|
||||
$class = ltrim(preg_replace($pattern, '', $classname), '\\');
|
||||
|
||||
$classParts = explode('\\', $class);
|
||||
$classPath = '';
|
||||
|
||||
foreach ($classParts as $part) {
|
||||
// make the first letter lowercase, because auto routing makes
|
||||
// the URI path's first letter uppercase and search the controller
|
||||
$classPath .= lcfirst($part) . '/';
|
||||
}
|
||||
|
||||
return rtrim($classPath, '/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a route without default controller.
|
||||
*/
|
||||
private function getRouteWithoutController(
|
||||
string $classShortname,
|
||||
string $defaultController,
|
||||
string $uriByClass,
|
||||
string $classname,
|
||||
string $methodName
|
||||
): array {
|
||||
if ($classShortname !== $defaultController) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$pattern = '#' . preg_quote(lcfirst($defaultController), '#') . '\z#';
|
||||
$routeWithoutController = rtrim(preg_replace($pattern, '', $uriByClass), '/');
|
||||
$routeWithoutController = $routeWithoutController ?: '/';
|
||||
|
||||
return [[
|
||||
'route' => $routeWithoutController,
|
||||
'handler' => '\\' . $classname . '::' . $methodName,
|
||||
]];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* 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\Commands\Utilities\Routes;
|
||||
|
||||
use CodeIgniter\Config\Services;
|
||||
use CodeIgniter\Filters\Filters;
|
||||
use CodeIgniter\HTTP\Method;
|
||||
use CodeIgniter\HTTP\Request;
|
||||
use CodeIgniter\Router\Router;
|
||||
use Config\Filters as FiltersConfig;
|
||||
|
||||
/**
|
||||
* Collects filters for a route.
|
||||
*
|
||||
* @see \CodeIgniter\Commands\Utilities\Routes\FilterCollectorTest
|
||||
*/
|
||||
final class FilterCollector
|
||||
{
|
||||
public function __construct(
|
||||
/**
|
||||
* Whether to reset Defined Routes.
|
||||
*
|
||||
* If set to true, route filters are not found.
|
||||
*/
|
||||
private readonly bool $resetRoutes = false
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns filters for the URI
|
||||
*
|
||||
* @param string $method HTTP verb like `GET`,`POST` or `CLI`.
|
||||
* @param string $uri URI path to find filters for
|
||||
*
|
||||
* @return array{before: list<string>, after: list<string>} array of filter alias or classname
|
||||
*/
|
||||
public function get(string $method, string $uri): array
|
||||
{
|
||||
if ($method === strtolower($method)) {
|
||||
@trigger_error(
|
||||
'Passing lowercase HTTP method "' . $method . '" is deprecated.'
|
||||
. ' Use uppercase HTTP method like "' . strtoupper($method) . '".',
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 4.5.0
|
||||
* @TODO Remove this in the future.
|
||||
*/
|
||||
$method = strtoupper($method);
|
||||
|
||||
if ($method === 'CLI') {
|
||||
return [
|
||||
'before' => [],
|
||||
'after' => [],
|
||||
];
|
||||
}
|
||||
|
||||
$request = Services::incomingrequest(null, false);
|
||||
$request->setMethod($method);
|
||||
|
||||
$router = $this->createRouter($request);
|
||||
$filters = $this->createFilters($request);
|
||||
|
||||
$finder = new FilterFinder($router, $filters);
|
||||
|
||||
return $finder->find($uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Required Filters
|
||||
*
|
||||
* @return array{before: list<string>, after: list<string>} array of filter alias or classname
|
||||
*/
|
||||
public function getRequiredFilters(): array
|
||||
{
|
||||
$request = Services::incomingrequest(null, false);
|
||||
$request->setMethod(Method::GET);
|
||||
|
||||
$router = $this->createRouter($request);
|
||||
$filters = $this->createFilters($request);
|
||||
|
||||
$finder = new FilterFinder($router, $filters);
|
||||
|
||||
return $finder->getRequiredFilters();
|
||||
}
|
||||
|
||||
private function createRouter(Request $request): Router
|
||||
{
|
||||
$routes = service('routes');
|
||||
|
||||
if ($this->resetRoutes) {
|
||||
$routes->resetRoutes();
|
||||
}
|
||||
|
||||
return new Router($routes, $request);
|
||||
}
|
||||
|
||||
private function createFilters(Request $request): Filters
|
||||
{
|
||||
$config = config(FiltersConfig::class);
|
||||
|
||||
return new Filters($config, $request, service('response'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* 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\Commands\Utilities\Routes;
|
||||
|
||||
use CodeIgniter\Exceptions\PageNotFoundException;
|
||||
use CodeIgniter\Filters\Filters;
|
||||
use CodeIgniter\HTTP\Exceptions\BadRequestException;
|
||||
use CodeIgniter\HTTP\Exceptions\RedirectException;
|
||||
use CodeIgniter\Router\Router;
|
||||
use Config\Feature;
|
||||
|
||||
/**
|
||||
* Finds filters.
|
||||
*
|
||||
* @see \CodeIgniter\Commands\Utilities\Routes\FilterFinderTest
|
||||
*/
|
||||
final class FilterFinder
|
||||
{
|
||||
private readonly Router $router;
|
||||
private readonly Filters $filters;
|
||||
|
||||
public function __construct(?Router $router = null, ?Filters $filters = null)
|
||||
{
|
||||
$this->router = $router ?? service('router');
|
||||
$this->filters = $filters ?? service('filters');
|
||||
}
|
||||
|
||||
private function getRouteFilters(string $uri): array
|
||||
{
|
||||
$this->router->handle($uri);
|
||||
|
||||
return $this->router->getFilters();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $uri URI path to find filters for
|
||||
*
|
||||
* @return array{before: list<string>, after: list<string>} array of filter alias or classname
|
||||
*/
|
||||
public function find(string $uri): array
|
||||
{
|
||||
$this->filters->reset();
|
||||
|
||||
// Add route filters
|
||||
try {
|
||||
$routeFilters = $this->getRouteFilters($uri);
|
||||
|
||||
$this->filters->enableFilters($routeFilters, 'before');
|
||||
|
||||
$oldFilterOrder = config(Feature::class)->oldFilterOrder ?? false;
|
||||
if (! $oldFilterOrder) {
|
||||
$routeFilters = array_reverse($routeFilters);
|
||||
}
|
||||
|
||||
$this->filters->enableFilters($routeFilters, 'after');
|
||||
|
||||
$this->filters->initialize($uri);
|
||||
|
||||
return $this->filters->getFilters();
|
||||
} catch (RedirectException) {
|
||||
return [
|
||||
'before' => [],
|
||||
'after' => [],
|
||||
];
|
||||
} catch (BadRequestException|PageNotFoundException) {
|
||||
return [
|
||||
'before' => ['<unknown>'],
|
||||
'after' => ['<unknown>'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Required Filters
|
||||
*
|
||||
* @return array{before: list<string>, after:list<string>}
|
||||
*/
|
||||
public function getRequiredFilters(): array
|
||||
{
|
||||
[$requiredBefore] = $this->filters->getRequiredFilters('before');
|
||||
[$requiredAfter] = $this->filters->getRequiredFilters('after');
|
||||
|
||||
return [
|
||||
'before' => $requiredBefore,
|
||||
'after' => $requiredAfter,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* 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\Commands\Utilities\Routes;
|
||||
|
||||
use CodeIgniter\Router\RouteCollection;
|
||||
use Config\App;
|
||||
|
||||
/**
|
||||
* Generate a sample URI path from route key regex.
|
||||
*
|
||||
* @see \CodeIgniter\Commands\Utilities\Routes\SampleURIGeneratorTest
|
||||
*/
|
||||
final class SampleURIGenerator
|
||||
{
|
||||
private readonly RouteCollection $routes;
|
||||
|
||||
/**
|
||||
* Sample URI path for placeholder.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
private array $samples = [
|
||||
'any' => '123/abc',
|
||||
'segment' => 'abc_123',
|
||||
'alphanum' => 'abc123',
|
||||
'num' => '123',
|
||||
'alpha' => 'abc',
|
||||
'hash' => 'abc_123',
|
||||
];
|
||||
|
||||
public function __construct(?RouteCollection $routes = null)
|
||||
{
|
||||
$this->routes = $routes ?? service('routes');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $routeKey route key regex
|
||||
*
|
||||
* @return string sample URI path
|
||||
*/
|
||||
public function get(string $routeKey): string
|
||||
{
|
||||
$sampleUri = $routeKey;
|
||||
|
||||
if (str_contains($routeKey, '{locale}')) {
|
||||
$sampleUri = str_replace(
|
||||
'{locale}',
|
||||
config(App::class)->defaultLocale,
|
||||
$routeKey
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($this->routes->getPlaceholders() as $placeholder => $regex) {
|
||||
$sample = $this->samples[$placeholder] ?? '::unknown::';
|
||||
|
||||
$sampleUri = str_replace('(' . $regex . ')', $sample, $sampleUri);
|
||||
}
|
||||
|
||||
// auto route
|
||||
return str_replace('[/...]', '/1/2/3/4/5', $sampleUri);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user