diff --git a/axm b/axm index e184298..d22a13b 100644 --- a/axm +++ b/axm @@ -12,7 +12,9 @@ if (PHP_SAPI !== 'cli') { exit("This script can only be run from the command line!\n\n"); } -require_once __DIR__ . DIRECTORY_SEPARATOR . 'core' . DIRECTORY_SEPARATOR . 'bootstrap.php'; +require_once dirname(__DIR__) . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'axm' . + DIRECTORY_SEPARATOR . 'framework' . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . + 'bootstrap.php'; //The console application instance. $console = app()->createConsoleApplication(); diff --git a/composer.json b/composer.json index 78844e9..c033830 100644 --- a/composer.json +++ b/composer.json @@ -25,8 +25,7 @@ "ext-zip": "*", "ext-mbstring": "*", "ext-intl": "*", - "filp/whoops": "^2.15.4", - "illuminate/database": "^10.45.1" + "axm/framework": "^1.0" }, "require-dev": { "symfony/var-dumper": "^6.3.5" diff --git a/core/App.php b/core/App.php deleted file mode 100644 index a4d174f..0000000 --- a/core/App.php +++ /dev/null @@ -1,302 +0,0 @@ - - * @package Framework - */ -final class App extends Container -{ - protected $class = [ - 'app' => App::class, - 'config' => Config::class, - 'session' => Session\Session::class, - 'request' => Http\Request::class, - 'response' => Http\Response::class, - 'router' => Http\Router::class, - 'view' => Views\View::class, - 'controller' => App\Controllers\BaseController::class, - 'database' => Database\Database::class, - 'validator' => Validation\Validator::class, - 'auth' => Auth\Auth::class, - 'console' => Console\ConsoleApplication::class, - // 'event' => EventManager::class, - // 'cache' => Cache\Cache::class, - ]; - - /** - * Constructor for the Application class. - */ - public function __construct() - { - $this->setApp(); - $this->loadEnv(); - $this->registerProviders(); - $this->bootServices(); - } - - /** - * Set instance app - */ - private function setApp() - { - $this->singleton('app', fn () => $this); - } - - /** - * Open the .env file - */ - private function loadEnv(): void - { - Env::load(ROOT_PATH . DIRECTORY_SEPARATOR . '.env'); - } - - /** - * Register service providers. - * - * This method reads the service provider configurations from the `providers.php` file - * and registers them with the application container. - */ - public function registerProviders() - { - $pathConfig = config('paths.providersPath') . DIRECTORY_SEPARATOR; - $providers = include $pathConfig . 'providers.php'; - $this->services(array_merge($this->class, $providers)); - } - - /** - * Boot the services specified in the 'boot' configuration array. - */ - public function bootServices() - { - $services = config('boot'); - $method = 'boot'; - - foreach ($services ?? [] as $class) { - $class::$method; - } - } - - /** - * Check if the application is in production mode. - * @return bool True if the application is in production mode, false otherwise. - */ - public function isProduction(): bool - { - return env('APP_ENVIRONMENT') === 'production'; - } - - /** - * Get the current application environment. - */ - public function getEnvironment() - { - return env('APP_ENVIRONMENT', 'production'); - } - - /** - * Check if the user is logged in. - */ - public function isLogged(): bool - { - return !empty($this->user); - } - - /** - * Set the user from the session variable. - */ - private function getUser(): void - { - $this->user = function () { - return $this->session->get('user', true); - }; - } - - /** - * Set the user from the session variable. - */ - private function setUser(): void - { - $this->user = function () { - return $this->session->set('user', true); - }; - } - - /** - * Attempts to log in a user based on provided data. - * - * @param array|array[] $fields An array or nested arrays containing the fields to use for the database query. - * @param array|array[] $values An array or nested arrays containing the corresponding values to match in the database query. - * @param callable|null $callback A callback function to execute upon successful login. - * @return bool Returns true if the login is successful, false otherwise. - * @throws \Exception Throws an exception in case of an error during the login process. - */ - public function login(): bool - { - $instance = new Auth\Auth($this); - return $instance->resolverLogin(...func_get_args()); - } - - /** - * Log out the user. - * @param string $path The optional path to redirect after logout. - */ - public function logout(string $path = '/'): void - { - $this->session->flush(); - $this->response->redirect($path); - } - - /** - * Returns the time zone used by this application. - * @see http://php.net/manual/en/function.date-default-timezone-get.php - */ - public function getTimeZone(): string - { - return date_default_timezone_get(); - } - - /** - * Sets the time zone used by this application. - * @see http://php.net/manual/en/function.date-default-timezone-set.php - */ - public function setTimeZone(string $timezone): void - { - date_default_timezone_set($timezone); - } - - /** - * Get the user's preferred locale based on the HTTP Accept-Language header. - */ - public function getLocale(): string|false - { - if (!extension_loaded('intl')) - throw new \Exception('The "Intl" extension is not enabled on this server'); - - $http = \Locale::acceptFromHttp($_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? ''); - $locale = !empty($http) ? $http : 'en_US'; - - return $locale; - } - - /** - * Generate security tokens, including CSRF tokens. - */ - private function generateTokens(): string - { - return bin2hex(random_bytes(64) . time()); - } - - /** - * Modify cookies for the CSRF token - */ - public function setCsrfCookie(string $csrfToken): void - { - $expiration = config('session.expiration'); - setcookie('csrfToken', $csrfToken, time() + 60 * $expiration); - } - - /** - * Get the CSRF token. If the token is not present in the cookie, generate and set a new one. - * @return string The generated or existing CSRF token. - */ - public function getCsrfToken(): string - { - return isset($_COOKIE['csrfToken']) ? $_COOKIE['csrfToken'] : $this->generateAndSetCsrfToken(); - } - - /** - * Generate a CSRF token and set it in the cookie. - * @return string The newly generated CSRF token. - */ - private function generateAndSetCsrfToken(): string - { - $csrfToken = $this->generateTokens(); - $this->setCsrfCookie($csrfToken); - - return $csrfToken; - } - - /** - * Check if the provided CSRF token matches the one in the session. - */ - public function hasCsrfToken(string $token): bool - { - return $_COOKIE['csrfToken'] === $token; - } - - /** - * Get the version of a specified library. - */ - public function version(string $libraryName = 'axm/axm'): ?string - { - $v = InstalledVersions::getVersion($libraryName); - return $v; - } - - /** - * Get the user or a specific user property. - */ - public function user(string $value = null) - { - if (is_null($value)) return $this->get('user'); - - return $this - ->get('user') - ->{$value} ?? null; - } - - /** - * Remove a service identified by its alias from the container. - * @param string $alias - */ - public function removeService(string $alias) - { - return $this->remove($alias); - } - - /** - * Magic method to dynamically retrieve properties. - * @return mixed The value of the retrieved property. - */ - public function __get(string $name) - { - if ($name == 'user') { - return $this->setUser(); - } - - return $this->get($name); - } - - /** - * Returns a new instance of the ConsoleApplication class - * @return ConsoleApplication A new instance of the ConsoleApplication class. - */ - public function createConsoleApplication() - { - static $console; - return $console ??= new ConsoleApplication($this); - } - - /** - * Run the application. - * - * This method triggers events before and after handling the request - * and dispatches the router. - */ - public function run(): void - { - (new Http\Router()) - ->openRoutesUser() - ->dispatch(); - } -} diff --git a/core/BaseModel.php b/core/BaseModel.php deleted file mode 100644 index f98c31b..0000000 --- a/core/BaseModel.php +++ /dev/null @@ -1,21 +0,0 @@ - - * @link http://www.axm.com/ - * @license http://www.axm.com/license/ - * @package Axm - * - * BaseModel provides a convenient place for loading components - * and performing functions that are needed by all your Models. - * Extend this class in any new Models: - * class User extends BaseModel - * - */ -class BaseModel extends Model -{ -} diff --git a/core/Config.php b/core/Config.php deleted file mode 100644 index 295fecc..0000000 --- a/core/Config.php +++ /dev/null @@ -1,92 +0,0 @@ - - */ - protected static $config = []; - - /** - * Get config - * - * @param string $var fichero.sección.variable - * @throws Exception - * @return mixed - */ - public static function get($var) - { - $sections = explode('.', $var); - self::$config[$sections[0]] ??= self::load($sections[0]); - - return match (count($sections)) { - 3 => self::$config[$sections[0]][$sections[1]][$sections[2]] ?? null, - 2 => self::$config[$sections[0]][$sections[1]] ?? null, - 1 => self::$config[$sections[0]] ?? null, - default => throw new \Exception('Máximo 3 niveles en Config::get(fichero.sección.variable), pedido: ' . $var) - }; - } - - /** - * Get all configs - * - * @return array - */ - public static function getAll() - { - return self::$config; - } - - /** - * Set variable in config - * - * @param string $var variable de configuración - * @param mixed $value valor para atributo - * @throws Exception - * @return void - */ - public static function set($var, $value) - { - $sections = explode('.', $var); - match (count($sections)) { - 3 => self::$config[$sections[0]][$sections[1]][$sections[2]] = $value, - 2 => self::$config[$sections[0]][$sections[1]] = $value, - 1 => self::$config[$sections[0]] = $value, - default => throw new \Exception('Máximo 3 niveles en Config::set(fichero.sección.variable), pedido: ' . $var) - }; - } - - /** - * Read config file - * - * @param string $file archivo .php o .ini - * @param bool $force forzar lectura de .php o .ini - * @return array - */ - public static function read($file, $force = false) - { - if ($force) { - return self::$config[$file] = self::load($file); - } - - return self::$config[$file] ??= self::load($file); - } - - /** - * Load config file - * - * @param string $file archivo - * @return array - */ - private static function load(string $file): array - { - if (is_file($fileConfig = APP_PATH . DIRECTORY_SEPARATOR - . 'config' . DIRECTORY_SEPARATOR . $file . '.php')) { - return require $fileConfig; - } - - throw new \Exception(sprintf('Error when opening the configuration file %s ', $fileConfig)); - } -} diff --git a/core/Container.php b/core/Container.php deleted file mode 100644 index ca1f2cf..0000000 --- a/core/Container.php +++ /dev/null @@ -1,154 +0,0 @@ -storage[$key] = $value; - return $value; - } - - public function singleton(string $key, callable $value) - { - $this->bind($key, function () use ($key, $value) { - static $instance; - $instance[$key] ??= $value($this); - - return $instance[$key]; - }); - - return $this; - } - - public function key(string $key) - { - if ($this->has($key)) { - return $key; - } - - throw new \Exception("Key '{$key}' not found in container."); - } - - public function resolve(string $key) - { - $resolver = $this->storage[$this->key($key)]; - - return $resolver($this); - } - - public function has(string $key): bool - { - return isset($this->storage[$key]); - } - - public function isSingleton(string $key): bool - { - return $this->has($key); - } - - public function get(string $key): object - { - if ($this->has($key)) { - $class = $this->storage[$key] ?? null; - - $instance = is_object($class) - ? $class - : $this->storage[$key] = new $class(); - - return $instance; - } - - return false; - } - - public function set(string $key, $value): void - { - $this->storage[$this->key($key)] = $value; - } - - public function services(array $values): void - { - $this->storage += $values; - } - - public function all(): array - { - return $this->storage; - } - - public function remove(string $key) - { - unset($this->storage[$this->key($key)]); - } - - public function clear() - { - $this->storage = []; - } - - /** - * Array Access - */ - public function offsetExists($offset) - { - return $this->has($offset); - } - - public function offsetGet($offset) - { - return $this->get($offset); - } - - public function offsetSet($offset, $value) - { - $this->set($offset, $value); - } - - public function offsetUnset($offset) - { - $this->remove($offset); - } - - public function count() - { - return count($this->storage); - } - - public function getIterator() - { - return new \ArrayIterator($this->storage); - } - - public function __get(string $key) - { - return $this->get($key); - } - - public function __set(string $key, $value) - { - $this->set($key, $value); - } - - public function __isset(string $key) - { - return $this->has($key); - } - - public function __unset(string $key) - { - $this->remove($key); - } -} diff --git a/core/Controller.php b/core/Controller.php deleted file mode 100644 index 3d1ce3f..0000000 --- a/core/Controller.php +++ /dev/null @@ -1,219 +0,0 @@ - - * @link http://www.axm.com/ - * @license http://www.axm.com/license/ - * @package Axm - */ -class Controller -{ - /** - * @var object|null The current user. - */ - protected ?object $user = null; - - /** - * @var string|null The layout to be used. - */ - protected ?string $layout = null; - - /** - * @var View|null The View instance. - */ - protected ?View $view = null; - - /** - * @var string The current action. - */ - protected string $action = ''; - - /** - * @var Request|null The Request instance. - */ - protected ?Request $request = null; - - /** - * @var Response|null The Response instance. - */ - protected ?Response $response = null; - - /** - * @var BaseMiddleware[]|null The array of middlewares. - */ - protected ?array $middlewares = []; - - /** - * Controller constructor. - */ - public function __construct() - { - $app = app(); - $this->request = app('request', new Request()); - $this->response = app('response', new Response()); - $this->view = app('response', new View()); - - $this->registerDefaultMiddleware(); - } - - /** - * Execute the registered middlewares. - * @return void - */ - private function registerDefaultMiddleware() - { - $middlewares = BaseMiddleware::$httpMiddlewares; - foreach ($middlewares as $middleware) { - if (is_subclass_of($middleware, BaseMiddleware::class)) { - $this->middlewares[] = new $middleware; - } - } - } - - /** - * Set the layout for the current controller. - * - * @param string $layout - * @return void - */ - public function setLayout(string $layout): void - { - $this->layout = $layout; - } - - /** - * Get the current layout. - * @return string - */ - public function getLayout(): string - { - return $this->layout; - } - - /** - * Set the path for the current view. - * - * @param string $path - * @return void - */ - public function setPathView(string $path) - { - $this->view::$viewPath = $path; - } - - /** - * Add an action to the controller. - * - * @param string|null $action - * @return void - */ - public function addAction(?string $action): void - { - $this->action = $action ?? ''; - } - - /** - * Get the current controller action. - * @return string - */ - public function getAction(): string - { - return $this->action; - } - - /** - * Render the view. - * - * @param string $view - * @param array|null $params - * @param bool $buffer - * @param string $ext - * @return string|null - */ - public function renderView(string $view, string|array $params = null, bool $withLayout = true, string $ext = '.php'): ?string - { - return $this->view->render($view, $ext)->withData($params)->withLayout($withLayout)->get(); - } - - /** - * Get the view object associated with this controller. - * @return View|null. - */ - public function view(): ?View - { - return $this->view; - } - - /** - * Register a middleware in the controller. - * - * @param BaseMiddleware $middleware - * @return void - */ - public function registerMiddleware(BaseMiddleware $middleware): void - { - $this->middlewares[] = $middleware; - } - - /** - * Get the registered middlewares. - * @return Middlewares\BaseMiddleware[] - */ - public function getMiddlewares(): array - { - return $this->middlewares; - } - - /** - * Register and execute the AuthMiddleware for access control to the specified actions. - * - * @param array $actions Actions requiring authorization. - * @param bool $allowedAction Indicates whether access to other actions than those specified is allowed. - * @return void - */ - public function accessControl(array $actions, bool $allowedAction = false) - { - $middleware = new AuthMiddleware($actions, $allowedAction); - $this->registerMiddleware($middleware); - } - - /** - * - */ - public function response(): ?Response - { - return $this->response; - } - - /** - * - */ - public function request(): ?Request - { - return $this->request; - } - - /** - * Handle calls to methods that do not exist. - * - * @param string $name - * @param array $arguments - * @throws RuntimeException - * @return void - */ - public function __call($name, $arguments) - { - throw new \RuntimeException(sprintf('Method [ %s ] does not exist', $name)); - } -} diff --git a/core/Env.php b/core/Env.php deleted file mode 100644 index 9382cbd..0000000 --- a/core/Env.php +++ /dev/null @@ -1,59 +0,0 @@ -$method(); - case 1: - return $instance->$method($args[0]); - case 2: - return $instance->$method($args[0], $args[1]); - default: - return call_user_func_array([$instance, $method], $args); - } - } -} diff --git a/core/HandlerErrors.php b/core/HandlerErrors.php deleted file mode 100644 index b3c5080..0000000 --- a/core/HandlerErrors.php +++ /dev/null @@ -1,108 +0,0 @@ -getMessage(); - $t_trace = "\ntrace: \n\n"; - $trace = $e->getTraceAsString(); - - $date = date('d-m-Y H:i:s'); - $axm_v = 1.0 ?? ''; - $php_v = PHP_VERSION; - $info = sprintf('Date: %s Axm Framework version: %s PHP version: %s', $date, $axm_v, $php_v); - $output = $title - . $t_trace - . $trace . PHP_EOL - . $info . PHP_EOL - . str_repeat('••', strlen($info)) . "\n\n\n"; - - return $output; -} - -/** - * Get the directory path for the error log file. - */ -function getDirectory() -{ - // Construct the full directory path for the error log file. - $dir = Config::get('paths.logsPath') . DIRECTORY_SEPARATOR . 'axm-errors.log'; - return $dir; -} - -function handleCliError($errno, $errstr, $errfile, $errline) -{ - $e = new ErrorException($errstr, $errno, 1, $errfile, $errline); - Console\CLIException::handleCLIException($e); - - $log = Config::get('app.initLogReportings'); - - if ($log === true) { - error_log((string) theme($e), 3, getDirectory()); - } - exit; -} - -function handleWebError($errno, $errstr, $errfile, $errline) -{ - $whoops = new Run(); - $handler = new PrettyPageHandler(); - $handler->setPageTitle("¡Oops! Ha ocurrido un error"); - $whoops->pushHandler($handler); - $whoops->handleError($errno, $errstr, $errfile, $errline); -} - -function handleCliException(Throwable $e) -{ - Console\CLIException::handleCLIException($e); - exit; -} - -function handleWebException(Throwable $e) -{ - $whoops = new Run(); - $handler = new PrettyPageHandler(); - $handler->setPageTitle("¡Oops! Ha ocurrido un error"); - $whoops->pushHandler($handler); - $whoops->handleException($e); -} diff --git a/core/Router(luego revisar).php b/core/Router(luego revisar).php deleted file mode 100644 index ffffb57..0000000 --- a/core/Router(luego revisar).php +++ /dev/null @@ -1,133 +0,0 @@ -prefix ??= $prefix ?? rtrim(dirname($_SERVER['SCRIPT_NAME']), '/'); - } - - public function addRoutes(array $routes): self - { - $this->routesMap += $routes; - return $this; - } - - /** - * This function, when called, will parse the URI, - * dispatch the appropriate controller and method - */ - public function run(): ?string - { - $uri = $this->parseUri(); - - $results = $this->parserControllerAndMethod($uri); - $httpMethod = $results[0]; - $controller = $results[1]; - $methodName = $results[2]; - - $controllerFile = $this->getControllerPath($controller); - - if (!is_file($controllerFile)) { - $this->handleNotFound($controllerFile); - } - - require $controllerFile; // include file - - $controllerClass = self::CONTROLLERS_NAMESPACE . $controller; - $instance = new $controllerClass(); - - if ($httpMethod === strtoupper($_SERVER['REQUEST_METHOD'])) { - call_user_func([$instance, $methodName], $_SERVER['REQUEST_METHOD']); - } - - return $uri; - } - - /** - * Parse the current uri from REQUEST_URI server variable. - */ - private function parseUri(): string - { - $uri = strtok(rawurldecode($_SERVER['REQUEST_URI']), '?'); - - // Delete the base path - if ($this->prefix !== '/') { - $uri = str_replace($this->prefix, '', $uri); - } - - return '/' . trim($uri, '/'); - } - - private function getControllerPath(string $controllerName): string - { - $path = Config::get('paths.controllersPath'); - return $path . DIRECTORY_SEPARATOR . $controllerName . '.php'; - } - - function parserControllerAndMethod(string $uri) - { - $result = $this->getHandler($uri); - if (count($result) !== 2) { - throw new \RuntimeException('Invalid handler: ' . implode(',', $result)); - } - - return $this->parserController($result); - } - - - function parserController($result) - { - $httpMethod = strtoupper($result[0]); - $controller = ucfirst($result[1]); - - if (is_string($controller) && str_contains($controller, '@')) { - $result = explode('@', $controller); - $controller = $result[0]; - $methodName = $result[1]; - } elseif(is_callable($controller)){ - call_user_func( $controller ); - }else { - $methodName = 'index'; - } - - return [$httpMethod, $controller, $methodName]; - } - - private function handleNotFound(string $controllerFile): void - { - about("Controlador no encontrado: [ $controllerFile ]"); - } - - private function getHandler(string $uri): array - { - return $this->routesMap[$uri] ?? $this->getDefaultHandler(); - } - - private function getDefaultHandler(): array - { - return [strtoupper($_SERVER['REQUEST_METHOD']), self::DEFAULT_CONTROLLER_NAME]; - } - - // Función para obtener la ruta base del proyecto - static function getBaseUrl() - { - // Obtener el nombre del archivo actual - $scriptName = $_SERVER['SCRIPT_NAME']; - // Obtener la carpeta en la que se encuentra el archivo actual - $scriptPath = dirname($scriptName); - // Obtener la URL base - $baseUrl = isset($_SERVER['HTTPS']) ? 'https' : 'http' . '://' . $_SERVER['HTTP_HOST'] . $scriptPath; - return $baseUrl; - } -} diff --git a/core/autoload.php b/core/autoload.php deleted file mode 100644 index 7d09a97..0000000 --- a/core/autoload.php +++ /dev/null @@ -1,34 +0,0 @@ - AXM_PATH . DIRECTORY_SEPARATOR . 'Container.php', - 'App' => AXM_PATH . DIRECTORY_SEPARATOR . 'App.php', - 'Config' => AXM_PATH . DIRECTORY_SEPARATOR . 'Config.php', - 'Env' => AXM_PATH . DIRECTORY_SEPARATOR . 'Env.php', - 'Facade' => AXM_PATH . DIRECTORY_SEPARATOR . 'Facade.php', - 'Config' => AXM_PATH . DIRECTORY_SEPARATOR . 'Config.php', - 'Controller' => AXM_PATH . DIRECTORY_SEPARATOR . 'Controller.php', - // 'Router' => AXM_PATH . DIRECTORY_SEPARATOR . 'Router.php', - 'BaseModel' => AXM_PATH . DIRECTORY_SEPARATOR . 'BaseModel.php', - ]; - - if (isset($classMap[$class])) { - include $classMap[$class]; - return; - } - - if (is_file(AXM_PATH . DIRECTORY_SEPARATOR . 'libraries' . DIRECTORY_SEPARATOR . $class . '.php')) { - include AXM_PATH . DIRECTORY_SEPARATOR . 'libraries' . DIRECTORY_SEPARATOR . $class . '.php'; - return; - } - - if (is_file(AXM_PATH . DIRECTORY_SEPARATOR . 'libraries' . $class . DIRECTORY_SEPARATOR . $class . '.php')) { - include AXM_PATH . DIRECTORY_SEPARATOR . 'libraries' . $class . DIRECTORY_SEPARATOR . $class . '.php'; - return; - } -} - -spl_autoload_register('axm_autoloader'); diff --git a/core/bootstrap.php b/core/bootstrap.php deleted file mode 100644 index 03ab8a2..0000000 --- a/core/bootstrap.php +++ /dev/null @@ -1,41 +0,0 @@ - - * @link http://www.axm.com/ - * @license http://www.axm.com/license/ - * @package Axm - * ------------------------------------------------------------------------------- - SETUP OUR PATH CONSTANTS --------------------------------------------------------------------------------*/ - -// Get the current directory -defined('ROOT_PATH') or define('ROOT_PATH', getcwd()); - -// Define the public path -const PUBLIC_PATH = ROOT_PATH . DIRECTORY_SEPARATOR . 'public'; - -// Defines the path of the dependencies -const VENDOR_PATH = ROOT_PATH . DIRECTORY_SEPARATOR . 'vendor'; - -// Define AXM framework installation path -const AXM_PATH = ROOT_PATH . DIRECTORY_SEPARATOR . 'core'; - -// Define the application path -const APP_PATH = ROOT_PATH . DIRECTORY_SEPARATOR . 'app'; - -// Defines the path for writing files -const STORAGE_PATH = ROOT_PATH . DIRECTORY_SEPARATOR . 'storage'; - -const APP_NAMESPACE = 'App\\'; - -/** --------------------------------------------------------------------------------- - FILES FOR INITIALIZATION --------------------------------------------------------------------------------- */ -require(ROOT_PATH . DIRECTORY_SEPARATOR . 'core' . DIRECTORY_SEPARATOR . 'autoload.php'); -require(ROOT_PATH . DIRECTORY_SEPARATOR . 'core' . DIRECTORY_SEPARATOR . 'HandlerErrors.php'); -require(ROOT_PATH . DIRECTORY_SEPARATOR . 'core' . DIRECTORY_SEPARATOR . 'functions.php'); -// Add Composer autoload to load external dependencies -require VENDOR_PATH . DIRECTORY_SEPARATOR . 'autoload.php'; diff --git a/core/functions.php b/core/functions.php deleted file mode 100644 index 6b1d9eb..0000000 --- a/core/functions.php +++ /dev/null @@ -1,890 +0,0 @@ -$alias; - } - - if ($alias !== null && $value !== null) { - return $instance->$alias = $value; - } - - return $instance; - } -} - -if (!function_exists('url')) { - - // Función para obtener la URL completa de una ruta relativa - function url(string $url = '') - { - $baseUrl = Router::getBaseUrl(); - return $baseUrl . '/' . trim($url, '/'); - } -} - -function env(string $params, string|bool $default = null) -{ - $env = Env::get($params, $default); - return $env ?? $default; -} - -if (!function_exists('is_cli')) { - - function is_cli(): bool - { - if (in_array(PHP_SAPI, ['cli', 'phpdbg'], true)) return true; - - return !isset($_SERVER['REMOTE_ADDR']) && !isset($_SERVER['REQUEST_METHOD']); - } -} - -if (!function_exists('extend')) { - - /** - * Extend the current View template with a layout. - * - * This function is used to extend the current View template with a layout template. - * It calls the 'extend' method of the 'View' class to specify the layout template to use. - * @param string $layout The name of the layout template to extend with. - * @return void - */ - function extend(string $layout) - { - // Call the 'extend' method of the 'View' class to specify the layout template. - return Views\View::extend($layout); - } -} - -if (!function_exists('memoize')) { - - /** - * Memoize the result of a callable function. - * - * This function is used to cache and reuse the results of a callable function for the same - * set of arguments. It returns a new callable function that stores and retrieves results from - * a cache based on argument values. - * @param callable $fn The callable function to memoize. - * @return callable A memoized version of the original callable function. - */ - function memoize(callable $fn): callable - { - $cache = []; - - // Return a new callable function that handles memoization. - return function (...$args) use ($fn, &$cache) { - // Generate a unique key based on the serialized arguments. - $key = sha1(serialize($args)); - - // Check if the result is already cached; if not, compute and cache it. - return $cache[$key] ?? ($cache[$key] = $fn(...$args)); - }; - } -} - -if (!function_exists('view')) { - - /** - * Render and display a View template. - * - * This function is used to render and display a View template within the application. - * @param string $view The name of the View template to render. - * @param mixed $params Optional data to pass to the View template (default is null). - * @param bool $buffer If true, the output is buffered; if false, it's immediately displayed (default is true). - * @param string $ext The file extension of the View template (default is '.php'). - * @return void - */ - function view(string $view, string|array $params = null, bool $show = false, bool $withLayout = false, string $ext = '.php') - { - return app('view', new View) - ->render($view, $ext) - ->withData($params) - ->withLayout($withLayout) - ->get(); - - // // Render the View template using the provided parameters. - // $output = app()->controller->renderView($view, $params, $withLayout, $ext); - - // if (!$show) { - // return $output; - // } - - // echo $output . PHP_EOL; - } -} - -if (!function_exists('section')) { - - /** - * Begin a new named section in a View template. - * - * This function is used to start a new named section within a View template. - * It calls the 'section' method of the 'View' class, allowing you to define content - * that can be yielded or included in other parts of the template. - * @param string $name The name of the section being started. - * @return void - */ - function section(string $name) - { - // Call the 'section' method of the 'View' class to begin a new named section. - return Views\View::section($name); - } -} - -if (!function_exists('endSection')) { - - /** - * End the current section in a View. - * - * This function is used to mark the end of a section within a View template. - * It calls the 'endSection' method of the 'View' class. - * @return void - */ - function endSection() - { - // Call the 'endSection' method of the 'View' class to mark the end of the section. - return Views\View::endSection(); - } -} - -if (!function_exists('partials')) { - - function partials(string $partial_name, array $data = []) - { - $partialsPath = config('paths.partialsPath'); // Make sure we have our paths set up! - - $partial_file = $partialsPath . DIRECTORY_SEPARATOR . $partial_name . '.php'; - $partials = app()->view->file($partial_file, $data); - return $partials; - } -} - -if (!function_exists('cleanInput')) { - - /** - * Sanitizes and cleans input data to prevent XSS attacks. - * - * @param mixed $data The data to be cleaned. - * @return mixed The cleaned data. - */ - function cleanInput($data) - { - return match (true) { - is_array($data) => array_map('cleanInput', $data), - is_object($data) => cleanInput((array) $data), - is_email($data) => filter_var($data, FILTER_SANITIZE_EMAIL), - is_url($data) => filter_var($data, FILTER_SANITIZE_URL), - is_ip($data) => filter_var($data, FILTER_VALIDATE_IP), - is_string($data) => preg_replace('/[\x00-\x1F\x7F]/u', '', filter_var(trim($data), FILTER_SANITIZE_SPECIAL_CHARS, FILTER_FLAG_NO_ENCODE_QUOTES)), - is_int($data) => filter_var($data, FILTER_SANITIZE_NUMBER_INT), - is_float($data) => filter_var($data, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION), - is_bool($data) => filter_var($data, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE), - is_null($data) => settype($data, 'NULL'), - - default => filter_var($data, FILTER_SANITIZE_SPECIAL_CHARS), - }; - } -} - -if (!function_exists('is_email')) { - - /** - * Check if a string is a valid email address. - * - * @param string $email The email address to be checked. - * @return bool True if it's a valid email address, false otherwise. - */ - function is_email($email): bool - { - return filter_var($email, FILTER_VALIDATE_EMAIL) !== false; - } -} - -if (!function_exists('is_url')) { - - /** - * Check if a string is a valid URL. - * - * @param string $url The URL to be checked. - * @return bool True if it's a valid URL, false otherwise. - */ - function is_url($url): bool - { - return filter_var($url, FILTER_VALIDATE_URL) !== false; - } -} - -if (!function_exists('is_ip')) { - - /** - * Check if a string is a valid IP address. - * - * @param string $ip The IP address to be checked. - * @return bool True if it's a valid IP address, false otherwise. - */ - function is_ip($ip): bool - { - return filter_var($ip, FILTER_VALIDATE_IP) !== false; - } -} - -if (!function_exists('show')) { - - /** - * Display or return data. - * - * This function is used to either display data or return it as a string - * based on the provided parameters. - * @param mixed $data The data to be displayed or returned (default is null). - * @param bool $return If true, the data is returned as a string; if false, it's echoed (default is false). - * @return mixed If $return is true, the data is returned as a string; otherwise, it's echoed. - */ - function show($data = null, bool $return = false): string - { - $output = $data ?? ''; - if ($return) return $output; - - echo $output . PHP_EOL; - return ''; - } -} - -if (!function_exists('cVar')) { - - /** - * Copies the value of an original variable, removes the original variable, - * and returns the copied value. - * - * This function is primarily used for duplicating and removing variables - * of types like $_COOKIE or $_SESSION. - * @param mixed $var The variable whose value you want to copy and remove. - * @return mixed The copied value of the original variable. - */ - function cVar($var) - { - $result = $var; - - // Unset the original variable to remove it. - unset($var); - return $result; - } -} - -if (!function_exists('randomId')) { - - /** - * Checks if the 'randomId' function exists and defines it if not. - * - * This function generates a random identifier of a specified size. - * @param int $size The size of the random identifier (default is 50). - * @return int A randomly generated identifier based on CRC32 hashing. - */ - function randomId($size = 50) - { - // Generate a random binary string of the specified size and convert it to hexadecimal. - $randomBytesHex = bin2hex(random_bytes($size)); - - // Calculate a CRC32 hash of the hexadecimal string to create a random identifier. - return crc32($randomBytesHex); - } -} - -if (!function_exists('lang')) { - - /** - * Checks if the 'lang' function exists and defines it if not. - * - * This function is used for language localization and translation of messages. - * @param string $key The key representing the message to be translated. - * @return string The translated message, with optional placeholders replaced. - */ - function lang(string $key, array $args = []) - { - // Get an instance of Lang - $lang = Lang::make(); - - if (empty($args)) { - return $lang->trans($key); - } - - // Translate a key - return $lang->trans($key, $args); - } -} - -if (!function_exists('setFlash')) { - - /** - * Checks if the 'setFlash' function exists and defines it if not. - * - * This function is used to set flash messages in an application. - * @param string $type The type of flash message (e.g., 'success', 'error', 'info', etc.). - * @param string $message The message to be displayed as a flash message. - * @return void - */ - function setFlash(string $type, string $message) - { - // Calls the 'setFlash' method from the 'session' component of the Axm application. - return app() - ->session - ->setFlash($type, $message); - } -} - -if (!function_exists('generateUrl')) { - - /** - * This code checks if a function called "urlSite" exists. - * - * If it does not exist, the code creates a function called "urlSite" that takes in one parameter, - * a string called $dir. The function then sets the scheme and host variables to the request scheme - * and http host from the server respectively. It then sets the path variable to the value of $dir - * after trimming off any slashes at the end. It then creates an url variable by concatenating the - * scheme, host and path variables. If this url is not valid, it throws an exception. Otherwise, - * it returns the url. - **/ - function generateUrl(string $dir = ''): string - { - $url = app() - ->request - ->createNewUrl($dir); - - // If the URL is not valid, throw an exception - if (!filter_var($url, FILTER_VALIDATE_URL)) { - throw new RuntimeException(sprintf('Invalid URL: %s', $url)); - } - - // Return the generated URL - return $url; - } -} - -if (!function_exists('baseUrl')) { - - /** - * Returns the full site root, if the $dir parameter is not passed it will show only the root - * if the path to a file is passed it will show it, this function is used to show paths primarily. - * e.g.: baseUrl('assets/css/bootstrap.min.css'); - **/ - function baseUrl(string $dir = ''): string - { - // If $dir is not empty, remove any forward-slashes or back-slashes from the beginning - // or end of the string, add a forward-slash to the end and assign it to $dir - $dir = (!empty($dir)) ? rtrim($dir, '\/') . '/' : ''; - - // Concatenate PUBLIC_PATH and $dir to form the full URL of the current site - // with the directory appended - $url = generateUrl(trim("$dir/")); - - return $url; - } -} - -if (!function_exists('asset')) { - - /** - * Generate the URL for an asset. - * - * This function takes a relative path to an asset and combines it with the base URL of the application, - * producing the full URL to the asset. It ensures proper handling of directory separators. - * @param string $path The relative path to the asset. - * @param string|null $basePath The base URL of the application (optional). If not provided, it uses '/resources/assets/' as the default. - * @return string The full URL to the asset. - */ - function asset(string $path, ?string $basePath = null): string - { - // Get the URL base of your application from the configuration or as you prefer. - $baseUrl = rtrim($basePath ?? '/resources/assets/', '/') . '/' . ltrim($path, '/'); - - return baseUrl($baseUrl); - } -} - -if (!function_exists('go')) { - - /** - * Used to go to a specific page, - * e.g.: go('login'); - **/ - function go(string $page = ''): string - { - $url = generateUrl($page); - return $url; - } -} - -if (!function_exists('back')) { - - /** - * Is used to go backwards, - * if $_SERVER['HTTP_REFERER'] is defined it goes backwards, - * otherwise it does a refresh - * e.g.: back('login'); **/ - function back() - { - $referer = $_SERVER['HTTP_REFERER'] ?? null; - - if (null !== $referer) - return redirect($referer); - - return refresh(); - } -} - -if (!function_exists('redirect')) { - - /** - * Redirect one page to another, - * e.g.: redirect('login'); - **/ - function redirect($url) - { - return app() - ->response - ->redirect($url); - } -} - -if (!function_exists('refresh')) { - - /** - * Redirects the request to the current URL - **/ - function refresh() - { - return app() - ->request - ->getUri(); - } -} - -if (!function_exists('post')) { - - /** - * Returns all data sent by the POST method. - * if no parameter is passed it shows all the element, if parameters are passed it - * shows the specific element - * e.g.: post(); || post('name'); - **/ - function post($key = null) - { - if (!($post = app()->request->post())) return false; - - if ($key !== null) { - return htmlspecialchars($post[$key], ENT_QUOTES, 'UTF-8'); - } - - return htmlspecialchars($post, ENT_QUOTES, 'UTF-8'); - } -} - -if (!function_exists('isLogged')) { - - /** - * Check if a user is logged in. - * - * This function is used to determine whether a user is currently logged in within the application. - * It calls the 'isLogged' method of the 'Axm' application instance to perform the check. - * @return bool True if a user is logged in; false otherwise. - */ - function isLogged() - { - // Call the 'isLogged' method of the 'Axm' application instance to check if a user is logged in. - return app()->isLogged(); - } -} - -if (!function_exists('old')) { - - /** - * Used to show again if the data sent in - * html elements (input, select, textarea, etc) sent by the POST method exist. - * e.g.: old('name); **/ - function old(string $value) - { - $input = app()->request->post(); - return (isset($input[$value]) && !empty($input[$value])) ? $input[$value] : ''; - } -} - -if (!function_exists('checkSession')) { - - /** - * Check whether a session is defined or not - */ - function checkSession(string $key): bool - { - return app()->session->get($key); - } -} - -if (!function_exists('getInfoUser')) { - - /** - * Returns any user specific info, the name of the class from the ConfigApp - * - * @param string $user - * @param string $value - */ - function getInfoUser(string $user, string $value) - { - $userClass = config('app.userClass'); - return $userClass::getInfoUser($user, $value); - } -} - -if (!function_exists('getUser')) { - - /** - * Returns a specific info of the user who - * has successfully logged in. - */ - function getUser(string $value = null) - { - return app()->user->{$value}; - } -} - -if (!function_exists('now')) { - - /** - * Get the current date and time using the Carbon library. - * @return \Carbon\Carbon A Carbon instance representing the current date and time. - */ - function now() - { - return Carbon::now(); - } -} - -if (!function_exists('once')) { - - /** - * Call a function only once. - * - * @param callable $fn The function to be executed. - * @return callable A closure that wraps the provided function and ensures it is executed only once. - */ - function once($fn) - { - $hasRun = false; - return function (...$params) use ($fn, &$hasRun) { - if ($hasRun) return; - - $hasRun = true; - - return $fn(...$params); - }; - } -} - -if (!function_exists('str')) { - - /** - * Create a new string helper instance or operate on a string. - * - * @param string|null $string (Optional) The input string to operate on. - * @return Stringable|object Returns a Stringable instance if a string argument is provided. - */ - function str($string = null) - { - if (is_null($string)) { - // Return a new class instance for chaining string methods - return new class - { - public function __call($method, $params) - { - // Delegate method calls to the Str class - return Str::$method(...$params); - } - }; - } - // Return a Stringable instance for the provided string - return Str::of($string); - } -} - -if (!function_exists('__')) { - - /** - * Create a Fluent instance for method chaining. - * - * This function is used to create a Fluent instance, allowing for method chaining - * on the provided object. It enhances the readability and expressiveness of code by enabling - * a sequence of method calls on the object. - * @param object $obj The object on which method chaining is desired. - * @return Fluent\Fluent An instance of the Fluent class for method chaining. - */ - function __($obj) - { - // Return a new instance of the FluentInterface class for method chaining - return new Fluent\Fluent($obj); - } -} - -if (!function_exists('reflect')) { - - /** - * Access and manipulate non-public properties and methods of an object using reflection. - * - * @param object $obj The object to be reflect. - * @return object An object with enhanced access to non-public members of $obj. - * @throws InvalidArgumentException If $obj is not a valid object. - */ - function reflect($obj) - { - return new class($obj) - { - private $obj; - private $reflected; - - public function __construct($obj) - { - $this->obj = $obj; - $this->reflected = new ReflectionClass($obj); - } - - public function &__get($name) - { - $getProperty = function & () use ($name) { - return $this->{$name}; - }; - - $getProperty = $getProperty->bindTo($this->obj, get_class($this->obj)); - - return $getProperty(); - } - - public function __set($name, $value) - { - $setProperty = function () use ($name, &$value) { - $this->{$name} = $value; - }; - - $setProperty = $setProperty->bindTo($this->obj, get_class($this->obj)); - - $setProperty(); - } - - public function __call($name, $params) - { - if (!$this->reflected->hasMethod($name)) { - throw new RuntimeException("Method '{$name}' not found."); - } - - $method = $this->reflected->getMethod($name); - $method->setAccessible(true); - - return $method->invoke($this->obj, ...$params); - } - }; - } -} - -if (!function_exists('to_object')) { - - /** - * Converts the element into an object - * - * @param [type] $array - * @return void - */ - function to_object($array) - { - return json_decode(json_encode($array)); - } -} - -if (!function_exists('helpers')) { - - /** - * Load one or multiple helpers. - * - * @param string|array $helpers Names of the helpers to load, separated by spaces, commas, dots or an array. - * @param string|null $customPath The path to custom helper files. If not provided, custom helpers are not loaded. - * @return bool True if all helpers were loaded successfully, false otherwise. - * @throws HelperNotFoundException If a specified helper file does not exist in the given paths. - */ - function helpers($helpers, string $customPath = null, string $separator = '_') - { - // Convert $helpers to an array if it's a string and split by spaces, commas, or dots - if (is_string($helpers)) { - $helpers = preg_split('/[\s,\.]+/', $helpers); - } elseif (!is_array($helpers)) { - throw new InvalidArgumentException('The $helpers variable must be an array.'); - } - - $config = config('paths'); - - // Define paths for helper files - $appPath = $config['helpersPath']; // Default application path - $axmHelpersPath = $config['helpersAxmPath']; // Axm system path - - // Load custom helpers from the provided path - if ($customPath) { - $customPath = rtrim($customPath, '/'); // Ensure the path does not end with a slash - } - - foreach ($helpers as $helper) { - $helper = trim($helper) . $separator . 'helper.php'; - - // Try to load the helper from the custom path first - if ($customPath) { - $customHelperFile = $customPath . DIRECTORY_SEPARATOR . $helper; - if (is_file($customHelperFile)) { - require_once($customHelperFile); - continue; - } - } - - // Try to load the helper from the default application path - $appHelperFile = $appPath . DIRECTORY_SEPARATOR . $helper; - if (is_file($appHelperFile)) { - require_once($appHelperFile); - continue; - } - - // Try to load the helper from the Axm system path - $axmHelperFile = $axmHelpersPath . DIRECTORY_SEPARATOR . $helper; - if (is_file($axmHelperFile)) { - require_once($axmHelperFile); - continue; - } - - // If the helper is not found in any of the specified locations, throw an exception - throw new Exception("The helper '$axmHelperFile' does not exist in any of the specified paths."); - } - - return true; - } -} - -if (!function_exists('getRouteParams')) { - - /** - * Get the route parameters from the current request. - * - * This function retrieves the route parameters from the current HTTP request. - * Route parameters are typically used to - * capture values from the URL in a structured way and are commonly used in routing - * systems to determine the action to - * be taken based on the requested URL. - * @return array An associative array containing the route parameters. - */ - function getRouteParams() - { - return app() - ->request - ->getRouteParams(); - } -} - -if (!function_exists('getUri')) { - - /** - * Get the URI (Uniform Resource Identifier) of the current request. - * - * This function retrieves the URI of the current HTTP request. - * The URI represents the unique identifier for the requested - * resource and typically includes the scheme, host, path, query parameters, - * and fragment identifier. - * @return string The URI of the current request as a string. - */ - function getUri() - { - return app() - ->request - ->getUri(); - } -} - -if (!function_exists('class_basename')) { - - /** - * Class Basename - * - * Returns the base name of a class, effectively stripping the namespace. - * @param mixed $class Either an object or a string with the class name. - * @return string - */ - function class_basename($class) - { - return is_object($class) - ? basename(str_replace('\\', '/', get_class($class))) - : (string) basename(str_replace('\\', '/', $class)); - } -} - -if (!function_exists('camelCase')) { - - /** - * Converts a string in "date_format" style to camelCase format. - * - * @param string $str The input string in "date_format" style. - * @return string The input string converted to camelCase format. - */ - function camelCase($str) - { - $words = explode('_', $str); - - // Capitalize the first letter of each word (except the first one) - for ($i = 1; $i < count($words); $i++) { - $words[$i] = ucfirst($words[$i]); - } - // Join the words back together without spaces and in camelCase format - $camelCaseStr = implode('', $words); - - return $camelCaseStr; - } -} - -if (!function_exists('esc')) { - /** - * Escapes and formats a text string for safe display in HTML. - * - * This function combines HTML encoding and newline-to-break conversion. - * @param string $text The input text to be escaped and formatted. - * @return string The escaped and formatted text. - */ - function esc(string $text): string - { - $encodedText = htmlspecialchars($text, ENT_QUOTES, config('app.charset') ?? 'UTF-8'); - $brText = nl2br($encodedText); - return $brText; - } -} diff --git a/core/libraries/Auth/Auth.php b/core/libraries/Auth/Auth.php deleted file mode 100644 index bc4030c..0000000 --- a/core/libraries/Auth/Auth.php +++ /dev/null @@ -1,220 +0,0 @@ - - * @package Axm\Auth - */ - -class Auth -{ - protected $userClass; - protected $usernameField; - protected $passwordField; - protected $session; - protected $maxFailedAttempts = 5; - protected $failedAttempts = 0; - protected $primaryKey; - protected $userId; - protected $user; - protected $userModel; - protected $app; - - const EVENT_BEFORE_AUTH = 'beforeAuth'; - const EVENT_AFTER_AUTH = 'afterAuth'; - - - /** - * __construct - * - * @param Axm\App $app - * @param string $usernameField - * @param string $passwordField - * @return void - */ - public function __construct(App $app, string $usernameField = 'email', string $passwordField = 'password') - { - $this->app = $app; - - $this->session = $app->session; - $this->userClass = config('app.userClass'); - $this->usernameField = $usernameField; - $this->passwordField = $passwordField; - } - - /** - * Attempts to log in a user based on provided data. - * - * @param array[] ...$keyValuePairs An array or nested arrays containing the fields and values to use for the database query. - * @return bool Returns true if the login is successful, false otherwise. - * @throws \Exception Throws an exception in case of an error during the login process. - */ - public function resolverLogin(array ...$keyValuePairs): bool - { - $count = count($keyValuePairs); - return match ($count) { - 1 => $this->loginWithSingleKey($keyValuePairs[0]), - 2 => $this->loginWithMultipleKeys($keyValuePairs[0], $keyValuePairs[1]), - default => false, - }; - } - - /** - * Attempts to log in a user based on a single key-value pair. - * - * @param array $data An associative array containing user login data. - * @return bool Returns true if the login is successful, false otherwise. - * @throws \Exception Throws an exception in case of an error during the login process. - */ - private function loginWithSingleKey(array $data): bool - { - [$field, $value] = $data; - $result = $this->getUserFromDatabase($this->userClass, $field, $value); - if ($result) { - $keyName = (string)(new $this->userClass)->getKeyName(); - return $this->setUserSession($keyName, $result); - } - - return false; - } - - /** - * Attempts to log in a user based on multiple key-value pairs. - * - * @param array $keys An array containing the fields to use for the database query. - * @param array $values An array containing the corresponding values to match in the database query. - * @return bool Returns true if the login is successful, false otherwise. - * @throws \Exception Throws an exception in case of an error during the login process. - */ - private function loginWithMultipleKeys(array $keys, array $values): bool - { - if (count($keys) !== count($values)) - throw new \InvalidArgumentException('Number of keys and values must match.'); - - $userData = array_combine($keys, $values); - foreach ($userData as $field => $value) { - if ($result = $this->getUserFromDatabase($this->userClass, $field, $value)) { - $keyName = (new $this->userClass)->getKeyName(); - return $this->setUserSession($keyName, $result); - } - } - - return false; - } - - /** - * Sets the user session based on the provided user ID and data. - * - * @param string $userId The key to use for storing user data in the session. - * @param mixed $result The user data to store in the session. - * @return bool - */ - private function setUserSession($userId, $result): bool - { - return $this->app->session->write($userId, serialize($result)); - } - - /** - * Retrieves a user from the database based on provided field and value. - * - * @param string $userClass The class representing the user model. - * @param string $field The field to use for the database query. - * @param mixed $value The value to match in the database query. - * @return mixed|null Returns the user object if found, or null if no user is found. - */ - private function getUserFromDatabase(string $userClass, string $field, $value) - { - // Use prepared statements or an ORM to prevent SQL injection - return $userClass::where($field, $value)->first(); - } - - /** - * Performs a login attempt with the provided credentials - * Returns true if the login was successful, false otherwise. - * - * @param mixed $username - * @param mixed $password - * @return void - */ - public function attempt($username, $password) - { - if ($this->failedAttempts >= $this->maxFailedAttempts) { - throw new \Exception('You have reached the maximum number of failed attempts.'); - } - - $this->userModel = $this->getUserFromDatabase($this->userClass, $this->usernameField, $username); - - if (!$this->userModel || !password_verify($password, $this->userModel->{$this->passwordField})) { - ++$this->failedAttempts; - return false; - } - - $this->failedAttempts = 0; - $this->session->write($this->userId, $this->userModel->{$this->userModel->primaryKey}); - return true; - } - - /** - * Checks if there is a currently authenticated user - * Returns true if there is an authenticated user, false otherwise. - * @return bool - */ - public function check(): bool - { - return $this->session->has($this->userId); - } - - /** - * Returns the currently authenticated user or null if there - * is no authenticated user. - * @return void - */ - public function user() - { - if (!$this->check()) return null; - - return $this->userModel; - } - - /** - * Logs out the current user. - * @return void - */ - public function logout(string $path = null): void - { - $this->session->remove($this->userId); - if (!is_null($path)) { - app()->response->redirect($path); - } - } - - /** - * Returns the maximum number of failed attempts allowed. - */ - public function getMaxFailedAttempts() - { - return (int) $this->maxFailedAttempts; - } - - /** - * Sets the maximum number of failed attempts allowed. - */ - public function setMaxFailedAttempts($maxFailedAttempts) - { - $this->maxFailedAttempts = $maxFailedAttempts; - } - - /** - * Returns the number of unsuccessful attempts made at current login. - */ - public function getFailedAttempts() - { - return $this->failedAttempts ?? null; - } -} diff --git a/core/libraries/Auth/README.md b/core/libraries/Auth/README.md deleted file mode 100644 index e29e9a4..0000000 --- a/core/libraries/Auth/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# Exception -Handler Exception Axm diff --git a/core/libraries/Cache/Cache.php b/core/libraries/Cache/Cache.php deleted file mode 100644 index 1dbc743..0000000 --- a/core/libraries/Cache/Cache.php +++ /dev/null @@ -1,99 +0,0 @@ -init(); - return self::$drivers[$driver]; - } - - /** - * Resolves and instantiates a cache driver if not already initialized. - * - * @param string $driver The name of the cache driver. - * @return void - */ - protected static function resolveDriver(string $driver = 'File'): void - { - if (!isset(self::$drivers[$driver])) { - $class = $driver . 'Cache'; - $classNamespace = self::$namespace . $class; - self::$drivers[$driver] = new $classNamespace(); - } - } - - /** - * Sets the default cache driver. - * - * @param string $driver The name of the default cache driver. - * @return void - */ - public static function setDefaultDriver(string $driver = 'File'): void - { - self::$defaultDriver = $driver; - } -} diff --git a/core/libraries/Cache/Drivers/FileCache.php b/core/libraries/Cache/Drivers/FileCache.php deleted file mode 100644 index a75f64e..0000000 --- a/core/libraries/Cache/Drivers/FileCache.php +++ /dev/null @@ -1,249 +0,0 @@ -cachePath === null) { - $this->cachePath = config('paths.cacheViewPath'); - - if (!is_dir($this->cachePath)) { - mkdir($this->cachePath, $this->cachePathPermission, true); - } elseif (!is_writable($this->cachePath)) { - throw new RuntimeException('Cache path is not writable.'); - } - } - } - - /** - * Clears all cached data. - * @return bool Whether the operation was successful. - */ - public function flush(): bool - { - $this->gc(false); - return true; - } - - /** - * Retrieves a cached value based on a given key. - * - * @param string $key The key for the cached value. - * @return mixed|false The cached value if found, or false if not found or expired. - */ - public function get(string $key) - { - $cacheFile = $this->getCacheFile($key); - if (file_exists($cacheFile) && !$this->isExpired($cacheFile)) { - return $this->embedExpiry - ? substr(file_get_contents($cacheFile), 10) - : file_get_contents($cacheFile); - } - - $this->delete($key); - return false; - } - - /** - * Stores a value in the cache with a specified key and optional expiration time. - * - * @param string $key The key for the cached value. - * @param mixed $value The value to be cached. - * @param int $expire The expiration time for the cache entry in seconds. - * @return bool Whether the operation was successful. - */ - public function set(string $key, $value, int $expire = 0): bool - { - $cacheFile = $this->getCacheFile($key); - if ($expire <= 0) { - $expire = self::DEFAULT_EXPIRE; - } - - $data = $this->embedExpiry ? time() + $expire . "\n" . $value : $value; - if (@file_put_contents($cacheFile, $data, LOCK_EX) !== false) { - @chmod($cacheFile, $this->cacheFilePermission); - return true; - } - - return false; - } - - /** - * Deletes a cached value based on a given key. - * - * @param string $key The key for the cached value. - * @return bool Whether the operation was successful. - */ - public function delete(string $key): bool - { - $cacheFile = $this->getCacheFile($key); - return @unlink($cacheFile); - } - - /** - * Gets the absolute path to the cache file for a given key. - * - * @param string $key The key for the cached value. - * @return string The absolute path to the cache file. - */ - private function getCacheFile(string $key): string - { - $base = $this->cachePath . DIRECTORY_SEPARATOR . md5($key); - if ($this->directoryLevels > 0) { - $hash = md5($key); - - for ($i = 0; $i < $this->directoryLevels; ++$i) { - $base .= DIRECTORY_SEPARATOR . substr($hash, $i + 1, 2); - } - } - - return $base . $this->cacheFileSuffix; - } - - /** - * Performs garbage collection on expired cache files. - * @param bool $expiredOnly If true, only expired files will be deleted. - */ - private function gc(bool $expiredOnly = true): void - { - $this->_gced = true; - foreach (glob($this->cachePath . DIRECTORY_SEPARATOR . '*' . $this->cacheFileSuffix) as $file) { - if ($file[0] === '.') { - continue; - } - - if ($expiredOnly && $this->isExpired($file)) { - @unlink($file); - } elseif (!$expiredOnly) { - @unlink($file); - } - } - - $this->_gced = false; - } - - /** - * Gets the garbage collection (GC) probability. - * @return int The GC probability. - */ - private function getGCProbability(): int - { - return $this->_gcProbability; - } - - /** - * Sets the garbage collection (GC) probability. - * @param int $value The GC probability to set, clamped between 0 and 1,000,000. - */ - private function setGCProbability(int $value): void - { - $this->_gcProbability = max(0, min(1000000, $value)); - } - - /** - * Checks whether a cached file has expired. - * - * @param string $cacheFile The absolute path to the cache file. - * @return bool Whether the cache file has expired. - */ - private function isExpired(string $cacheFile): bool - { - if ($this->embedExpiry) { - $data = file_get_contents($cacheFile); - $timestamp = (int) substr($data, 0, 10); - - return $timestamp > 0 && time() > $timestamp + (int) substr($data, 10); - } - - clearstatcache(); - return filemtime($cacheFile) + $this->getExpire() < time(); - } - - /** - * Gets the default expiration time for cache files. - * @return int The default expiration time in seconds. - */ - public function getExpire(): int - { - return $this->expire; - } - - /** - * Gets all cache files in the cache directory. - * @return array An array of cache file paths. - */ - public function getAllFiles(): array - { - $ext = $this->cacheFileSuffix; - return glob($this->cachePath . DIRECTORY_SEPARATOR . "*$ext"); - } -} diff --git a/core/libraries/Cache/Drivers/MemcachedCache.php b/core/libraries/Cache/Drivers/MemcachedCache.php deleted file mode 100644 index b9f25eb..0000000 --- a/core/libraries/Cache/Drivers/MemcachedCache.php +++ /dev/null @@ -1,136 +0,0 @@ - 'localhost', - 'port' => 11211, - 'persistent' => true, - 'weight' => 1, - 'timeout' => 1, - 'retry_interval' => 15, - ], - ]; - - /** - * Initializes the connection to Memcached. - * - * @throws \Exception If failed to connect to Memcached. - */ - public function init() - { - try { - $this->memcached = new Memcached(); - - // Configure Memcached servers - foreach ($this->servers as $server) { - $this->memcached->addServer( - $server['host'], - $server['port'], - $server['weight'], - $server['persistent'], - $server['timeout'], - $server['retry_interval'] - ); - } - } catch (\Exception $e) { - // Handle the Memcached connection exception as needed - throw new \Exception('Failed to connect to Memcached: ' . $e->getMessage()); - } - } - - /** - * Gets a value from Memcached for the given key. - * - * @param string $key The key to retrieve the value. - * @return mixed|null The stored value or null if not found. - * @throws \RuntimeException If an error occurs during the operation with Memcached. - */ - public function get($key) - { - try { - $value = $this->memcached->get($key); - - // Automatically unserialize objects - return ($value !== false && is_string($value)) ? unserialize($value) : $value; - } catch (\Exception $e) { - throw new RuntimeException(sprintf('Error getting value from Memcached: %s', $e->getMessage())); - } - } - - /** - * Stores a value in Memcached with the given key. - * - * @param string $key The key to store the value. - * @param mixed $value The value to store. - * @param int $expire The expiration time in seconds (0 for never expire). - * @return bool True if stored successfully, false otherwise. - * @throws \RuntimeException If an error occurs during the operation with Memcached. - */ - public function set($key, $value, $expire = 0) - { - try { - // Automatically serialize objects - $serializedValue = (is_object($value) || is_array($value)) ? serialize($value) : $value; - - return $this->memcached->set($key, $serializedValue, $expire); - } catch (\Exception $e) { - // Handle the Memcached operation exception as needed - throw new RuntimeException('Error setting value in Memcached: ' . $e->getMessage()); - } - } - - /** - * Deletes a value from Memcached with the given key. - * - * @param string $key The key of the value to be deleted. - * @return bool True if deleted successfully, false otherwise. - * @throws \RuntimeException If an error occurs during the operation with Memcached. - */ - public function delete($key) - { - try { - return $this->memcached->delete($key); - } catch (\Exception $e) { - // Handle the Memcached operation exception as needed - throw new RuntimeException('Error deleting value from Memcached: ' . $e->getMessage()); - } - } - - /** - * Flushes all values from Memcached. - * - * @return bool True if the flush operation was successful, false otherwise. - * @throws \RuntimeException If an error occurs during the operation with Memcached. - */ - public function flush() - { - try { - return $this->memcached->flush(); - } catch (\Exception $e) { - // Handle the Memcached operation exception as needed - throw new \Exception('Error flushing Memcached cache: ' . $e->getMessage()); - } - } - -} diff --git a/core/libraries/Cache/Drivers/RedisCache.php b/core/libraries/Cache/Drivers/RedisCache.php deleted file mode 100644 index 8ad1251..0000000 --- a/core/libraries/Cache/Drivers/RedisCache.php +++ /dev/null @@ -1,180 +0,0 @@ - 'tcp', - 'host' => '127.0.0.1', - 'port' => 6379, - 'timeout' => 2.5, - 'read_timeout' => 2.5, - ]; - - /** - * Initializes the connection to Redis. - * - * @throws \Exception If the connection to Redis fails. - */ - public function init() - { - try { - if (class_exists('RedisCluster')) { - $this->cluster = true; - $this->redis = new RedisCluster(null, [$this->options]); - } elseif (extension_loaded('redis')) { - $this->cluster = false; - $this->redis = new Redis(); - $this->redis->connect( - $this->options['host'], - $this->options['port'], - $this->options['timeout'], - null, - 0, - $this->options['read_timeout'] - ); - } - } catch (RedisException $e) { - // Handle the Redis connection exception as needed - throw new \Exception('Failed to connect to Redis: ' . $e->getMessage()); - } - } - - /** - * Gets the value associated with a key from Redis. - * - * @param string $key The key to look up in Redis. - * @return mixed The value associated with the key, or null if not found. - * @throws RuntimeException If an error occurs while getting the value. - */ - public function get(string $key) - { - try { - return $this->redis->get($key); - } catch (RedisException $e) { - throw new RuntimeException(sprintf('Error getting value from Redis: %s', $e->getMessage())); - } - } - - /** - * Sets a value identified by a key into Redis. - * - * @param string $key The key identifying the value to be cached. - * @param mixed $value The value to be cached. - * @param int $expire The number of seconds until the cached value will expire. 0 means never expire. - * @return bool True if the value is successfully stored in Redis, false otherwise. - * @throws RuntimeException If an error occurs while setting the value. - */ - public function set(string $key, $value, int $expire = 0): bool - { - try { - if ($expire > 0) { - return $this->redis->setex($key, $expire, $value); - } else { - return $this->redis->set($key, $value); - } - } catch (RedisException $e) { - // Handle the Redis operation exception as needed - throw new RuntimeException('Error setting value in Redis: ' . $e->getMessage()); - } - } - - /** - * Deletes a value with the specified key from Redis. - * - * @param string $key The key of the value to be deleted. - * @return bool True if the value is successfully deleted, false otherwise. - * @throws RuntimeException If an error occurs while deleting the value. - */ - public function delete(string $key): bool - { - try { - return $this->redis->del($key) > 0; - } catch (RedisException $e) { - // Handle the Redis operation exception as needed - throw new RuntimeException('Error deleting value from Redis: ' . $e->getMessage()); - } - } - - /** - * Flushes all values from Redis cache. - * - * @return bool True if the flush operation was successful, false otherwise. - * @throws \Exception If an error occurs while flushing the cache. - */ - public function flush(): bool - { - try { - if ($this->cluster) { - return $this->redis->flushall(); - } else { - return $this->redis->flushDB(); - } - } catch (RedisException $e) { - // Handle the Redis operation exception as needed - throw new \Exception('Error flushing Redis cache: ' . $e->getMessage()); - } - } - - /** - * Sets the expiration time for a key in Redis. - * - * @param string $key The key for which to set the expiration time. - * @param int $expire The number of seconds until the key will expire. - * @return bool True if the expiration time is set successfully, false otherwise. - * @throws \Exception If an error occurs while setting the expiration time. - */ - public function expire(string $key, int $expire): bool - { - try { - return $this->redis->expire($key, $expire); - } catch (RedisException $e) { - // Handle the Redis operation exception as needed - throw new \Exception('Error setting expiration for key in Redis: ' . $e->getMessage()); - } - } - - /** - * Gets multiple values from Redis with the specified keys. - * - * @param array $keys A list of keys identifying the cached values. - * @return array A list of cached values indexed by the keys. - * @throws \Exception If an error occurs while getting multiple values. - */ - public function mget(array $keys): array - { - try { - return $this->redis->mget($keys); - } catch (RedisException $e) { - // Handle the Redis operation exception as needed - throw new \Exception('Error getting multiple values from Redis: ' . $e->getMessage()); - } - } -} diff --git a/core/libraries/Cache/InterfaceCache.php b/core/libraries/Cache/InterfaceCache.php deleted file mode 100644 index b994445..0000000 --- a/core/libraries/Cache/InterfaceCache.php +++ /dev/null @@ -1,47 +0,0 @@ - - - Total Downloads - - - Latest Stable Version - - - License - -

- -# Cache Axm -Cache \ No newline at end of file diff --git a/core/libraries/Console/BaseCommand.php b/core/libraries/Console/BaseCommand.php deleted file mode 100644 index 7511818..0000000 --- a/core/libraries/Console/BaseCommand.php +++ /dev/null @@ -1,206 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - -namespace Console; - -use Console\CLI; -use Console\Commands; -use Psr\Log\LoggerInterface; -use ReflectionException; - - -/** - * BaseCommand is the base class used in creating CLI commands. - * - * @property array $arguments - * @property Commands $commands - * @property string $description - * @property string $group - * @property LoggerInterface $logger - * @property string $name - * @property array $options - * @property string $usage - */ -abstract class BaseCommand -{ - /** - * The group the command is lumped under - * when listing commands. - * @var string - */ - protected $group; - - /** - * The Command's name - * @var string - */ - protected $name; - - /** - * the Command's usage description - * @var string - */ - protected $usage; - - /** - * the Command's short description - * @var string - */ - protected $description; - - /** - * the Command's options description - * @var array - */ - protected $options = []; - - /** - * the Command's Arguments description - * @var array - */ - protected $arguments = []; - - /** - * Instance of Commands so - * commands can call other commands. - * @var Commands - */ - protected $commands = []; - - /** - * @var array - */ - private $params = []; - - protected const ARROW_SYMBOL = '➜ '; - - - /** - * Actually execute a command. - * @param array $params - */ - abstract public function run(array $params); - - /** - * Define a protected method commands that returns a new instance of the Commands class - * if it hasn't been set yet - */ - protected function commands() - { - if ($this->commands !== []) { - return; - } - - return $this->commands = new Commands(); - } - - /** - * Can be used by a command to run other commands. - * - * @throws ReflectionException - * @return mixed - */ - protected function call(string $command, array $params = []) - { - return $this->commands()->run($command, $params); - } - - /** - * Show Help includes (Usage, Arguments, Description, Options). - */ - public function showHelp() - { - CLI::write('CLI help Usage: ', 'yellow'); - - if (!empty($this->usage)) { - $usage = $this->usage; - } else { - $usage = $this->name; - - if (!empty($this->arguments)) { - $usage .= ' [arguments]'; - } - } - - CLI::write($this->setPad($usage, 0, 0, 2)); - - if (!empty($this->description)) { - CLI::newLine(); - CLI::write(self::ARROW_SYMBOL . 'CLI help Description: ', 'yellow'); - CLI::write($this->setPad($this->description, 0, 0, 2)); - } - - if (!empty($this->arguments)) { - CLI::newLine(); - CLI::write(self::ARROW_SYMBOL . 'CLI help Arguments: ', 'yellow'); - - $length = max(array_map('strlen', array_keys($this->arguments))); - - foreach ($this->arguments as $argument => $description) { - CLI::write(CLI::color($this->setPad($argument, $length, 2, 2), 'green') . $description); - } - } - - if (!empty($this->options)) { - CLI::newLine(); - CLI::write(self::ARROW_SYMBOL . 'CLI help Options: ', 'yellow'); - - $length = max(array_map('strlen', array_keys($this->options))); - - foreach ($this->options as $option => $description) { - CLI::write(CLI::color($this->setPad($option, $length, 2, 2), 'green') . $description); - } - } - } - - /** - * Pads our string out so that all titles are the same length to nicely line up descriptions. - * @param int $extra How many extra spaces to add at the end - */ - public function setPad(string $item, int $max, int $extra = 2, int $indent = 0): string - { - $max += $extra + $indent; - return str_pad(str_repeat(' ', $indent) . $item, $max); - } - - /** - * Get pad for $key => $value array output - * - * @deprecated Use setPad() instead. - * @codeCoverageIgnore - */ - public function getPad(array $array, int $pad): int - { - $max = 0; - foreach (array_keys($array) as $key) { - $max = max($max, strlen($key)); - } - - return $max + $pad; - } - - /** - * Makes it simple to access our protected properties. - * @return mixed - */ - public function __get(string $key) - { - return $this->{$key} ?? null; - } - - /** - * Makes it simple to check our protected properties. - */ - public function __isset(string $key): bool - { - return isset($this->{$key}); - } -} diff --git a/core/libraries/Console/CLI.php b/core/libraries/Console/CLI.php deleted file mode 100644 index fc4194d..0000000 --- a/core/libraries/Console/CLI.php +++ /dev/null @@ -1,1005 +0,0 @@ - - */ - protected static $foreground_colors = [ - 'black' => '0;30', - 'dark_gray' => '1;30', - 'blue' => '0;34', - 'light_blue' => '1;34', - 'green' => '0;32', - 'light_green' => '1;32', - 'cyan' => '0;36', - 'light_cyan' => '1;36', - 'red' => '0;31', - 'light_red' => '1;31', - 'purple' => '0;35', - 'light_purple' => '1;35', - 'yellow' => '0;33', - 'light_yellow' => '1;33', - 'light_gray' => '0;37', - 'white' => '1;37', - 'default' => '1;37', - ]; - - /** - * Background color list - * - * @var array - */ - protected static $background_colors = [ - 'black' => '40', - 'red' => '41', - 'green' => '42', - 'yellow' => '43', - 'blue' => '44', - 'magenta' => '45', - 'cyan' => '46', - 'light_gray' => '47', - ]; - - /** - * List of array segments. - * - * @var array - */ - protected static $segments = []; - - /** - * @var array - */ - protected static $options = []; - - /** - * Helps track internally whether the last - * output was a "write" or a "print" to - * keep the output clean and as expected. - * - * @var string|null - */ - protected static $lastWrite; - - /** - * Height of the CLI window - * - * @var int|null - */ - protected static $height; - - /** - * Width of the CLI window - * - * @var int|null - */ - protected static $width; - - /** - * Whether the current stream supports colored output. - * - * @var bool - */ - protected static $isColored = false; - - /** - * Static "constructor". - */ - public static function init() - { - if (static::is_cli()) { - // Readline is an extension for PHP that makes interactivity with PHP - // much more bash-like. - // http://www.php.net/manual/en/readline.installation.php - static::$readline_support = extension_loaded('readline'); - - // clear segments & options to keep testing clean - static::$segments = []; - static::$options = []; - - // Check our stream resource for color support - static::$isColored = static::hasColorSupport(STDOUT); - - static::parseCommandLine(); - - static::$initialized = true; - } else { - // If the command is being called from a controller - // we need to define STDOUT ourselves - define('STDOUT', 'php://output'); - } - } - - /** - * Get input from the shell, using readline or the standard STDIN - * - * Named options must be in the following formats: - * php index.php user -v --v -name=John --name=John - * @param string $prefix - */ - public static function input(?string $prefix = null): string - { - if (static::$readline_support) { - return readline($prefix); - } - - echo $prefix; - return fgets(STDIN); - } - - /** - * Asks the user for input. - * - * Usage: - * - * // Takes any input - * $color = CLI::prompt('What is your favorite color?'); - * - * // Takes any input, but offers default - * $color = CLI::prompt('What is your favourite color?', 'white'); - * - * // Will validate options with the in_list rule and accept only if one of the list - * $color = CLI::prompt('What is your favourite color?', array('red','blue')); - * - * // Do not provide options but requires a valid email - * $email = CLI::prompt('What is your email?', null, 'required|valid_email'); - * @param string $field Output "field" question - * @param array|string $options String to a default value, array to a list of options (the first option will be the default value) - * @param array|string $validation Validation rules - * @return string The user input - */ - public static function prompt(string $field, $options = null, $validation = null, string $color = 'yellow'): string - { - $extraOutput = ''; - $default = ''; - - if ($validation && !is_array($validation) && !is_string($validation)) { - throw new Exception('$rules can only be of type string|array'); - } - - if (is_string($options)) { - $extraOutput = ' [' . static::color($options, 'green') . ']'; - $default = $options; - } - - if (is_array($options) && $options) { - $opts = $options; - $extraOutputDefault = static::color($opts[0], 'green'); - - unset($opts[0]); - - if (empty($opts)) { - $extraOutput = $extraOutputDefault; - } else { - $extraOutput = '[' . $extraOutputDefault . ', ' . implode(', ', $opts) . ']'; - } - - $default = $options[0]; - } - - static::fwrite(STDOUT, static::color($field, $color) . (trim($field) ? ' ' : '') . $extraOutput . ': '); - - // Read the input from keyboard. - $input = trim(static::input()) ?: $default; - - if ($validation) { - while (!static::validate(trim($field), $input, $validation)) { - $input = static::prompt($field, $options, $validation); - } - } - - return $input; - } - - /** - * prompt(), but based on the option's key - * - * @param array|string $text Output "field" text or an one or two value array where the first value is the text before listing the options - * and the second value the text before asking to select one option. Provide empty string to omit - * @param array $options A list of options (array(key => description)), the first option will be the default value - * @param array|string|null $validation Validation rules - * @return string The selected key of $options - */ - public static function promptByKey($text, array $options, $validation = null): string - { - if (is_string($text)) { - $text = [$text]; - } elseif (!is_array($text)) { - throw new Exception("$text can only be of type string|array"); - } - - if (!$options) { - throw new Exception('No options to select from were provided'); - } - - if ($line = array_shift($text)) { - CLI::write($line); - } - - // +2 for the square brackets around the key - $keyMaxLength = max(array_map('mb_strwidth', array_keys($options))) + 2; - - foreach ($options as $key => $description) { - $name = str_pad(' [' . $key . '] ', $keyMaxLength + 4, ' '); - CLI::write(CLI::color($name, 'green') . CLI::wrap($description, 125, $keyMaxLength + 4)); - } - - return static::prompt(PHP_EOL . array_shift($text), array_keys($options), $validation); - } - - /** - * Validate one prompt "field" at a time - * - * @param string $field Prompt "field" output - * @param string $value Input value - * @param array|string $rules Validation rules - * - */ - protected static function validate(string $field, string $value, $rules): bool - { - $rules = ['input' => $rules]; - $data = ['input' => $value]; - $validator = Validator::make($rules, $data); - - if ($validator->fails()) { - $msg = $validator->getFirstError(); - static::error($validator->getFirstError()); - exit(); - } - - return true; - } - - /** - * Outputs a string to the CLI without any surrounding newlines. - * Useful for showing repeating elements on a single line. - */ - public static function print(string $text = '', ?string $foreground = null, ?string $background = null) - { - if ($foreground || $background) { - $text = static::color($text, $foreground, $background); - } - - static::$lastWrite = null; - - static::fwrite(STDOUT, $text); - } - - /** - * Outputs a string to the cli on it's own line. - */ - public static function write(string $text = '', ?string $foreground = null, ?string $background = null) - { - if ($foreground || $background) { - $text = static::color($text, $foreground, $background); - } - - if (static::$lastWrite !== 'write') { - $text = PHP_EOL . $text; - static::$lastWrite = 'write'; - } - - static::fwrite(STDOUT, $text . PHP_EOL); - } - - /** - * Outputs an msg to the CLI using STDERR instead of STDOUT - */ - public static function msg(string $text, string $foreground = 'light_red', ?string $background = null) - { - // Check color support for STDERR - $stdout = static::$isColored; - static::$isColored = static::hasColorSupport(STDERR); - - if ($foreground || $background) { - $text = static::color($text, $foreground, $background); - } - - static::fwrite(STDERR, $text . PHP_EOL); - - // return STDOUT color support - static::$isColored = $stdout; - } - - /** - * error - * - * @param mixed $text - * @param mixed $background - * @return void - */ - public static function error(string $text, ?string $background = null) - { - return static::msg($text, 'red', $background); - } - - /** - * success - * - * @param mixed $text - * @param mixed $background - * @return void - */ - public static function success(string $text, ?string $background = null) - { - return static::msg($text, 'green', $background); - } - - /** - * info - * - * @param mixed $text - * @param mixed $background - * @return void - */ - public static function info(string $text, ?string $background = null) - { - return static::msg($text, 'blue', $background); - } - - /** - * warning - * - * @param mixed $text - * @param mixed $background - * @return void - */ - public static function warning(string $text, ?string $background = null) - { - return static::msg($text, 'yellow', $background); - } - - /** - * Beeps a certain number of times. - * @param int $num The number of times to beep - */ - public static function beep(int $num = 1) - { - echo str_repeat("\x07", $num); - } - - /** - * Waits a certain number of seconds, optionally showing a wait message and - * waiting for a key press. - * - * @param int $seconds Number of seconds - * @param bool $countdown Show a countdown or not - */ - public static function wait(int $seconds, bool $countdown = false) - { - if ($countdown === true) { - $time = $seconds; - - while ($time > 0) { - static::fwrite(STDOUT, $time . '... '); - sleep(1); - $time--; - } - - static::write(); - } elseif ($seconds > 0) { - sleep($seconds); - } else { - // this chunk cannot be tested because of keyboard input - static::write(static::$wait_msg); - static::input(); - } - } - - /** - * isWindows - * if operating system === windows - * @return bool - */ - public static function isWindows(): bool - { - return PHP_OS_FAMILY === 'Windows'; - } - - /** - * Enter a number of empty lines - */ - public static function newLine(int $num = 1) - { - // Do it once or more, write with empty string gives us a new line - for ($i = 0; $i < $num; $i++) { - static::write(); - } - } - - /** - * Clears the screen of output - */ - public static function clearScreen() - { - // Unix systems, and Windows with VT100 Terminal support (i.e. Win10) - // can handle CSI sequences. For lower than Win10 we just shove in 40 new lines. - static::isWindows() && !static::streamSupports('sapi_windows_vt100_support', STDOUT) - ? static::newLine(40) - : static::fwrite(STDOUT, "\033[H\033[2J"); - } - - /** - * Returns the given text with the correct color codes for a foreground and - * optionally a background color. - * - * @param string $text The text to color - * @param string $foreground The foreground color - * @param string $background The background color - * @param string $format Other formatting to apply. Currently only 'underline' is understood - * - * @return string The color coded string - */ - public static function color(string $text, string $foreground, ?string $background = null, ?string $format = null): string - { - if (!static::$isColored) { - return $text; - } - - if (!array_key_exists($foreground, static::$foreground_colors)) { - throw new Exception(' foreground Invalid Color: ' . $foreground); - } - - if ($background !== null && !array_key_exists($background, static::$background_colors)) { - throw new Exception(' background Invalid Color'); - } - - $string = "\033[" . static::$foreground_colors[$foreground] . 'm'; - - if ($background !== null) { - $string .= "\033[" . static::$background_colors[$background] . 'm'; - } - - if ($format === 'underline') { - $string .= "\033[4m"; - } - - // Detect if color method was already in use with this text - if (strpos($text, "\033[0m") !== false) { - // Split the text into parts so that we can see - // if any part missing the color definition - $chunks = mb_split('\\033\\[0m', $text); - // Reset text - $text = ''; - - foreach ($chunks as $chunk) { - if ($chunk === '') { - continue; - } - - // If chunk doesn't have colors defined we need to add them - if (strpos($chunk, "\033[") === false) { - $chunk = static::color($chunk, $foreground, $background, $format); - // Add color reset before chunk and clear end of the string - $text .= rtrim("\033[0m" . $chunk, "\033[0m"); - } else { - $text .= $chunk; - } - } - } - - return $string . $text . "\033[0m"; - } - - /** - * Get the number of characters in string having encoded characters - * and ignores styles set by the color() function - */ - public static function strlen(?string $string): int - { - if ($string === null) { - return 0; - } - - foreach (static::$foreground_colors as $color) { - $string = strtr($string, ["\033[" . $color . 'm' => '']); - } - - foreach (static::$background_colors as $color) { - $string = strtr($string, ["\033[" . $color . 'm' => '']); - } - - $string = strtr($string, ["\033[4m" => '', "\033[0m" => '']); - - return mb_strwidth($string); - } - - /** - * Checks whether the current stream resource supports or - * refers to a valid terminal type device. - * - * @param resource $resource - */ - public static function streamSupports(string $function, $resource): bool - { - if (app()->isProduction()) { - // In the current setup of the tests we cannot fully check - // if the stream supports the function since we are using - // filtered streams. - return function_exists($function); - } - - return function_exists($function) && @$function($resource); - } - - - /** - * Returns true if the stream resource supports colors. - * - * This is tricky on Windows, because Cygwin, Msys2 etc. emulate pseudo - * terminals via named pipes, so we can only check the environment. - * Reference: https://github.com/composer/xdebug-handler/blob/master/src/Process.php - * - * @param resource $resource - */ - public static function hasColorSupport($resource): bool - { - // Follow https://no-color.org/ - if (isset($_SERVER['NO_COLOR']) || getenv('NO_COLOR') !== false) { - return false; - } - - if (getenv('TERM_PROGRAM') === 'Hyper') { - return true; - } - - if (static::isWindows()) { - return static::streamSupports('sapi_windows_vt100_support', $resource) - || isset($_SERVER['ANSICON']) - || getenv('ANSICON') !== false - || getenv('ConEmuANSI') === 'ON' - || getenv('TERM') === 'xterm'; - } - - return static::streamSupports('stream_isatty', $resource); - } - - /** - * Attempts to determine the width of the viewable CLI window. - */ - public static function getWidth(int $default = 80): int - { - if (static::$width === null) { - static::generateDimensions(); - } - - return static::$width ?: $default; - } - - /** - * Attempts to determine the height of the viewable CLI window. - */ - public static function getHeight(int $default = 32): int - { - if (static::$height === null) { - static::generateDimensions(); - } - - return static::$height ?: $default; - } - - /** - * Populates the CLI's dimensions. - * - * @codeCoverageIgnore - */ - public static function generateDimensions() - { - try { - if (static::isWindows()) { - // Shells such as `Cygwin` and `Git bash` returns incorrect values - // when executing `mode CON`, so we use `tput` instead - if (getenv('TERM') || (($shell = getenv('SHELL')) && preg_match('/(?:bash|zsh)(?:\.exe)?$/', $shell))) { - static::$height = (int) exec('tput lines'); - static::$width = (int) exec('tput cols'); - } else { - $return = -1; - $output = []; - exec('mode CON', $output, $return); - - // Look for the next lines ending in ": " - // Searching for "Columns:" or "Lines:" will fail on non-English locales - if ($return === 0 && $output && preg_match('/:\s*(\d+)\n[^:]+:\s*(\d+)\n/', implode("\n", $output), $matches)) { - static::$height = (int) $matches[1]; - static::$width = (int) $matches[2]; - } - } - } elseif (($size = exec('stty size')) && preg_match('/(\d+)\s+(\d+)/', $size, $matches)) { - static::$height = (int) $matches[1]; - static::$width = (int) $matches[2]; - } else { - static::$height = (int) exec('tput lines'); - static::$width = (int) exec('tput cols'); - } - } catch (\Throwable $e) { - // Reset the dimensions so that the default values will be returned later. - // Then let the developer know of the error. - static::$height = null; - static::$width = null; - error_log('error: ' . $e->getMessage()); - } - } - - /** - * Displays a progress bar on the CLI. You must call it repeatedly - * to update it. Set $thisStep = false to erase the progress bar. - * - * @param bool|int $thisStep - */ - public static function showProgress($thisStep = 1, int $totalSteps = 10) - { - static $inProgress = false; - - // restore cursor position when progress is continuing. - if ($inProgress !== false && $inProgress <= $thisStep) { - static::fwrite(STDOUT, "\033[1A"); - } - $inProgress = $thisStep; - - if ($thisStep !== false) { - // Don't allow div by zero or negative numbers.... - $thisStep = abs($thisStep); - $totalSteps = $totalSteps < 1 ? 1 : $totalSteps; - - $percent = (int) (($thisStep / $totalSteps) * 100); - $step = (int) round($percent / 10); - - // Write the progress bar - static::fwrite(STDOUT, "[\033[32m" . str_repeat('#', $step) . str_repeat('.', 10 - $step) . "\033[0m]"); - // Textual representation... - static::fwrite(STDOUT, sprintf(' %3d%% Complete', $percent) . PHP_EOL); - } else { - static::fwrite(STDOUT, "\007"); - } - } - - /** - * Takes a string and writes it to the command line, wrapping to a maximum - * width. If no maximum width is specified, will wrap to the window's max - * width. - * - * If an int is passed into $pad_left, then all strings after the first - * will padded with that many spaces to the left. Useful when printing - * short descriptions that need to start on an existing line. - */ - public static function wrap(?string $string = null, int $max = 0, int $padLeft = 0): string - { - if (empty($string)) { - return ''; - } - - if ($max === 0) { - $max = self::getWidth(); - } - - if (self::getWidth() < $max) { - $max = self::getWidth(); - } - - $max = $max - $padLeft; - - $lines = wordwrap($string, $max, PHP_EOL); - - if ($padLeft > 0) { - $lines = explode(PHP_EOL, $lines); - - $first = true; - - array_walk($lines, static function (&$line) use ($padLeft, &$first) { - if (!$first) { - $line = str_repeat(' ', $padLeft) . $line; - } else { - $first = false; - } - }); - - $lines = implode(PHP_EOL, $lines); - } - - return $lines; - } - - //-------------------------------------------------------------------- - // Command-Line 'URI' support - //-------------------------------------------------------------------- - - /** - * Parses the command line it was called from and collects all - * options and valid segments. - */ - protected static function parseCommandLine() - { - $args = $_SERVER['argv'] ?? []; - array_shift($args); // scrap invoking program - $optionValue = false; - - foreach ($args as $i => $arg) { - // If there's no "-" at the beginning, then - // this is probably an argument or an option value - if (mb_strpos($arg, '-') !== 0) { - if ($optionValue) { - // We have already included this in the previous - // iteration, so reset this flag - $optionValue = false; - } else { - // Yup, it's a segment - static::$segments[] = $arg; - } - - continue; - } - - $arg = ltrim($arg, '-'); - $value = null; - - if (isset($args[$i + 1]) && mb_strpos($args[$i + 1], '-') !== 0) { - $value = $args[$i + 1]; - $optionValue = true; - } - - static::$options[$arg] = $value; - } - } - - /** - * Returns the command line string portions of the arguments, minus - * any options, as a string. This is used to pass along to the main - * axm application. - */ - public static function getURI(): string - { - return implode('/', static::$segments); - } - - /** - * Returns an individual segment. - * - * This ignores any options that might have been dispersed between - * valid segments in the command: - * - * // segment(3) is 'three', not '-f' or 'anOption' - * > php axm one two -f anOption three - * - * **IMPORTANT:** The index here is one-based instead of zero-based. - * - * @return mixed - */ - public static function getSegment(int $index) - { - return static::$segments[$index - 1] ?? null; - } - - /** - * Returns the raw array of segments found. - */ - public static function getSegments(): array - { - return static::$segments; - } - - /** - * Gets a single command-line option. Returns TRUE if the option - * exists, but doesn't have a value, and is simply acting as a flag. - * - * @return mixed - */ - public static function getOption(string $name, string|int $default = null) - { - if (!array_key_exists($name, static::$options)) { - return $default; - } - - // If the option didn't have a value, simply return TRUE - // so they know it was set, otherwise return the actual value. - $val = static::$options[$name] ?? true; - - return $val; - } - - /** - * Returns the raw array of options found. - */ - public static function getOptions(): array - { - return static::$options; - } - - /** - * Returns the options as a string, suitable for passing along on - * the CLI to other commands. - * - * @param bool $useLongOpts Use '--' for long options? - * @param bool $trim Trim final string output? - */ - public static function getOptionString(bool $useLongOpts = false, bool $trim = false): string - { - if (empty(static::$options)) { - return ''; - } - - $out = ''; - - foreach (static::$options as $name => $value) { - if ($useLongOpts && mb_strlen($name) > 1) { - $out .= "--{$name} "; - } else { - $out .= "-{$name} "; - } - - if ($value === null) { - continue; - } - - if (mb_strpos($value, ' ') !== false) { - $out .= "\"{$value}\" "; - } elseif ($value !== null) { - $out .= "{$value} "; - } - } - - return $trim ? trim($out) : $out; - } - - /** - * Returns a well formatted table - * - * @param array $tbody List of rows - * @param array $thead List of columns - */ - public static function table(array $tbody, array $thead = []) - { - // All the rows in the table will be here until the end - $tableRows = []; - - // We need only indexes and not keys - if (!empty($thead)) { - $tableRows[] = array_values($thead); - } - - foreach ($tbody as $tr) { - $tableRows[] = array_values($tr); - } - - // Yes, it really is necessary to know this count - $totalRows = count($tableRows); - - // Store all columns lengths - // $all_cols_lengths[row][column] = length - $allColsLengths = []; - - // Store maximum lengths by column - // $max_cols_lengths[column] = length - $maxColsLengths = []; - - // Read row by row and define the longest columns - for ($row = 0; $row < $totalRows; $row++) { - $column = 0; // Current column index - - foreach ($tableRows[$row] as $col) { - // Sets the size of this column in the current row - $allColsLengths[$row][$column] = static::strlen($col); - - // If the current column does not have a value among the larger ones - // or the value of this is greater than the existing one - // then, now, this assumes the maximum length - if (!isset($maxColsLengths[$column]) || $allColsLengths[$row][$column] > $maxColsLengths[$column]) { - $maxColsLengths[$column] = $allColsLengths[$row][$column]; - } - - // We can go check the size of the next column... - $column++; - } - } - - // Read row by row and add spaces at the end of the columns - // to match the exact column length - for ($row = 0; $row < $totalRows; $row++) { - $column = 0; - - foreach ($tableRows[$row] as $col) { - $diff = $maxColsLengths[$column] - static::strlen($col); - - if ($diff) { - $tableRows[$row][$column] = $tableRows[$row][$column] . str_repeat(' ', $diff); - } - - $column++; - } - } - - $table = ''; - - // Joins columns and append the well formatted rows to the table - for ($row = 0; $row < $totalRows; $row++) { - // Set the table border-top - if ($row === 0) { - $cols = '+'; - - foreach ($tableRows[$row] as $col) { - $cols .= str_repeat('-', static::strlen($col) + 2) . '+'; - } - $table .= $cols . PHP_EOL; - } - - // Set the columns borders - $table .= '| ' . implode(' | ', $tableRows[$row]) . ' |' . PHP_EOL; - - // Set the thead and table borders-bottom - if (isset($cols) && (($row === 0 && !empty($thead)) || ($row + 1 === $totalRows))) { - $table .= $cols . PHP_EOL; - } - } - - static::write($table); - } - - /** - * While the library is intended for use on CLI commands, - * commands can be called from controllers and elsewhere - * so we need a way to allow them to still work. - * - * For now, just echo the content, but look into a better - * solution down the road. - * - * @param resource $handle - */ - protected static function fwrite($handle, string $string) - { - if (static::is_cli()) { - - echo $string; - - return; - } - - fwrite($handle, $string); - } - - /** - * @return bool - */ - protected static function is_cli(): bool - { - if (in_array(PHP_SAPI, ['cli', 'phpdbg'], true)) { - return true; - } - - return !isset($_SERVER['REMOTE_ADDR']) && !isset($_SERVER['REQUEST_METHOD']); - } -} - -// Ensure the class is initialized. Done outside of code coverage -CLI::init(); diff --git a/core/libraries/Console/CLIException.php b/core/libraries/Console/CLIException.php deleted file mode 100644 index d109f64..0000000 --- a/core/libraries/Console/CLIException.php +++ /dev/null @@ -1,417 +0,0 @@ -'; - protected static int $maxLinesToDisplay = 10; - - - /** - * Handles an exception and displays relevant information in the console. - * @param Throwable $e The exception to handle. - */ - public static function handleCLIException(Throwable $e): void - { - self::printExceptionInfo($e); - self::snipCode($e); - self::printBacktrace($e->getTrace()); - } - - /** - * info - * - * @param string $key - * @return void - */ - public static function info(string $key) - { - $data = [ - 'protocolVersion' => isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : '', - 'statusCode' => http_response_code(), - 'reasonPhrase' => isset($_SERVER['HTTP_REASON_PHRASE']) ? $_SERVER['HTTP_REASON_PHRASE'] : '' - ]; - - return $data[$key] ?? null; - } - - /** - * Prints information about the exception. - * @param Throwable $e The exception. - */ - protected static function printExceptionInfo(Throwable $e): void - { - $filepath = str_replace(ROOT_PATH, '', $e->getFile()) ?? '[internal function]'; - - $errorLocation = ['file' => $filepath, 'line' => $e->getLine()]; - self::displayHeaderBox(get_class($e), $e->getMessage()); - CLI::write('at ' . CLI::color("$errorLocation[file]:$errorLocation[line]", 'green')); - } - - /** - * Displays a formatted header box with a title and message. - * - * @param string $title The title of the header box. - * @param string $message The message to be displayed in the header box. - * @return void - */ - protected static function displayHeaderBox(string $title, string $message): void - { - CLI::newLine(); - CLI::write(sprintf('[ %s ]', $title), 'light_gray', 'red'); - CLI::newLine(); - CLI::write(self::ARROW_SYMBOL_UTF8 . $message, 'white'); - CLI::newLine(); - } - - /** - * Displays a snippet of relevant code for the exception. - * @param Throwable $e The exception. - */ - public static function snipCode(Throwable $e) - { - $code = self::getCode($e); - CLI::write($code); - } - - /** - * Gets the relevant source code for the exception. - * - * @param Throwable $e The exception. - * @return string The source code. - */ - public static function getCode(Throwable $e): string - { - $code = self::renderSourceCode($e->getFile(), $e->getLine(), self::$maxLinesToDisplay); - return $code; - } - - /** - * Gets the color associated with a PHP token ID. - * - * @param int $tokenId The PHP token ID. - * @return string The color associated with the token ID. - */ - protected static function getTokenColor(int $tokenId): string - { - $tokenColors = [ - T_OPEN_TAG => 'blue', - T_OPEN_TAG_WITH_ECHO => 'light_yellow', - T_CLOSE_TAG => 'blue', - T_STRING => 'blue', - T_VARIABLE => 'light_cyan', - // Constants - T_DIR => 'light_cyan', - T_FILE => 'default', - T_METHOD_C => 'light_yellow', - T_DNUMBER => 'default', - T_LNUMBER => 'default', - T_NS_C => 'default', - T_LINE => 'default', - T_CLASS_C => 'light_cyan', - T_FUNC_C => 'light_yellow', - T_TRAIT_C => 'light_cyan', - // Comment - T_COMMENT => 'light_green', - T_DOC_COMMENT => 'dark_gray', - - T_ENCAPSED_AND_WHITESPACE => 'light_red', - T_CONSTANT_ENCAPSED_STRING => 'light_red', - - T_INLINE_HTML => 'blue', - // - T_FOREACH => 'light_purple', - T_FOR => 'light_purple', - T_WHILE => 'light_purple', - T_DO => 'light_purple', - T_CASE => 'light_purple', - T_TRY => 'light_purple', - T_ENUM => 'light_purple', - T_EXIT => 'light_purple', - T_BREAK => 'light_purple', - T_THROW => 'light_purple', - T_SWITCH => 'light_purple', - T_IF => 'light_purple', - T_RETURN => 'light_purple', - T_CONTINUE => 'light_purple', - T_YIELD => 'light_purple', - T_ENDSWITCH => 'light_purple', - T_ENDIF => 'light_purple', - T_ENDFOR => 'light_purple', - T_ENDFOREACH => 'light_purple', - T_ENDWHILE => 'light_purple', - T_THROW => 'light_purple', - // - T_DOLLAR_OPEN_CURLY_BRACES => 'light_purple', - - T_START_HEREDOC => 'light_cyan', - T_END_HEREDOC => 'light_cyan', - // - T_FUNCTION => 'light_cyan', - T_PRIVATE => 'light_cyan', - T_PROTECTED => 'light_cyan', - T_PUBLIC => 'light_cyan', - - T_NEW => 'blue', - T_CLONE => 'blue', - T_NAMESPACE => 'blue', - T_INTERFACE => 'blue', - ]; - - return $tokenColors[$tokenId] ?? 'white'; - } - - /** - * Renders the relevant source code. - * - * @param string $file The file name. - * @param int $errorLine The line number with the error. - * @param int $maxLines The maximum number of lines to display. - * @return string The formatted source code. - */ - protected static function renderSourceCode(string $file, int $errorLine, int $maxLines): string - { - --$errorLine; // Adjust line number to 0-based from 1-based - $lines = @file($file); - - if ($lines === false || count($lines) <= $errorLine) { - return ''; - } - - $lineRange = self::calculateLineRange($errorLine, count($lines), $maxLines); - $highlightedLines = self::highlightLines($lines, $lineRange, $errorLine); - - return $highlightedLines; - } - - /** - * Calculates the line range to display. - * - * @param int $errorLine Error line number. - * @param int $lineCount Total number of lines. - * @param int $maxLines Maximum number of lines to display. - * @return array Line range to display (start and end). - */ - protected static function calculateLineRange(int $errorLine, int $lineCount, int $maxLines): array - { - $halfLines = (int) ($maxLines / 2); - $beginLine = max($errorLine - $halfLines, 0); - $endLine = min($beginLine + $maxLines - 1, $lineCount - 1); - return [ - 'start' => $beginLine, - 'end' => $endLine - ]; - } - - /** - * Highlights the relevant lines of the source code. - * - * @param array $lines Array of code lines. - * @param array $lineRange Line range to display. - * @param int $errorLine Error line number. - * @return array Array of highlighted lines. - */ - protected static function highlightLines($lines, $lineRange, $errorLine) - { - $highlightedLines = []; - - foreach ($lines as $index => $line) { - if ($index < $lineRange['start'] || $index > $lineRange['end']) { - continue; - } - - // Delete newline characters - $cleanLine = str_replace(["\r\n", "\r", "\n"], '', $line); - - // Preparing the code and highlighting the syntax - $preparedCode = (string) self::prepareCode([$cleanLine]); - $highlightedCode = self::highlightSyntax($preparedCode); - - // Add line numbers and handle error if needed - $formattedCode = self::addLines(explode("\n", trim($highlightedCode)), $index, $errorLine); - - $highlightedLines[] = self::clearCodeOutput($formattedCode); - } - - return implode('', $highlightedLines); - } - - - /** - * Prepares the code for rendering by adding a PHP opening tag. - * - * @param array $code An array of code lines. - * @return string The prepared code with a PHP opening tag. - */ - protected static function prepareCode(array $code): string - { - $stringOut = implode("\n", $code); - $output = " $lineI) { - $lineNumber = $i + $lineBegin + 1; - $isErrorLine = $lineNumber - 1 === $errorLine; - - $linePrefix = ($isErrorLine ? CLI::color(self::ARROW_SYMBOL . ' ', 'red') : ' ') - . CLI::color($lineNumber . self::DELIMITER, 'dark_gray'); - - // Use sprintf for better readability - $formattedLine = sprintf('%s%s', $linePrefix, ($lineI === '') ? $lineI : $lineI . PHP_EOL); - $outputLines[] = $formattedLine; - } - - return implode('', $outputLines); - } - - /** - * Clears unnecessary code elements from the provided code. - * - * Removes PHP opening tags and other unwanted code elements. - * @param string $code The code to be cleaned. - * @return string The code with unnecessary elements removed. - */ - protected static function clearCodeOutput(string $code): string - { - $output = str_replace(['', '<%', '%>'], '', $code); - return $output; - } - - /** - * Prints a formatted backtrace for display. - * - * @param array $backtrace An array containing the backtrace information. - * @return void - */ - protected static function printBacktrace(array $backtrace): void - { - if (!empty($backtrace)) { - CLI::write('Backtrace:', 'blue'); - } - - foreach ($backtrace as $i => $error) { - self::printStackTraceEntryInfo($i, $error); - } - } - - /** - * Prints a formatted backtrace entry for display. - * - * @param int $i The index of the backtrace entry. - * @param array $error An array containing information about the backtrace entry. - * @return void - */ - protected static function printStackTraceEntryInfo(int $i, array $error): void - { - $c = str_pad(strval($i + 1), 3, ' ', STR_PAD_LEFT); - - $filepath = str_replace(ROOT_PATH, '', $error['file'] ?? '[internal function]'); - $line = $error['line'] ?? 'unknown'; - CLI::write($c . self::DELIMITER_UTF8 . ' ' . CLI::color("$filepath:{$line}", 'white'), 'dark_gray'); - CLI::write(' ' . self::DELIMITER_UTF8 . ' ' . CLI::color(self::formatCallableInfo($error), 'dark_gray'), 'dark_gray'); - CLI::write(str_repeat('-', CLI::getWidth() - 4), 'dark_gray'); - } - - /** - * Formats information about a function or method for display. - * - * @param array $error An array containing information about the error. - * @return string The formatted string representation of the function or method information. - */ - protected static function formatCallableInfo(array $error): string - { - $function = $error['class'] ?? ''; - $function .= $error['type'] ?? ''; - $function .= $error['function'] ?? ''; - - if (isset($error['args'])) { - $args = array_map(fn ($arg) => self::formatArgument($arg), $error['args']); - $function .= '(' . implode(', ', $args) . ')'; - } else { - $function .= '()'; - } - - return $function; - } - - /** - * Formats a function argument for display. - * - * @param mixed $arg The argument to be formatted. - * @return string The formatted string representation of the argument. - */ - protected static function formatArgument($arg): string - { - return match (true) { - is_object($arg) => 'Object(' . get_class($arg) . ')', - is_array($arg) => count($arg) ? self::formatArray($arg) : '[]', - is_string($arg) => "'" . $arg . "'", - is_bool($arg) => $arg ? 'true' : 'false', - is_null($arg) => 'null', - - default => (string) $arg, - }; - } - - /** - * Formats an associative array for display. - * - * @param array $array The associative array to be formatted. - * @return string The formatted string representation of the array. - */ - protected static function formatArray(array $array): string - { - $result = []; - foreach ($array as $key => $value) { - $keyValue = self::formatArgument($key) . '=>' . self::formatArgument($value); - $result[] = $keyValue; - } - - return '[' . implode(', ', $result) . ']'; - } -} diff --git a/core/libraries/Console/Commands.php b/core/libraries/Console/Commands.php deleted file mode 100644 index 578359a..0000000 --- a/core/libraries/Console/Commands.php +++ /dev/null @@ -1,253 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - -namespace Console; - -use Console\CLI; -use Console\BaseCommand; -use Exception; -use ReflectionClass; -use ReflectionException; -use RecursiveIteratorIterator; -use RecursiveDirectoryIterator; - -/** - * Core functionality for running, listing, etc commands. - */ -class Commands -{ - /** - * The found commands. - * @var array - */ - public $commands = []; - private $classCache = []; - private $cachedCommands = []; - const COMMAND_EXTENSION = 'php'; - - /** - * Constructor - * @param Logger|null $logger - */ - public function __construct() - { - return $this->discoverCommands(); - } - - /** - * run - * - * @param mixed $command - * @param mixed $params - * @return void - */ - public function run(string $command, array $params) - { - if (!$this->verifyCommand($command, $this->commands)) { - return; - } - - // The file would have already been loaded during the - // createCommandList function... - $className = $this->commands[$command]['class']; - $class = new $className(); - - return $class->run($params); - } - - /** - * Provide access to the list of commands. - * @return array - */ - public function getCommands(): array - { - return $this->commands; - } - - /** - * Discovers all commands in the framework, within user code, and also - * within the vendor/composer/axm/ directory and its subdirectories. - */ - protected function discoverCommands(): void - { - if ($this->commands !== []) return; - - $commandsFolder = AXM_PATH; - $appCommandsFolder = config('paths.commandsPath') . DIRECTORY_SEPARATOR; - - // Caching - if ($cachedCommands = $this->loadCachedCommands()) { - $this->commands = $cachedCommands; - return; - } - - // Create an array of directories to scan, including the vendor directory - $directoriesToScan = [$commandsFolder, $appCommandsFolder]; - foreach ($directoriesToScan as $dir) { - $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir)); - - foreach ($iterator as $fileInfo) { - if (!$fileInfo->isFile() || $fileInfo->getExtension() !== self::COMMAND_EXTENSION) { - continue; - } - - $className = self::getClassnameFromFile($fileInfo->getPathname()); - if (!$className || !class_exists($className)) { - continue; - } - - try { - - $class = new ReflectionClass($className); - if (!$class->isInstantiable() || !$class->isSubclassOf(BaseCommand::class)) { - continue; - } - - /** @var BaseCommand $class */ - $class = new $className(); - if (isset($class->group)) { - $this->commands[$class->name] = [ - 'class' => $className, - 'file' => $fileInfo->getPathname(), - 'group' => $class->group, - 'description' => $class->description, - ]; - } - - unset($class); - } catch (ReflectionException $e) { - CLI::error($e->getMessage()); - } - } - } - - // Caching - $this->saveCachedCommands($this->commands); - asort($this->commands); - } - - /** - * loadCachedCommands - * @return void - */ - private function loadCachedCommands() - { - // Implement your caching logic to load from an array here - // For example, if using a class property for caching: - if (isset($this->cachedCommands)) { - return $this->cachedCommands; - } - - return null; - } - - /** - * @param mixed $commands - */ - private function saveCachedCommands($commands) - { - // Implement your caching logic to save to an array here - // For example, if using a class property for caching: - $this->cachedCommands = $commands; - } - - /** - * @param string $filePath - * @param bool $includeNamespace - */ - private function getClassnameFromFile(string $filePath, bool $includeNamespace = true) - { - // Check if the result is cached - if (isset($this->classCache[$filePath][$includeNamespace])) { - return $this->classCache[$filePath][$includeNamespace]; - } - - // Check if the file exists and is readable - if (!file_exists($filePath)) { - throw new Exception("The $filePath file does not exist."); - } - - if (!is_readable($filePath)) { - throw new Exception("The $filePath file cannot be read."); - } - - // Read file contents - $contents = file_get_contents($filePath); - - // Search the namespace of the class - $namespace = ''; - $namespaceRegex = '/^\s*namespace\s+([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(?:\\\\[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)*)\s*;/m'; - if (preg_match($namespaceRegex, $contents, $matches)) { - $namespace = '\\' . trim($matches[1], '\\'); - } - - // Search for the class name - $class = ''; - $classRegex = '/^\s*(abstract\s+|final\s+)?class\s+([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)/m'; - if (preg_match($classRegex, $contents, $matches)) { - $class = trim($namespace . '\\' . $matches[2], '\\'); - } - - // Cache and return result - $this->classCache[$filePath][$includeNamespace] = $class; - return $includeNamespace ? $class : substr(strrchr($class, "\\"), 1); - } - - /** - * Verifies if the command being sought is found - * in the commands list. - */ - public function verifyCommand(string $command, array $commands): bool - { - if (isset($commands[$command])) { - return true; - } - - $command = $command; - $message = "Command Not Found: [$command]"; - - if ($alternatives = $this->getCommandAlternatives($command, $commands)) { - if (count($alternatives) === 1) { - $message .= "\n\n" . 'Command in Singular' . "\n "; - } else { - $message .= "\n\n" . 'Did you mean one of these?' . "\n "; - } - - $message .= implode("\n ", $alternatives); - } - - CLI::error($message); - CLI::newLine(); - - return false; - } - - /** - * Finds alternative of `$name` among collection - * of commands. - */ - protected function getCommandAlternatives(string $name, array $collection): array - { - $alternatives = []; - - foreach (array_keys($collection) as $commandName) { - $lev = levenshtein($name, $commandName); - - if ($lev <= strlen($commandName) / 3 || strpos($commandName, $name) !== false) { - $alternatives[$commandName] = $lev; - } - } - - ksort($alternatives, SORT_NATURAL | SORT_FLAG_CASE); - - return array_keys($alternatives); - } -} diff --git a/core/libraries/Console/Commands/Cache/ClearCache.php b/core/libraries/Console/Commands/Cache/ClearCache.php deleted file mode 100644 index 0dc349b..0000000 --- a/core/libraries/Console/Commands/Cache/ClearCache.php +++ /dev/null @@ -1,82 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - -namespace Console\Commands\Cache; - -use Cache\Cache; -use Console\BaseCommand; -use Console\CLI; - - -/** - * Clears current cache. - */ -class ClearCache extends BaseCommand -{ - /** - * Command grouping. - * - * @var string - */ - protected $group = 'Cache'; - - /** - * The Command's name - * - * @var string - */ - protected $name = 'clear:cache'; - - /** - * the Command's short description - * - * @var string - */ - protected $description = 'Clears the current system caches.'; - - /** - * the Command's usage - * - * @var string - */ - protected $usage = 'clear:cache [driver]'; - - /** - * the Command's Arguments - * - * @var array - */ - protected $arguments = [ - 'driver' => 'The cache driver to use', - ]; - - /** - * Clears the cache - */ - public function run(array $params) - { - $config = config()->load('Cache.php'); - - $handler = (string) ($params[1] ?? $config->cache->handler); - if (!array_key_exists($handler, $config->cache->validHandlers)) { - CLI::error($handler . ' is not a valid cache handler.'); - return; - } - - $cache = Cache::driver($handler); - if (!$cache->flush()) { - CLI::error(self::ARROW_SYMBOL . 'Error while clearing the cache.'); - return; - } - - CLI::write(CLI::color(self::ARROW_SYMBOL . 'Cache cleared.', 'green')); - } -} diff --git a/core/libraries/Console/Commands/Cache/InfoCache.php b/core/libraries/Console/Commands/Cache/InfoCache.php deleted file mode 100644 index 708c123..0000000 --- a/core/libraries/Console/Commands/Cache/InfoCache.php +++ /dev/null @@ -1,85 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - -namespace Console\Commands\Cache; - -use Cache\Cache; -use Console\BaseCommand; -use Console\CLI; - -/** - * Shows information on the cache. - */ -class InfoCache extends BaseCommand -{ - /** - * Command grouping. - * - * @var string - */ - protected $group = 'Cache'; - - /** - * The Command's name - * - * @var string - */ - protected $name = 'info:cache'; - - /** - * 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 = 'info:cache'; - - /** - * Clears the cache - */ - public function run(array $params) - { - config()->load('Cache.php'); - $handler = config('cache.handler'); - - if ($handler !== 'file') { - CLI::error(self::ARROW_SYMBOL . 'This command only supports the file cache handler.'); - return; - } - - $caches = Cache::driver()->getAllFiles(); - - $tbody = []; - foreach ($caches as $key => $field) { - $tbody[] = [ - $key, - $field, - filesize($field), - date("F d Y H:i:s.", fileatime($field)), - ]; - } - - $thead = [ - CLI::color('Name', 'green'), - CLI::color('Server Path', 'green'), - CLI::color('Size', 'green'), - CLI::color('Date', 'green'), - ]; - - CLI::table($tbody, $thead); - } -} diff --git a/core/libraries/Console/Commands/Database/Adapters/Factory.php b/core/libraries/Console/Commands/Database/Adapters/Factory.php deleted file mode 100644 index 9d992d5..0000000 --- a/core/libraries/Console/Commands/Database/Adapters/Factory.php +++ /dev/null @@ -1,185 +0,0 @@ -faker = \Faker\Factory::create(); - } - - if (class_exists(\Illuminate\Support\Str::class)) { - $this->str = \Illuminate\Support\Str::class; - } - } - - /** - * Define the model's default state. - * - * @return array - */ - public function definition(): array - { - return []; - } - - /** - * Create a number of records based on definition - * - * @param int $number The number of records to create - * @return self - */ - public function create(int $number): Factory - { - $data = []; - - for ($i = 0; $i < $number; $i++) { - $data[] = $this->definition(); - } - - $this->data = $data; - - return $this; - } - - /** - * Create a relationship with another factory - * - * @param \Leaf\Factory $factory The instance of the factory to tie to - * @param array|string $primaryKey The primary key for that factory's table - * @throws \Exception - * @throws \Throwable - */ - public function has(Factory $factory, $primaryKey = null): Factory - { - if (count($this->data) === 0) { - $this->data[] = $this->definition(); - } - - $dataToOverride = []; - $model = $this->model ?? $this->getModelName(); - - if (!$primaryKey) { - $primaryKey = strtolower($this->getModelName() . '_id'); - $primaryKey = str_replace('\app\models\\', '', $primaryKey); - } - - if (is_array($primaryKey)) { - $dataToOverride = $primaryKey; - } else { - $key = explode('_', $primaryKey); - if (count($key) > 1) { - unset($key[0]); - } - $key = implode($key); - - $primaryKeyData = $this->data[\rand(0, count($this->data) - 1)][$key] ?? null; - $primaryKeyData = $primaryKeyData ?? $model::all()[\rand(0, count($model::all()) - 1)][$key]; - - $dataToOverride[$primaryKey] = $primaryKeyData; - } - - $factory->save($dataToOverride); - - return $this; - } - - /** - * Save created records in db - * - * @param array $override Override data to save - * - * @return true - * @throws \Exception - */ - public function save(array $override = []): bool - { - $model = $this->model ?? $this->getModelName(); - - if (count($this->data) === 0) { - $this->data[] = $this->definition(); - } - - foreach ($this->data as $item) { - $item = array_merge($item, $override); - - $model = new $model; - foreach ($item as $key => $value) { - $model->{$key} = $value; - } - $model->save(); - } - - return true; - } - - /** - * Return created records - * - * @param array|null $override Override data to save - * - * @return array - */ - public function get(array $override = null): array - { - if (count($this->data) === 0) { - $this->data[] = $this->definition(); - } - - if ($override) { - foreach ($this->data as $item) { - $item = array_merge($item, $override); - } - } - - return $this->data; - } - - /** - * Get the default model name - * @throws \Exception - */ - public function getModelName(): string - { - $class = get_class($this); - $modelClass = '\App\Models' . - $this->str::studly(str_replace(['App\Database\Factories', 'Factory'], '', $class)); - - if (!class_exists($modelClass)) { - throw new \Exception('Couldn\'t retrieve model for ' - . $class . '. Add a \$model attribute to fix this.'); - } - - return $modelClass; - } -} diff --git a/core/libraries/Console/Commands/Database/Adapters/Schema.php b/core/libraries/Console/Commands/Database/Adapters/Schema.php deleted file mode 100644 index 04bd15c..0000000 --- a/core/libraries/Console/Commands/Database/Adapters/Schema.php +++ /dev/null @@ -1,187 +0,0 @@ -hasTable($table)) { - static::$capsule::schema()->create($table, function (Blueprint $table) use ($schema) { - foreach ($schema as $key => $value) { - list($key, $type) = static::getColumns($key, $value); - - echo $key . ' => ' . $type . "\n"; - - if (strpos($key, '*') === 0) { - $table->foreignId(substr($key, 1)); - continue; - } - - if ($key === 'timestamps') { - $table->timestamps(); - continue; - } - - if ($key === 'softDeletes') { - $table->softDeletes(); - continue; - } - - if ($key === 'rememberToken') { - $table->rememberToken(); - continue; - } - - if ($type === 'enum') { - if (substr($key, -1) === '?') { - $table->enum(substr($key, 0, -1), $value)->nullable(); - continue; - } - - $table->enum($key, $value); - continue; - } - - if (method_exists($table, $type)) { - if (substr($key, -1) === '?') { - call_user_func_array([$table, $type], [substr($key, 0, -1)])->nullable(); - continue; - } - - call_user_func_array([$table, $type], [$key]); - continue; - } - } - }); - } - } - - /** - * Get the table and table structure - * @param string $table The name of table to manipulate (can be the name of the file) - * @param string|null $schema The JSON schema for database (if $table is not the name of the file) - * - * @return array - */ - protected static function getSchema(string $table, ?string $schema = null): array - { - try { - - if ($schema === null) { - if (file_exists($table)) { - $schema = file_get_contents($table); - $table = str_replace('.json', '', basename($table)); - } else { - $table = str_replace('.json', '', $table); - $schema = json_decode(file_get_contents(config('paths.schemaPaths') - . DIRECTORY_SEPARATOR . "$table.json")); - } - } else { - $schema = json_decode($schema); - } - - return [$table, $schema]; - } catch (\Throwable $th) { - throw $th; - } - } - - /** - * Get the columns of a table and their types - * - * @param string $key The column as provided in the schema - * @param mixed $value The value of the column as provided in the schema - */ - protected static function getColumns(string $key, $value): array - { - $column = ''; - $type = ''; - - $keyData = explode(':', $key); - - if (count($keyData) > 1) { - $type = trim($keyData[1]); - $column = trim($keyData[0]); - - if ($type === 'id') { - $column .= '*'; - $type = 'bigIncrements'; - } else if ($type === 'number') { - $type = 'integer'; - } else if ($type === 'bool') { - $type = 'boolean'; - } - - if (gettype($value) === 'NULL' && rtrim($column, '*') !== $column) { - $column .= '?'; - } - - return [$column, $type]; - } - - echo $key . ' => ' . $value . "\n"; - - if ( - (strtolower($key) === 'id' && gettype($value) === 'integer') || - (strpos(strtolower($key), '_id') !== false && gettype($value) === 'integer') - ) { - return [$key, 'bigIncrements']; - } - - if ( - strpos(ltrim(strtolower(preg_replace('/[A-Z]([A-Z](?![a-z]))*/', '_$0', $key)), '_'), '_at') !== false || - strpos(ltrim(strtolower(preg_replace('/[A-Z]([A-Z](?![a-z]))*/', '_$0', $key)), '_'), '_date') !== false || - strpos(ltrim(strtolower(preg_replace('/[A-Z]([A-Z](?![a-z]))*/', '_$0', $key)), '_'), '_time') !== false || - strpos($key, 'timestamp') === 0 || - strpos($key, 'time') === 0 || - strpos($key, 'date') === 0 - ) { - return [$key, 'timestamp']; - } - - if (gettype($value) === 'integer') { - return [$key, 'integer']; - } - - if (gettype($value) === 'double') { - return [$key, 'float']; - } - - if (gettype($value) === 'string') { - if (strpos($value, '{') === 0 || strpos($value, '[') === 0) { - return [$key, 'json']; - } - - if ($key === 'description' || $key === 'text' || strlen($value) > 150) { - return [$key, 'text']; - } - - return [$key, 'string']; - } - - if (gettype($value) === 'array') { - return [$key, 'enum']; - } - - if (gettype($value) === 'boolean') { - return [$key, 'boolean']; - } - - return [$column, $type]; - } -} diff --git a/core/libraries/Console/Commands/Database/CreateDatabase.php b/core/libraries/Console/Commands/Database/CreateDatabase.php deleted file mode 100644 index 7be669f..0000000 --- a/core/libraries/Console/Commands/Database/CreateDatabase.php +++ /dev/null @@ -1,120 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - -namespace Console\Commands\Database; - -use Console\BaseCommand; -use Console\CLI; -use function mysqli_query; -use function mysqli_connect; - -/** - * 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 new database.'; - - /** - * The Command's usage - * @var string - */ - protected $usage = 'db:create [options]'; - - /** - * The Command's arguments - * @var array - */ - protected $arguments = [ - 'db_name' => 'The database name to use', - ]; - - /** - * The Command's options - * @var array - */ - protected $options = []; - - /** - * @var string - */ - protected $driver = null; - - /** - * Creates a new database. - * @param array $params - */ - public function run(array $params) - { - $this->driver = $name = $params[1] ?? []; - if (empty($name)) { - $this->driver = $name = CLI::prompt(self::ARROW_SYMBOL . 'Database name', null, 'required|text'); - } - - try { - $this->createDatabase($name); - } catch (\Exception $e) { - throw $e; - } - } - - /** - * Create a new database with the given name. - * - * @param string $name The name of the database to create. - * @throws Exception If there is an error creating the database. - */ - protected function createDatabase(string $name) - { - $env = $this->getData(); - $connection = mysqli_connect($env['host'], $env['user'], $env['password'], '', (int) $env['port']); - - if (mysqli_query($connection, "CREATE DATABASE `$name`")) { - CLI::write(self::ARROW_SYMBOL . "Database \"{$name}\" successfully created.", 'green'); - CLI::newLine(); - } - } - - /** - * Get the database connection information from the environment. - * @return array The database connection information. - */ - public function getData(): array - { - return [ - 'host' => env('DB_HOST'), - 'user' => env('DB_USERNAME'), - 'password' => env('DB_PASSWORD'), - 'database' => env('DB_DATABASE'), - 'port' => empty(env('DB_PORT')) - ? 3306 - : env('DB_PORT') - ]; - } -} diff --git a/core/libraries/Console/Commands/Database/DeleteConsole.php b/core/libraries/Console/Commands/Database/DeleteConsole.php deleted file mode 100644 index da9f74c..0000000 --- a/core/libraries/Console/Commands/Database/DeleteConsole.php +++ /dev/null @@ -1,125 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - -namespace Console\Commands\Database; - -use Console\BaseCommand; -use Console\CLI; - -class DeleteConsole 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 = 'd:command'; - - /** - * The Command's short description - * @var string - */ - protected $description = 'Delete a console command'; - - /** - * The Command's usage - * @var string - */ - protected $usage = 'd:command '; - - /** - * The Command's arguments - * @var array - */ - protected $arguments = []; - - /** - * The Command's options - * @var array - */ - protected $options = []; - - - /** - * Runs the command based on the provided parameters. - * - * @param array $params An array of command parameters. - * @return int Returns 0 on success, 1 on failure. - */ - public function run(array $params): int - { - $command = $params[1] ?? ''; - - if (!str_contains($command, 'Command')) { - $command .= 'Command'; - } - - $commandFile = $this->getCommandFilePath($command); - if (!$this->deleteCommandFile($commandFile, $command)) { - return 1; - } - - $this->printSuccessMessage($command); - return 0; - } - - /** - * Gets the file path for the specified command. - * - * @param string $command The name of the command. - * @return string The file path for the command. - */ - private function getCommandFilePath(string $command): string - { - return config('paths.commandsPath') . DIRECTORY_SEPARATOR . "$command.php"; - } - - /** - * Deletes the file associated with the specified command. - * - * @param string $commandFile The file path for the command. - * @param string $command The name of the command. - * @return bool Returns true on successful deletion, false otherwise. - */ - private function deleteCommandFile(string $commandFile, string $command): bool - { - if (!file_exists($commandFile)) { - CLI::error(self::ARROW_SYMBOL . " \"{$command}\" doesn't exist!"); - return false; - } - - if (!unlink($commandFile)) { - CLI::error(self::ARROW_SYMBOL . " Couldn't delete \"{$command}\", you might need to remove it manually"); - return false; - } - - return true; - } - - /** - * Prints a success message for the deleted command. - * - * @param string $command The name of the deleted command. - * @return void - */ - private function printSuccessMessage(string $command): void - { - CLI::write(self::ARROW_SYMBOL . ' Command ' . CLI::color($command, 'yellow') . ' deleted successfully!.', 'green'); - CLI::newLine(); - } -} diff --git a/core/libraries/Console/Commands/Database/DeleteMigration.php b/core/libraries/Console/Commands/Database/DeleteMigration.php deleted file mode 100644 index 948c1d3..0000000 --- a/core/libraries/Console/Commands/Database/DeleteMigration.php +++ /dev/null @@ -1,104 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - -namespace Console\Commands\Database; - -use Console\BaseCommand; -use Console\CLI; -use Console\GeneratorTrait; - -class DeleteMigration extends BaseCommand -{ - use GeneratorTrait; - - /** - * The group the command is lumped under - * when listing commands. - * - * @var string - */ - protected $group = 'Database'; - - /** - * The Command's name - * @var string - */ - protected $name = 'd:migration'; - - /** - * The Command's short description - * @var string - */ - protected $description = 'Delete a migration'; - - /** - * The Command's usage - * @var string - */ - protected $usage = 'd:migration '; - - /** - * The Command's arguments - * @var array - */ - protected $arguments = []; - - /** - * The Command's options - * @var array - */ - protected $options = []; - - - /** - * Runs the migration deletion process based on the provided file name. - * - * @param array $params An array of parameters, with the file name at index 1. - * @return int Returns 0 on success, 1 on failure. - */ - public function run(array $params): int - { - $filename = ($params[1]) ?? ''; - if (!str_contains($filename, 'Migration')) { - $filename .= 'Migration'; - } - - $migrationsPath = config('paths.migrationsPath') . DIRECTORY_SEPARATOR; - - $migrations = glob($migrationsPath . "*_$filename.php") ?? []; - - if (empty($migrations)) { - CLI::error(self::ARROW_SYMBOL . " File \"{$filename}\" not found"); - CLI::newLine(); - return 1; - } - - foreach ($migrations as $migrationFile) { - $this->deleteMigrationFile($migrationFile); - } - - CLI::newLine(); - return 0; - } - - /** - * Deletes a migration file. - * - * @param string $filePath The path to the migration file. - * @return void - */ - private function deleteMigrationFile(string $filePath): void - { - unlink($filePath); - $file = str_replace('.php', '', basename($filePath)); - CLI::info(self::ARROW_SYMBOL . ' Migration ' . CLI::color($file, 'yellow') . ' has been deleted successfully.'); - } -} diff --git a/core/libraries/Console/Commands/Database/Migrate.php b/core/libraries/Console/Commands/Database/Migrate.php deleted file mode 100644 index 1cee77d..0000000 --- a/core/libraries/Console/Commands/Database/Migrate.php +++ /dev/null @@ -1,184 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ -namespace Console\Commands\Database; - -use Console\BaseCommand; -use Console\CLI; -use Illuminate\Support\Str; -use RuntimeException; -use Throwable; - -/** - * This class is responsible for managing database 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 = 'db:migrate'; - - /** - * The Command's short description - * @var string - */ - protected $description = 'Run the database migrations.'; - - /** - * The Command's usage - * @var string - */ - protected $usage = 'migrate [options]'; - - /** - * The Command's Options - * @var array - */ - protected $options = [ - '-f' => 'Rollback a particular file. (optional)', - '-s' => 'Run seeds after migration', - ]; - - /** - * The number of values to display before the file name in the migration list. - * This is used for better formatting of the output. - * @var int - */ - private int $numberOfValuesBeforeFileName = 20; - - - /** - * The main method that runs the migration process. - * - * @param array $params The command line parameters. - * @return int 0 if the migration is successful, or an error code if not. - */ - public function run(array $params) - { - $seeds = $params['s'] ?? CLI::getOption('s') ?? false; - $rollback = $params['f'] ?? CLI::getOption('f') ?? false; - - try { - $migrationFiles = $this->getMigrationFiles(); - - foreach ($migrationFiles as $migrationFile) { - $this->processMigration($migrationFile, $seeds, $rollback); - } - - CLI::write(self::ARROW_SYMBOL . 'Database migration completed!', 'green'); - CLI::newLine(); - return 0; - } catch (Throwable $e) { - throw new RuntimeException($e->getMessage()); - } - } - - /** - * Get the list of migration files. - * @return array The list of migration files. - */ - protected function getMigrationFiles(): array|false - { - $migrationsPath = config('paths.migrationsPath') . DIRECTORY_SEPARATOR; - return glob($migrationsPath . "*.php"); - } - - /** - * Process a migration file. - * - * @param string $migrationFile The path to the migration file. - * @param bool $seeds Run the seeds command after the migration. - * @param bool $rollback Rollback the migration. - */ - protected function processMigration(string $migrationFile, bool $seeds, bool $rollback): void - { - $fileInfo = pathinfo($migrationFile); - $filename = $fileInfo['filename']; - - if ($filename !== 'Schema') { - if ($rollback && $this->shouldRollback($migrationFile, $rollback)) { - $this->migrate($filename); - $this->displayMigrationInfo($migrationFile, $seeds, $filename, 'migrated'); - } else { - $this->migrate($filename); - $this->displayMigrationInfo($migrationFile, $seeds, $filename, 'applied'); - } - } - } - - /** - * Check if a migration should be rolled back. - * - * @param string $migrationFile - * @param mixed $rollback - * @return bool - */ - protected function shouldRollback($migrationFile, $rollback) - { - return str_contains($migrationFile, Str::snake("_create_$rollback.php")); - } - - /** - * Migrates a given migration file. - * @param string $filename The name of the migration file. - */ - protected function migrate($filename) - { - try { - - $migrationsPath = config('paths.migrationsPath') . DIRECTORY_SEPARATOR; - $class = include $migrationsPath . $filename . '.php'; - $class->up(); - } catch (\Throwable $e) { - throw new RuntimeException($e->getMessage()); - } - } - - /** - * Display information about the migration that was just run - * - * @param string $migrationFile The name of the migration file - * @param bool $seeds Whether or not seeds were run - * @param string $filename The name of the seed file (if seeds were run) - * @param string $status The status of the migration - */ - private function displayMigrationInfo(string $migrationFile, bool $seeds, string $filename, string $status): void - { - CLI::write(self::ARROW_SYMBOL . " db $status on " . CLI::color($filename, 'yellow'), 'blue'); - if ($seeds) { - $this->messageInfo($seeds, $filename); - } - } - - /** - * This method is used to display a success message when a seeding process is completed - * - * @param array $seeds An array of seeders that were run - * @param string $filename The name of the migration file - */ - protected function messageInfo($seeds, string $filename) - { - if ($seeds) { - $seederClass = str_replace('Create', '', Str::studly(substr($filename, $this->numberOfValuesBeforeFileName))); - - if ($seederClass) CLI::write(self::ARROW_SYMBOL . " \"{$seederClass}\" seeded successfully!", 'green'); - } - } -} diff --git a/core/libraries/Console/Commands/Database/MigrateRefresh.php b/core/libraries/Console/Commands/Database/MigrateRefresh.php deleted file mode 100644 index 3c5c41f..0000000 --- a/core/libraries/Console/Commands/Database/MigrateRefresh.php +++ /dev/null @@ -1,81 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - -namespace Console\Commands\Database; - -use Console\BaseCommand; -use Console\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 - */ - protected $options = [ - '--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 (env('APP_ENVIRONMENT') === 'production') { - - $force = array_key_exists('f', $params) || CLI::getOption('f'); - - if (!$force && CLI::prompt(self::ARROW_SYMBOL . ' Refresh confirm migrations?', ['y', 'n']) === 'n') { - return; - } - - $params['f'] = null; - - } - - $this->call('migrate:rollback', $params); - $this->call('migrate', $params); - } -} diff --git a/core/libraries/Console/Commands/Database/MigrateRollback.php b/core/libraries/Console/Commands/Database/MigrateRollback.php deleted file mode 100644 index 268d8cb..0000000 --- a/core/libraries/Console/Commands/Database/MigrateRollback.php +++ /dev/null @@ -1,171 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - -namespace Console\Commands\Database; - -use Console\BaseCommand; -use Console\CLI; -use RuntimeException; -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 - */ - protected $options = [ - '-s' => 'The batch to rollback', - '-f' => 'Rollback a particular file', - ]; - - /** - * The number of values to display before the file name in the migration list. - * This is used for better formatting of the output. - * @var int - */ - private int $numberOfValuesBeforeFileName = 20; - - - /** - * Runs all of the migrations in reverse order, until they have - * all been unapplied. - */ - public function run(array $params) - { - $step = $params['s'] ?? CLI::getOption('s') ?? false; - $file = $params['f'] ?? CLI::getOption('f') ?? false; - - try { - - $migrationFile = $this->getMigrationFiles(); - $migrations = ($step == 'all') ? $migrationFile : array_slice($migrationFile, -abs($step), abs($step), true); - - foreach ($migrations as $migration) { - $this->processRollback($migration, $step, $file); - } - - if ($file && !in_array($file, $migrations)) { - $this->messageError($file); - return 1; - } - - CLI::write(self::ARROW_SYMBOL . ' Database rollback completed!', 'green'); - CLI::newLine(); - return 0; - } catch (Throwable $e) { - throw new RuntimeException($e->getMessage()); - } - } - - - protected function processRollback(string $migration, bool $step, bool $file) - { - $fileInfo = pathinfo($migration); - $filename = $fileInfo['filename']; - - if (!$file) { - $this->down($file, $migration); - } - - if ($file && $this->shouldRollback($migration, $file)) { - $this->down($file, $migration); - $this->messageSuccess(); - return 0; - } - } - - /** - * Get the list of migration files. - * @return array The list of migration files. - */ - protected function getMigrationFiles(): array|false - { - $migrationsPath = config('paths.migrationsPath') . DIRECTORY_SEPARATOR; - return glob($migrationsPath . "*.php"); - } - - /** - * Check if a migration should be rolled back. - * - * @param string $migrationFile - * @param mixed $rollback - * @return bool - */ - protected function shouldRollback($migrationFile, $rollback) - { - return str_contains($migrationFile, Str::snake("_create_$rollback.php")); - } - - /** - * Rolls back a migration by including its file and calling the `up` method. - * @param string $filename The filename of the migration. - */ - protected function down($filename) - { - try { - - $migrationsPath = config('paths.migrationsPath') . DIRECTORY_SEPARATOR; - $class = include $migrationsPath . $filename . '.php'; - $class->down(); - } catch (\Throwable $e) { - throw new RuntimeException($e->getMessage()); - } - } - - /** - * This method is used to display a success message when a seeding process is completed - */ - protected function messageSuccess() - { - CLI::write(self::ARROW_SYMBOL . " Database rollback completed!", 'green'); - } - - /** - * This method is used to display a error message - * @param string $file The name of the rollback file - */ - protected function messageError(string $file) - { - CLI::error(self::ARROW_SYMBOL . " Rollback $file not found!"); - } -} diff --git a/core/libraries/Console/Commands/Database/MigrateStatus.php b/core/libraries/Console/Commands/Database/MigrateStatus.php deleted file mode 100644 index 50fb359..0000000 --- a/core/libraries/Console/Commands/Database/MigrateStatus.php +++ /dev/null @@ -1,160 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - -namespace Console\Commands\Database; - -use Console\BaseCommand; -use Console\CLI; -use Config\Services; - -/** - * Displays a list of all migrations and whether they've been run or not. - */ -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 - */ - protected $options = [ - '-g' => 'Set database group', - ]; - - /** - * Namespaces to ignore when looking for migrations. - * - * @var string[] - */ - protected $ignoredNamespaces = [ - 'Axm', - 'Psr\Log', - ]; - - /** - * Displays a list of all migrations and whether they've been run or not. - * - * @param array $params - */ - public function run(array $params) - { - //logo - $runner = Services::migrations(); - $group = $params['g'] ?? CLI::getOption('g'); - - // Get all namespaces - $namespaces = Services::autoloader()->getNamespace(); - - // Collection of migration status - $status = []; - - foreach (array_keys($namespaces) as $namespace) { - if (env('APP_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; - } - - $history = $runner->getHistory((string) $group); - ksort($migrations); - - foreach ($migrations as $uid => $migration) { - $migrations[$uid]->name = mb_substr($migration->name, 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', $row->time); - $group = $row->group; - $batch = $row->batch; - // @codeCoverageIgnoreEnd - } - - $status[] = [ - $namespace, - $migration->version, - $migration->name, - $group, - $date, - $batch, - ]; - } - } - - if (!$status) { - // @codeCoverageIgnoreStart - CLI::error(self::ARROW_SYMBOL . 'Migrations none found', 'light_gray', 'red'); - CLI::newLine(); - return; - // @codeCoverageIgnoreEnd - } - - $headers = [ - CLI::color('Migrations namespace', 'yellow'), - CLI::color('Migrations version', 'yellow'), - CLI::color('Migrations filename', 'yellow'), - CLI::color('Migrations group', 'yellow'), - CLI::color(str_replace(': ', '', 'Migrations.on'), 'yellow'), - CLI::color('Migrations.batch', 'yellow'), - ]; - - CLI::table($status, $headers); - } -} diff --git a/core/libraries/Console/Commands/Database/ResetDatabase.php b/core/libraries/Console/Commands/Database/ResetDatabase.php deleted file mode 100644 index 6cacd27..0000000 --- a/core/libraries/Console/Commands/Database/ResetDatabase.php +++ /dev/null @@ -1,161 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - -namespace Console\Commands\Database; - -use Console\BaseCommand; -use Console\CLI; -use Illuminate\Support\Str; - -class ResetDatabase 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:reset'; - - /** - * The Command's short description - * - * @var string - */ - protected $description = 'Rollback, migrate and seed database.'; - - /** - * The Command's usage - * - * @var string - */ - protected $usage = 'db:reset [options]'; - - /** - * The Command's arguments - * - * @var array - */ - 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 - */ - protected $options = []; - - - public function run(array $params) - { - $this->rollback(); - $this->startMigration(); - CLI::info(self::ARROW_SYMBOL . 'Database migration completed!'); - CLI::newLine(); - - return 0; - } - - /** - * @return [type] - */ - protected function rollback() - { - $ext = '.php'; - $migrationsPath = config('paths.migrationsPath') . DIRECTORY_SEPARATOR; - $migrations = glob($migrationsPath . "*$ext"); - - foreach ($migrations as $migration) { - $file = pathinfo($migration); - $this->down($file, $migration); - } - - CLI::write(self::ARROW_SYMBOL . 'Database rollback completed!', 'green'); - CLI::newLine(); - } - - /** - * @param mixed $file - * @param mixed $migration - */ - protected function down($file, $migration) - { - require_once $migration; - $className = Str::studly(\substr($file['filename'], 17)); - - $migrationsPath = config('paths.migrationsPath') . DIRECTORY_SEPARATOR; - $migrationName = str_replace([$migrationsPath, '.php'], '', $migration); - - $class = new $className; - $class->down(); - - CLI::write(self::ARROW_SYMBOL . "db rollback on \"{$migrationName}\" ", 'green'); - } - - /** - * @return [type] - */ - protected function startMigration() - { - $ext = '.php'; - $migrationsPath = config('paths.migrationsPath') . DIRECTORY_SEPARATOR; - $migrations = glob($migrationsPath . "*$ext"); - - foreach ($migrations as $migration) { - $file = pathinfo($migration); - $filename = $file['filename']; - - if ($filename !== 'Schema') : - $className = Str::studly(\substr($filename, 17)); - - $this->migrate($className, $filename); - CLI::info(self::ARROW_SYMBOL . 'db migration on ' . str_replace($migrationsPath, '', $migration)); - - if (!CLI::getOption('noSeed')) { - $seederClass = str_replace( - 'Create', - '', - Str::studly(\substr($filename, 17)) - ); - - if ($seederClass) { - CLI::write(self::ARROW_SYMBOL . "\"{$seederClass}\" seeded successfully!", 'green'); - } - } - endif; - } - - CLI::info(self::ARROW_SYMBOL . "Database migration completed!"); - CLI::newLine(); - } - - /** - * @param mixed $className - * @param mixed $filename - */ - protected function migrate($className, $filename) - { - require_once config('paths.migrationsPath') . DIRECTORY_SEPARATOR . $filename . '.php'; - - $class = new $className; - $class->up(); - } -} diff --git a/core/libraries/Console/Commands/Database/RollbackDatabase.php b/core/libraries/Console/Commands/Database/RollbackDatabase.php deleted file mode 100644 index d607040..0000000 --- a/core/libraries/Console/Commands/Database/RollbackDatabase.php +++ /dev/null @@ -1,230 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - -namespace Console\Commands\Database; - -use Console\BaseCommand; -use Console\CLI; -use Illuminate\Support\Str; - -class RollbackDatabase 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:rollback'; - - /** - * The Command's short description - * - * @var string - */ - protected $description = 'Rollback all database migrations. (Don\'t use -s and -f together)'; - - /** - * The Command's usage - * - * @var string - */ - protected $usage = 'db:rollback [options]'; - - /** - * The Command's arguments - * - * @var array - */ - protected $arguments = [ - '-f ' => 'Rollback a particular file. (optional)', - '-s ' => 'The batch to rollback. (optional)' - ]; - - /** - * The Command's options - * - * @var array - */ - protected $options = []; - - /** - * The space name of the migration classes in the app - * @var string - */ - private $nameSpaceMirations = 'App\\Database\\Migrations\\'; - - /** - * @var int - */ - private int $numberOfValuesBeforeFileName = 20; - - - /** - * @param array $params - * - * @return int - */ - public function run(array $params) - { - try { - - $migrations = $this->getMigrationFiles(); - - $step = CLI::getOption('step'); - $fileToRollback = CLI::getOption('file'); - - $this->processOptions($step, $fileToRollback); - foreach ($migrations as $migration) { - $file = pathinfo($migration); - $this->handleMigration($file, $migration, $fileToRollback, $migrations); - } - - CLI::write(self::ARROW_SYMBOL . 'Database rollback completed!', 'green'); - CLI::newLine(); - return 0; - } catch (\Exception $e) { - CLI::error($e->getMessage()); - return 1; - } - } - - /** - * @param mixed $file - * @param mixed $migration - * @param mixed $fileToRollback - * @param mixed $migrations - */ - protected function handleMigration($file, $migration, $fileToRollback, $migrations) - { - // Lógica para manejar cada migración - require_once $migration; - $className = Str::studly(substr($file['filename'], $this->numberOfValuesBeforeFileName)); - - $migrationsPath = config('paths.migrationsPath') . DIRECTORY_SEPARATOR; - $migrationName = str_replace([$migrationsPath, '.php'], '', $migration); - - $className = $this->nameSpaceMirations . $className; - - $class = new $className; - $class->down(); - - CLI::write(self::ARROW_SYMBOL . "db rollback on \"{$migrationName}\" ", 'green'); - } - - /** - * Get all migration files. - * - * @return array - */ - protected function getMigrationFiles() - { - $ext = '.php'; - $migrationsPath = config('paths.migrationsPath') . DIRECTORY_SEPARATOR; - return glob($migrationsPath . "*$ext"); - } - - /** - * @param mixed $step - * @param mixed $fileToRollback - * - * @return void - */ - protected function processOptions($step, $fileToRollback) - { - if ($step !== 'all') { - $this->filterMigrationsByStep($step); - } - - if ($fileToRollback) { - $this->rollbackSpecificFile($fileToRollback); - } - } - - /** - * @param mixed $step - * @return void - */ - protected function filterMigrationsByStep($step) - { - $migrations = $this->getMigrationFiles(); - $migrations = array_slice($migrations, -$step, null, true); - - foreach ($migrations as $migration) { - $file = pathinfo($migration); - $this->handleMigration($file, $migration, null, $migrations); - } - } - - /** - * @param mixed $fileToRollback - * @return CLI - */ - protected function rollbackSpecificFile($fileToRollback) - { - $migrations = $this->getMigrationFiles(); - - foreach ($migrations as $migration) { - $file = pathinfo($migration); - - if (strpos($migration, Str::snake("_create_$fileToRollback.php")) !== false) { - $this->handleMigration($file, $migration, $fileToRollback, $migrations); - CLI::write(self::ARROW_SYMBOL . 'Database rollback completed!', 'green'); - CLI::newLine(); - exit(0); - } - } - - CLI::error("$fileToRollback not found!"); - exit(1); - } - - /** - * @param mixed $filename - * @return string - */ - protected function getNameAfterUnderscore($filename) - { - // Split the string into parts using underscore as the delimiter - $parts = explode('_', $filename); - - // The last part of the array is what we want - $nameAfterUnderscore = end($parts); - $this->numberOfValuesBeforeFileName = (int) (strlen(reset($parts)) + 1); - - return $nameAfterUnderscore; - } - - /** - * @param mixed $file - * @param mixed $migration - */ - protected function down($file, $migration) - { - require_once $migration; - $className = Str::studly(substr($file['filename'], $this->numberOfValuesBeforeFileName)); - - $migrationsPath = config('paths.migrationsPath') . DIRECTORY_SEPARATOR; - $migrationName = str_replace([$migrationsPath, '.php'], '', $migration); - - $class = new $className; - $class->down(); - - CLI::write(self::ARROW_SYMBOL . "db rollback on \"{$migrationName}\" ", 'green'); - } -} diff --git a/core/libraries/Console/Commands/Database/Seed.php b/core/libraries/Console/Commands/Database/Seed.php deleted file mode 100644 index 239ea77..0000000 --- a/core/libraries/Console/Commands/Database/Seed.php +++ /dev/null @@ -1,82 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - -namespace Console\Commands\Database; - -use Console\BaseCommand; -use Console\CLI; -use Database\Seeder; -use App\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 '; - - /** - * the Command's Arguments - * - * @var array - */ - 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(self::ARROW_SYMBOL . 'Migrations migSeeder', null, 'required'); // @codeCoverageIgnore - } - - try { - $seeder->call($seedName); - } catch (Throwable $e) { - $this->showError($e); - } - } -} diff --git a/core/libraries/Console/Commands/Database/SeedDatabase.php b/core/libraries/Console/Commands/Database/SeedDatabase.php deleted file mode 100644 index 1a22aa7..0000000 --- a/core/libraries/Console/Commands/Database/SeedDatabase.php +++ /dev/null @@ -1,102 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - -namespace Console\Commands\Database; - -use Console\BaseCommand; -use Console\CLI; - -class SeedDatabase 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 = 'Seed the database with records'; - - /** - * The Command's usage - * - * @var string - */ - protected $usage = 'db:seed [options]'; - - /** - * The Command's arguments - * - * @var array - */ - protected $arguments = []; - - /** - * The Command's options - * - * @var array - */ - protected $options = []; - - - /** - * @param array $params - * - * @return [type] - */ - public function run(array $params) - { - $ext = '.php'; - $seedsPath = config('paths.seedsPath') . DIRECTORY_SEPARATOR; - $seeds = glob($seedsPath . "*$ext"); - - if (!file_exists($seedsPath . 'DatabaseSeeder.php')) { - CLI::error(self::ARROW_SYMBOL . 'DatabaseSeeder not found! Refer to the docs.'); - return 1; - } - - if (count($seeds) === 1) { - CLI::error(self::ARROW_SYMBOL . 'No seeds found! Create one with the g:seed command.'); - return 1; - } - - $seeder = new Seeder; - - if (count($seeder->run()) === 0) { - CLI::error(self::ARROW_SYMBOL . 'No seeds registered. Add your seeds in DatabaseSeeder.php'); - return 1; - } - - foreach ($seeder->run() as $seed) { - $seeder->call($seed); - CLI::write(self::ARROW_SYMBOL . "\"{$seed}\" seeded successfully!", 'green'); - CLI::newLine(); - } - - CLI::info(self::ARROW_SYMBOL . 'Database seed complete'); - CLI::newLine(); - - return 0; - } -} diff --git a/core/libraries/Console/Commands/Encryption/GenerateKey.php b/core/libraries/Console/Commands/Encryption/GenerateKey.php deleted file mode 100644 index 8083316..0000000 --- a/core/libraries/Console/Commands/Encryption/GenerateKey.php +++ /dev/null @@ -1,179 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - -namespace Console\Commands\Encryption; - -use Console\BaseCommand; -use Console\CLI; -use Encryption\Encrypter; - -/** - * 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 - */ - 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(self::ARROW_SYMBOL . '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(self::ARROW_SYMBOL . 'Error in setting new encryption key to .env file.', 'light_gray', 'red'); - CLI::newLine(); - return; - } - - // force DotEnv to reload the new env vars - putenv('APP_KEY'); - unset($_ENV['APP_KEY'], $_SERVER['APP_KEY']); - CLI::write(self::ARROW_SYMBOL . '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 = (new Encrypter())->getKey(); - if ($prefix === 'hex2bin') { - return 'hex2bin:' . bin2hex($key); - } - - return 'base64:' . base64_encode($key); - } - - /** - * Sets the new encryption key in your .env file. - */ - protected function setNewEncryptionKey(string $key, array $params): bool - { - $currentKey = env('APP_KEY', ''); - - if ($currentKey !== '' && !$this->confirmOverwrite($params)) { - return false; - } - - return $this->writeNewEncryptionKeyToFile($currentKey, $key); - } - - /** - * Checks whether to overwrite existing encryption key. - */ - protected function confirmOverwrite(array $params): bool - { - return (array_key_exists('force', $params) - || CLI::getOption('force')) - || CLI::prompt(self::ARROW_SYMBOL . 'Overwrite existing key?', ['n', 'y']) === 'y'; - } - - /** - * Writes the new encryption key to .env file. - */ - protected function writeNewEncryptionKeyToFile(string $oldKey, string $newKey): bool - { - $baseEnv = ROOT_PATH . DIRECTORY_SEPARATOR . 'env'; - $envFile = ROOT_PATH . DIRECTORY_SEPARATOR . '.env'; - - if (!file_exists($envFile)) { - if (!file_exists($baseEnv)) { - CLI::write(self::ARROW_SYMBOL . 'Both default shipped `env` file and custom `.env` are missing.', 'yellow'); - CLI::write(self::ARROW_SYMBOL . 'Here\'s your new key instead: ' . CLI::color($newKey, 'yellow')); - CLI::newLine(); - return false; - } - - copy($baseEnv, $envFile); - } - - $ret = file_put_contents($envFile, preg_replace( - $this->keyPattern($oldKey), - "\nAPP_KEY={$newKey}", - file_get_contents($envFile) - )); - - return $ret !== 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]*APP_KEY[=\\s]*{$escaped}$/m"; - } -} diff --git a/core/libraries/Console/Commands/Generators/CommandGenerator.php b/core/libraries/Console/Commands/Generators/CommandGenerator.php deleted file mode 100644 index 6c3347f..0000000 --- a/core/libraries/Console/Commands/Generators/CommandGenerator.php +++ /dev/null @@ -1,119 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - -namespace Console\Commands\Generators; - -use Console\CLI; -use Console\BaseCommand; -use Console\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 axm command.'; - - /** - * The Command's Usage - * - * @var string - */ - protected $usage = 'make:command [options]'; - - /** - * The Command's Arguments - * - * @var array - */ - protected $arguments = [ - 'name' => 'The command class name.', - ]; - - /** - * The Command's Options - * - * @var array - */ - protected $options = [ - '--command' => 'The command name. Default: "command:name"', - '--type' => 'The command type. Options [basic, generator]. Default: "basic".', - '--group' => 'The command group. Default: [basic -> "Axm", 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 = 'Command class name '; - $this->execute($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)) { - $type = CLI::prompt(self::ARROW_SYMBOL . 'Command type', ['basic', 'generator'], 'required'); - CLI::newLine(); - } - - if (!is_string($group)) { - $group = $type === 'generator' ? 'Generators' : 'Axm'; - } - - return $this->parseTemplate( - $class, - [ - '{group}' => $group, - '{command}' => $command - ], - ['type' => $type] - ); - } -} diff --git a/core/libraries/Console/Commands/Generators/ConfigGenerator.php b/core/libraries/Console/Commands/Generators/ConfigGenerator.php deleted file mode 100644 index f9c8a40..0000000 --- a/core/libraries/Console/Commands/Generators/ConfigGenerator.php +++ /dev/null @@ -1,97 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - -namespace Console\Commands\Generators; - -use Console\BaseCommand; -use Console\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 [options]'; - - /** - * The Command's Arguments - * - * @var array - */ - protected $arguments = [ - 'name' => 'The config class name.', - ]; - - /** - * The Command's Options - * - * @var array - */ - protected $options = [ - '--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->execute($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); - } -} diff --git a/core/libraries/Console/Commands/Generators/ControllerGenerator.php b/core/libraries/Console/Commands/Generators/ControllerGenerator.php deleted file mode 100644 index 7f87be6..0000000 --- a/core/libraries/Console/Commands/Generators/ControllerGenerator.php +++ /dev/null @@ -1,99 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - -namespace Console\Commands\Generators; - -use Console\BaseCommand; -use Console\CLI; -use Console\GeneratorTrait; - -/** - * 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 [options]'; - - /** - * The Command's Arguments - * - * @var array - */ - protected $arguments = [ - 'name' => 'The controller class name.', - ]; - - /** - * The Command's Options - * - * @var array - */ - protected $options = [ - '--bare' => 'Extends from Controller instead of BaseController.', - '--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->execute($params); - } - - /** - * Prepare options and do the necessary replacements. - */ - protected function prepare(string $class): string - { - $extends = 'BaseController'; - - return $this->parseTemplate( - $class . 'Controller', - [ '{extends}' => $extends ], - ); - } -} diff --git a/core/libraries/Console/Commands/Generators/EntityGenerator.php b/core/libraries/Console/Commands/Generators/EntityGenerator.php deleted file mode 100644 index 83fa2e9..0000000 --- a/core/libraries/Console/Commands/Generators/EntityGenerator.php +++ /dev/null @@ -1,84 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - -namespace Console\Commands\Generators; - -use Console\BaseCommand; -use Console\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 [options]'; - - /** - * The Command's Arguments - * - * @var array - */ - protected $arguments = [ - 'name' => 'The entity class name.', - ]; - - /** - * The Command's Options - * - * @var array - */ - 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 = 'Class Name entity'; - $this->execute($params); - } -} diff --git a/core/libraries/Console/Commands/Generators/FilterGenerator.php b/core/libraries/Console/Commands/Generators/FilterGenerator.php deleted file mode 100644 index 0109324..0000000 --- a/core/libraries/Console/Commands/Generators/FilterGenerator.php +++ /dev/null @@ -1,84 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - -namespace Console\Commands\Generators; - -use Console\BaseCommand; -use Console\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 [options]'; - - /** - * The Command's Arguments - * - * @var array - */ - protected $arguments = [ - 'name' => 'The filter class name.', - ]; - - /** - * The Command's Options - * - * @var array - */ - 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->execute($params); - } -} diff --git a/core/libraries/Console/Commands/Generators/MigrationGenerator.php b/core/libraries/Console/Commands/Generators/MigrationGenerator.php deleted file mode 100644 index 2a5e6d4..0000000 --- a/core/libraries/Console/Commands/Generators/MigrationGenerator.php +++ /dev/null @@ -1,129 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - -namespace Console\Commands\Generators; - -use Console\CLI; -use Console\BaseCommand; -use Console\GeneratorTrait; - -/** - * 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 [options]'; - - /** - * The Command's Arguments - * - * @var array - */ - protected $arguments = [ - 'name' => 'The migration class name.', - ]; - - /** - * The Command's Options - * - * @var array - */ - protected $options = [ - '--table' => 'Table name to use for database sessions.', - '--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' . DIRECTORY_SEPARATOR . 'migrations'; - $this->template = 'migration.tpl.php'; - - $this->classNameLang = 'Class name migration'; - $this->execute($params); - } - - /** - * Prepare options and do the necessary replacements. - */ - protected function prepare(string $class): string - { - $table = CLI::prompt('Table name: ', [], 'required'); - CLI::newLine(); - - return $this->parseTemplate($class, ['{table}' => $table]); - } - - /** - * Change file basename before saving. - */ - protected function basename(string $filename): string - { - $timeFormat = config()->load('Migrations.php')->get('migrations.timestampFormat'); - return 'm-' . gmdate($timeFormat) . basename($filename); - } - - /** - * Filters and formats a given string to a valid PHP class name. - * - * The function applies the following rules: - * - Removes any characters that are not letters, numbers, or underscores. - * - Ensures the first character is a letter or underscore. - * - Converts the name to CamelCase format. - * @param string $className The name of the class to be formatted. - * @return string The formatted and valid PHP class name. - */ - function formatClassName($className) - { - // Remove characters that are not letters, numbers, or underscores - $filteredName = preg_replace('/[^\p{L}\p{N}_]/u', '', $className); - - // Ensure the first character is a letter or underscore - $filteredName = preg_replace('/^[^a-zA-Z_]+/', '', $filteredName); - - // Convert to CamelCase format - $filteredName = str_replace('_', '', ucwords($filteredName, '_')); - - return $filteredName; - } -} diff --git a/core/libraries/Console/Commands/Generators/ModelGenerator.php b/core/libraries/Console/Commands/Generators/ModelGenerator.php deleted file mode 100644 index fac75b0..0000000 --- a/core/libraries/Console/Commands/Generators/ModelGenerator.php +++ /dev/null @@ -1,100 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - -namespace Console\Commands\Generators; - -use Console\BaseCommand; -use Console\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 [options]'; - - /** - * The Command's Arguments - * @var array - */ - protected $arguments = [ - 'name' => 'The model class name.', - ]; - - /** - * The Command's Options - * @var array - */ - protected $options = [ - '--table' => 'Supply a table name. Default: "the lowercased plural of the class name".', - '--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. - * - * @param array $params - * @return void - */ - public function run(array $params) - { - $this->component = 'Model'; - $this->directory = 'Models'; - $this->template = 'model.tpl.php'; - - $this->classNameLang = 'Class name model'; - $this->execute($params); - } - - /** - * Prepare options and do the necessary replacements. - * - * @param string $class - * @return string - */ - protected function prepare(string $class): string - { - $table = $this->getOption('table'); - $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 $this->parseTemplate($class, ['{table}' => $table]); - } -} diff --git a/core/libraries/Console/Commands/Generators/ProviderGenerator.php b/core/libraries/Console/Commands/Generators/ProviderGenerator.php deleted file mode 100644 index e7da59d..0000000 --- a/core/libraries/Console/Commands/Generators/ProviderGenerator.php +++ /dev/null @@ -1,97 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - -namespace Console\Commands\Generators; - -use Console\BaseCommand; -use Console\GeneratorTrait; - - -/** - * Generates a skeleton provider file. - */ -class ProviderGenerator extends BaseCommand -{ - use GeneratorTrait; - - /** - * The Command's Group - * - * @var string - */ - protected $group = 'Generators'; - - /** - * The Command's Name - * - * @var string - */ - protected $name = 'make:provider'; - - /** - * The Command's Description - * - * @var string - */ - protected $description = 'Generates a new provider file.'; - - /** - * The Command's Usage - * - * @var string - */ - protected $usage = 'make:provider [options]'; - - /** - * The Command's Arguments - * - * @var array - */ - protected $arguments = [ - 'name' => 'The provider class name.', - ]; - - /** - * The Command's Options - * - * @var array - */ - protected $options = [ - '--force' => 'Force overwrite existing file.', - ]; - - /** - * Actually execute a command. - */ - public function run(array $params) - { - $this->component = 'Provider'; - $this->directory = 'Providers'; - $this->template = 'provider.tpl.php'; - - $this->classNameLang = 'CLI.generator.className.provider'; - $this->execute($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); - } -} diff --git a/core/libraries/Console/Commands/Generators/RouteGenerator.php b/core/libraries/Console/Commands/Generators/RouteGenerator.php deleted file mode 100644 index ab0fb57..0000000 --- a/core/libraries/Console/Commands/Generators/RouteGenerator.php +++ /dev/null @@ -1,96 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - -namespace Console\Commands\Generators; - -use Console\BaseCommand; -use Console\GeneratorTrait; - - -/** - * Generates a skeleton route file. - */ -class RouteGenerator extends BaseCommand -{ - use GeneratorTrait; - - /** - * The Command's Group - * - * @var string - */ - protected $group = 'Generators'; - - /** - * The Command's Name - * - * @var string - */ - protected $name = 'make:route'; - - /** - * The Command's Description - * - * @var string - */ - protected $description = 'Generates a new route file.'; - - /** - * The Command's Usage - * - * @var string - */ - protected $usage = 'make:route [options]'; - - /** - * The Command's Arguments - * - * @var array - */ - protected $arguments = [ - 'name' => 'The route class name.', - ]; - - /** - * The Command's Options - * - * @var array - */ - protected $options = [ - '--force' => 'Force overwrite existing file.', - ]; - - /** - * Actually execute a command. - */ - public function run(array $params) - { - $this->component = 'route'; - $this->directory = '../routes'; - $this->template = 'route.tpl.php'; - $this->classNameLang = 'CLI.generator.className.route'; - $this->execute($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); - } -} diff --git a/core/libraries/Console/Commands/Generators/ScaffoldGenerator.php b/core/libraries/Console/Commands/Generators/ScaffoldGenerator.php deleted file mode 100644 index f55e632..0000000 --- a/core/libraries/Console/Commands/Generators/ScaffoldGenerator.php +++ /dev/null @@ -1,121 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - - namespace Console\Commands\Generators; - - use Console\BaseCommand; - use Console\CLI; - use Console\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 [options]'; - - /** - * The Command's Arguments - * - * @var array - */ - protected $arguments = [ - 'name' => 'The class name', - ]; - - /** - * The Command's Options - * - * @var array - */ - 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)); - } -} diff --git a/core/libraries/Console/Commands/Generators/SeederGenerator.php b/core/libraries/Console/Commands/Generators/SeederGenerator.php deleted file mode 100644 index 44cab35..0000000 --- a/core/libraries/Console/Commands/Generators/SeederGenerator.php +++ /dev/null @@ -1,84 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - -namespace Console\Commands\Generators; - -use Console\BaseCommand; -use Console\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 [options]'; - - /** - * The Command's Arguments - * - * @var array - */ - protected $arguments = [ - 'name' => 'The seeder class name.', - ]; - - /** - * The Command's Options - * - * @var array - */ - 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' . DIRECTORY_SEPARATOR . 'Seeds'; - $this->template = 'seeder.tpl.php'; - - $this->classNameLang = 'CLI.generator.className.seeder'; - $this->execute($params); - } -} diff --git a/core/libraries/Console/Commands/Generators/SessionMigrationGenerator.php b/core/libraries/Console/Commands/Generators/SessionMigrationGenerator.php deleted file mode 100644 index ff5e128..0000000 --- a/core/libraries/Console/Commands/Generators/SessionMigrationGenerator.php +++ /dev/null @@ -1,110 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - - namespace Console\Commands\Generators; - - use Console\BaseCommand; - use Console\CLI; - use Console\GeneratorTrait; - -/** - * Generates a migration file for database sessions. - * - * @deprecated Use `make:migration --session` instead. - * - * @codeCoverageIgnore - */ -class SessionMigrationGenerator extends BaseCommand -{ - use GeneratorTrait; - - /** - * The Command's Group - * - * @var string - */ - protected $group = 'Generators'; - - /** - * The Command's Name - * - * @var string - */ - protected $name = 'session:migration'; - - /** - * The Command's Description - * - * @var string - */ - protected $description = '[DEPRECATED] Generates the migration file for database sessions, Please use "make:migration --session" instead.'; - - /** - * The Command's Usage - * - * @var string - */ - protected $usage = 'session:migration [options]'; - - /** - * The Command's Options - * - * @var array - */ - protected $options = [ - '-t' => 'Supply a table name.', - '-g' => 'Database group to use. Default: "default".', - ]; - - /** - * Actually execute a command. - */ - public function run(array $params) - { - $this->component = 'Migration'; - $this->directory = 'Database\Migrations'; - $this->template = 'migration.tpl.php'; - - $table = 'ci_sessions'; - - if (array_key_exists('t', $params) || CLI::getOption('t')) { - $table = $params['t'] ?? CLI::getOption('t'); - } - - $params[0] = "_create_{$table}_table"; - - $this->execute($params); - } - - /** - * Performs the necessary replacements. - */ - protected function prepare(string $class): string - { - $data['session'] = true; - $data['table'] = $this->getOption('t'); - $data['DBGroup'] = $this->getOption('g'); - $data['matchIP'] = config('app.sessionMatchIP') ?? false; - - $data['table'] = is_string($data['table']) ? $data['table'] : 'ci_sessions'; - $data['DBGroup'] = is_string($data['DBGroup']) ? $data['DBGroup'] : 'default'; - - return $this->parseTemplate($class, [], [], $data); - } - - /** - * Change file basename before saving. - */ - protected function basename(string $filename): string - { - return gmdate(config('migrations.timestampFormat')) . basename($filename); - } -} diff --git a/core/libraries/Console/Commands/Generators/Template/command.tpl.php b/core/libraries/Console/Commands/Generators/Template/command.tpl.php deleted file mode 100644 index 712331f..0000000 --- a/core/libraries/Console/Commands/Generators/Template/command.tpl.php +++ /dev/null @@ -1,74 +0,0 @@ -namespace {namespace}; - -use Console\BaseCommand; -use Console\CLI; - -use Console\GeneratorTrait; - - -class {class} extends BaseCommand -{ - - use GeneratorTrait; - - - /** - * 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) - { - - $this->component = 'Command'; - $this->directory = 'Commands'; - $this->template = 'command.tpl.php'; - - $this->execute($params); - - // - - } -} diff --git a/core/libraries/Console/Commands/Generators/Template/config.tpl.php b/core/libraries/Console/Commands/Generators/Template/config.tpl.php deleted file mode 100644 index 8001303..0000000 --- a/core/libraries/Console/Commands/Generators/Template/config.tpl.php +++ /dev/null @@ -1,17 +0,0 @@ -return [ - - 'data' => [ - - /** - -------------------------------------------------------------------------- - DESCRIPTION TITLE - -------------------------------------------------------------------------- - * - * Add here the explanation of the configuration so that developers - * can understand what it is about. - * @var string - */ - 'name' => 'Hola mundo', - ] - -]; diff --git a/core/libraries/Console/Commands/Generators/Template/controller.tpl.php b/core/libraries/Console/Commands/Generators/Template/controller.tpl.php deleted file mode 100644 index ceb5a28..0000000 --- a/core/libraries/Console/Commands/Generators/Template/controller.tpl.php +++ /dev/null @@ -1,21 +0,0 @@ -namespace {namespace}; - -use App\Controllers\BaseController; - -class {class} extends BaseController -{ - - public function __construct() - { - parent::__construct(); - } - - /** - * Return view - * @return mixed - */ - public function index() - { - view('pages.welcome'); - } -} \ No newline at end of file diff --git a/core/libraries/Console/Commands/Generators/Template/entity.tpl.php b/core/libraries/Console/Commands/Generators/Template/entity.tpl.php deleted file mode 100644 index 39a3ff9..0000000 --- a/core/libraries/Console/Commands/Generators/Template/entity.tpl.php +++ /dev/null @@ -1,10 +0,0 @@ -namespace {namespace}; - -use Entity\Entity; - -class {class} extends Entity -{ - protected $datamap = []; - protected $dates = ['created_at', 'updated_at', 'deleted_at']; - protected $casts = []; -} diff --git a/core/libraries/Console/Commands/Generators/Template/filter.tpl.php b/core/libraries/Console/Commands/Generators/Template/filter.tpl.php deleted file mode 100644 index 8df3893..0000000 --- a/core/libraries/Console/Commands/Generators/Template/filter.tpl.php +++ /dev/null @@ -1,45 +0,0 @@ -namespace {namespace}; - -use Filters\FilterInterface; -use HTTP\RequestInterface; -use 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 - * Axm\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 mixed - */ - 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 mixed - */ - public function after(RequestInterface $request, ResponseInterface $response, $arguments = null) - { - // - } -} diff --git a/core/libraries/Console/Commands/Generators/Template/migration.tpl.php b/core/libraries/Console/Commands/Generators/Template/migration.tpl.php deleted file mode 100644 index adeaa62..0000000 --- a/core/libraries/Console/Commands/Generators/Template/migration.tpl.php +++ /dev/null @@ -1,36 +0,0 @@ -use Database; -use Illuminate\Database\Schema\Blueprint; - -return new class extends Database -{ - /** - * Run the migrations. - */ - public function up(): void - { - self::db()::schema()->create('{table}', function (Blueprint $table) { - $table->increments('id'); - $table->string('uuid')->unique(); - $table->string('email')->unique(); - $table->string('name')->default(''); - $table->string('address')->default(''); - $table->string('country')->default(''); - $table->string('province')->default(''); - $table->string('zip_code')->default(''); - $table->string('password')->default(''); - $table->timestamp('email_verified_at')->nullable(); - $table->timestamp('last_used_at')->nullable(); - $table->timestamp('expires_at')->nullable(); - $table->rememberToken(); - $table->timestamps(); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - self::db()::schema()->dropIfExists('{table}'); - } -}; diff --git a/core/libraries/Console/Commands/Generators/Template/model.tpl.php b/core/libraries/Console/Commands/Generators/Template/model.tpl.php deleted file mode 100644 index aa4f573..0000000 --- a/core/libraries/Console/Commands/Generators/Template/model.tpl.php +++ /dev/null @@ -1,54 +0,0 @@ -namespace {namespace}; - -use Model\Model; - -/** -* Modelo {class} -* -* @category App -* @package Models -*/ -class {class} extends Model -{ - /** - * The database table used by the model. - */ - protected $table = '{table}'; - - /** - * The primary key for the model. - */ - protected $primaryKey = 'id'; - - /** - * Indicates if the IDs are auto-incrementing. - */ - public $incrementing = true; - - /** - * The data format to return on model retrieval. - */ - protected $casts = [ - 'email_verified_at' => 'datetime', - ]; - - /** - * The attributes that are mass assignable. - */ - protected $fillable = [ - 'name', 'email', 'password', - ]; - - /** - * The attributes that should be hidden for serialization. - */ - protected $hidden = [ - 'password', 'remember_token', - ]; - - /** - * The accessors to append to the model's array form. - */ - protected $appends = ['is_admin']; - -} diff --git a/core/libraries/Console/Commands/Generators/Template/provider.tpl.php b/core/libraries/Console/Commands/Generators/Template/provider.tpl.php deleted file mode 100644 index b4acc44..0000000 --- a/core/libraries/Console/Commands/Generators/Template/provider.tpl.php +++ /dev/null @@ -1,18 +0,0 @@ -return [ - - /** - -------------------------------------------------------------------------- - PROVIDERS TITLE - -------------------------------------------------------------------------- - * - * Add here the explanation of the provider so that developers - * can understand what it is about. - * - * @var string - */ - 'alias' => [ - 'closure' => fn () => Axm\ClassName(), - 'share' => true - ], - -] \ No newline at end of file diff --git a/core/libraries/Console/Commands/Generators/Template/route.tpl.php b/core/libraries/Console/Commands/Generators/Template/route.tpl.php deleted file mode 100644 index a808d97..0000000 --- a/core/libraries/Console/Commands/Generators/Template/route.tpl.php +++ /dev/null @@ -1,30 +0,0 @@ - -use Http\Router as Route; - -/* -|-------------------------------------------------------------------------- -| Web Routes -|-------------------------------------------------------------------------- -| -| Here is where you can register web routes for your application. -| -*/ - - -Route::get('/home', function () { - echo 'HOLa Mundo'; -}); - -Route::get('/welcome', App\Raxm\WelcomeComponent::class); - -Route::get('/auth-redirect/{provider:\w+}', [App\Raxm\AuthComponent::class, 'handlerAuthRedirect']); - -Route::group('/login', function () { - Route::get('/ruta1', function () { - echo ' Lógica de la ruta 1'; - }); - - Route::get('/ruta2', function () { - echo ' Lógica de la ruta 2'; - }); -}); diff --git a/core/libraries/Console/Commands/Generators/Template/seeder.tpl.php b/core/libraries/Console/Commands/Generators/Template/seeder.tpl.php deleted file mode 100644 index a17edb6..0000000 --- a/core/libraries/Console/Commands/Generators/Template/seeder.tpl.php +++ /dev/null @@ -1,11 +0,0 @@ -namespace {namespace}; - -use Database\Seeder; - -class {class} extends Seeder -{ - public function run() - { - // - } -} diff --git a/core/libraries/Console/Commands/Generators/Template/validation.tpl.php b/core/libraries/Console/Commands/Generators/Template/validation.tpl.php deleted file mode 100644 index e040a74..0000000 --- a/core/libraries/Console/Commands/Generators/Template/validation.tpl.php +++ /dev/null @@ -1,11 +0,0 @@ -<@php - -namespace {namespace}; - -class {class} -{ - // public function custom_rule(): bool - // { - // return true; - // } -} diff --git a/core/libraries/Console/Commands/Generators/ValidationGenerator.php b/core/libraries/Console/Commands/Generators/ValidationGenerator.php deleted file mode 100644 index 4b57453..0000000 --- a/core/libraries/Console/Commands/Generators/ValidationGenerator.php +++ /dev/null @@ -1,86 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - - namespace Console\Commands\Generators; - - use Console\BaseCommand; - use Console\CLI; - use Console\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 [options]'; - - /** - * The Command's Arguments - * - * @var array - */ - protected $arguments = [ - 'name' => 'The validation class name.', - ]; - - /** - * The Command's Options - * - * @var array - */ - 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->execute($params); - } -} diff --git a/core/libraries/Console/Commands/Help.php b/core/libraries/Console/Commands/Help.php deleted file mode 100644 index 7d93f5a..0000000 --- a/core/libraries/Console/Commands/Help.php +++ /dev/null @@ -1,88 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - -namespace Console\Commands; - -use Console\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 = 'Axm'; - - /** - * 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 - */ - protected $arguments = [ - 'command_name' => 'The command name [default: "help"]', - ]; - - /** - * the Command's Options - * - * @var array - */ - protected $options = []; - - /** - * Displays the help for spark commands. - */ - public function run(array $params) - { - $this->commands(); - - array_shift($params); - - $command = $params[0] ?? 'help'; - $commands = $this->commands->getCommands(); - - if (!$this->commands->verifyCommand($command, $commands)) { - return; - } - - $class = new $commands[$command]['class'](); - $class->showHelp(); - } -} diff --git a/core/libraries/Console/Commands/Housekeeping/ClearDebugbar.php b/core/libraries/Console/Commands/Housekeeping/ClearDebugbar.php deleted file mode 100644 index 7d2873d..0000000 --- a/core/libraries/Console/Commands/Housekeeping/ClearDebugbar.php +++ /dev/null @@ -1,68 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - -namespace Console\Commands\Housekeeping; - -use Console\BaseCommand; -use Console\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 = 'clear:debugbar'; - - /** - * The Command's usage - * - * @var string - */ - protected $usage = 'clear:debugbar'; - - /** - * The Command's short description. - * - * @var string - */ - protected $description = 'Clears all debugbar JSON files.'; - - /** - * Actually runs the command. - */ - public function run(array $params) - { - helpers('filesystem'); - if (!deleteFiles(STORAGE_PATH . DIRECTORY_SEPARATOR . 'framework' - . DIRECTORY_SEPARATOR . 'debugbar')) { - - CLI::error(self::ARROW_SYMBOL . 'Error deleting the debugbar JSON files.'); - CLI::newLine(); - return; - } - - CLI::write(self::ARROW_SYMBOL . 'Debugbar cleared.', 'green'); - CLI::newLine(); - } -} diff --git a/core/libraries/Console/Commands/Housekeeping/ClearLogs.php b/core/libraries/Console/Commands/Housekeeping/ClearLogs.php deleted file mode 100644 index d7d6a86..0000000 --- a/core/libraries/Console/Commands/Housekeeping/ClearLogs.php +++ /dev/null @@ -1,85 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - -namespace Console\Commands\Housekeeping; - -use Console\BaseCommand; -use Console\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 = 'clear:logs'; - - /** - * The Command's short description - * - * @var string - */ - protected $description = 'Clears all log files.'; - - /** - * The Command's usage - * - * @var string - */ - protected $usage = 'clear:logs [option]'; - - /** - * The Command's options - * - * @var array - */ - 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') { - CLI::error(self::ARROW_SYMBOL . 'Deleting logs aborted.', 'light_gray', 'red'); - CLI::error(self::ARROW_SYMBOL . 'If you want, use the "-force" option to force delete all log files.', 'light_gray', 'red'); - CLI::newLine(); - return; - } - - helpers('filesystem'); - $path = config('paths.logsPath'); - if (!deleteFiles($path, false, true)) { - CLI::error(self::ARROW_SYMBOL . 'Error in deleting the logs files.', 'light_gray', 'red'); - CLI::newLine(); - return; - } - - CLI::write(self::ARROW_SYMBOL . 'Logs cleared.', 'green'); - CLI::newLine(); - } -} diff --git a/core/libraries/Console/Commands/ListCommands.php b/core/libraries/Console/Commands/ListCommands.php deleted file mode 100644 index 39402be..0000000 --- a/core/libraries/Console/Commands/ListCommands.php +++ /dev/null @@ -1,134 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - -namespace Console\Commands; - -use Console\BaseCommand; -use Console\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 = 'Axm'; - - /** - * 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 - */ - protected $arguments = []; - - /** - * the Command's Options - * - * @var array - */ - 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) - { - $this->commands(); - - $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. - */ - 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. - */ - protected function listSimple(array $commands) - { - foreach (array_keys($commands) as $title) { - CLI::write($title); - } - } -} diff --git a/core/libraries/Console/Commands/Server/Serve.php b/core/libraries/Console/Commands/Server/Serve.php deleted file mode 100644 index 9ce5e85..0000000 --- a/core/libraries/Console/Commands/Server/Serve.php +++ /dev/null @@ -1,309 +0,0 @@ - 'The PHP Binary [default: "PHP_BINARY"]', - '--host' => 'The HTTP Host [default: "localhost"]', - '--port' => 'The HTTP Host Port [default: "8080"]', - ]; - - /** - * The current port offset. - * @var int - */ - protected $portOffset = 0; - - /** - * The max number of ports to attempt to serve from - * @var int - */ - protected $maxTries = 10; - - /** - * Default port number - * @var int - */ - protected int $defaultPort = 8080; - - /** - * - */ - protected $process; - - /** - * Run the server - */ - public function run(array $params) - { - // Collect any user-supplied options and apply them. - $php = CLI::getOption('php', PHP_BINARY); - $host = CLI::getOption('host', 'localhost'); - $port = (int) CLI::getOption('port', $this->defaultPort); - - $scheme = 'http'; // Assuming default scheme is http for simplicity - - // Attempt alternative ports - if (!$port = $this->findAvailablePort($host, $port)) { - CLI::error('Could not bind to any port'); - exit; - } - - // Server up - $this->printServerInfo($scheme, $host, $port); - $this->startServer($php, $host, $port); - } - - /** - * Find an available port - */ - protected function findAvailablePort(string $host, int $port): ?int - { - $maxTries = $this->maxTries; - for ($i = 0; $i < $maxTries; $i++) { - if ($this->checkPort($host, $port)) { - return $port; - } - - $port++; - } - - return false; - } - - /** - * Check if a port is available - */ - protected function checkPort(string $host, int $port): bool - { - $socket = @fsockopen($host, $port); - if ($socket !== false) { - fclose($socket); - return false; - } - return true; - } - - /** - * Shutdown the server - */ - protected function shutdown() - { - CLI::info('Shutting down the server...'); - proc_terminate($this->process); - } - - /** - * Start the server - */ - protected function startServer(string $php, string $host, int $port) - { - // Path Root. - $fcroot = getcwd(); - if (is_dir($fcroot)) { - $descriptors = [ - 0 => ['pipe', 'r'], // stdin - 1 => STDOUT, // stdout - 2 => STDERR // stderr - ]; - - $command = "{$php} -S {$host}:{$port} -t {$fcroot}"; - $this->process = proc_open($command, $descriptors, $pipes); - - if (is_resource($this->process)) { - while ($output = fgets($pipes[0])) { - if (strpos($output, 'SIGINT') !== false) { - $this->shutdown(); - } - } - } - - $code = proc_close($this->process); - if ($code !== 0) { - throw new RuntimeException("Unknown error (code: $code)", $code); - } - } - } - - /** - * Print server information - */ - protected function printServerInfo(string $scheme, string $host, int $port) - { - CLI::info(self::ARROW_SYMBOL . 'Axm development server started on: ' . CLI::color("{$scheme}://{$host}:{$port}", 'green')); - - CLI::newLine(); - CLI::write(self::ARROW_SYMBOL . 'Press Control-C to stop.', 'yellow'); - CLI::newLine(2); - } -} - - - - -// declare(strict_types=1); - -// namespace Console\Commands\Server; - -// use Console\BaseCommand; -// use Console\CLI; -// use RuntimeException; - -// /** -// * Class Serve -// * -// * Launch the Axm PHP Development Server -// * @package Console\Commands\Server -// */ -// class Serve extends BaseCommand -// { - -// /** -// * Group -// * @var string -// */ -// protected $group = 'Axm'; - -// /** -// * Name -// * @var string -// */ -// protected $name = 'serve'; - -// /** -// * Description -// * @var string -// */ -// protected $description = 'Launches the Axm PHP Development Server'; - -// /** -// * Usage -// * @var string -// */ -// protected $usage = 'serve [--host] [--port]'; - -// /** -// * Options -// * @var array -// */ -// protected $options = [ -// '--php' => 'The PHP Binary [default: "PHP_BINARY"]', -// '--host' => 'The HTTP Host [default: "localhost"]', -// '--port' => 'The HTTP Host Port [default: "8080"]', -// ]; - -// /** -// * The current port offset. -// * @var int -// */ -// protected $portOffset = 0; - -// /** -// * The max number of ports to attempt to serve from -// * @var int -// */ -// protected $maxTries = 10; - -// /** -// * Default port number -// * @var int -// */ -// protected int $defaultPort = 8080; - -// private string $host; -// private int $port; -// private string $documentRoot; -// private string $phpBinary; -// private $process; - -// public function run(array $params) -// { -// $php = CLI::getOption('php', PHP_BINARY); -// $host = CLI::getOption('host', 'localhost'); -// $port = (int) CLI::getOption('port', $this->defaultPort); - -// $this->documentRoot = getcwd(); -// $this->validateOptions(); - -// $command = "{$php} -S {$host}:{$port} -t {$this->documentRoot}"; - -// $descriptors = [ -// 0 => ['pipe', 'r'], // stdin -// 1 => STDOUT, // stdout -// 2 => STDERR // stderr -// ]; - -// $this->process = proc_open($command, $descriptors, $pipes); - -// if (!is_resource($this->process)) { -// throw new RuntimeException("Failed to start server process."); -// } - -// register_shutdown_function([$this, 'shutdown']); - -// echo "Development server started at http://{$host}:{$port}\n"; -// echo "Document root: {$this->documentRoot}\n"; -// echo "Press Ctrl+C to stop the server.\n"; - -// while (proc_get_status($this->process)['running']) { -// usleep(100000); // Sleep for 100 milliseconds -// } -// } - -// protected function validateOptions(): void -// { -// if (!is_dir($this->documentRoot) || !is_readable($this->documentRoot)) { -// throw new RuntimeException("Invalid document root: {$this->documentRoot}."); -// } -// } - -// public function shutdown(): void -// { -// if ($this->process !== null) { -// echo "Shutting down the server...\n"; -// proc_terminate($this->process); -// } -// } -// } diff --git a/core/libraries/Console/Commands/Utilities/AddProvider.php b/core/libraries/Console/Commands/Utilities/AddProvider.php deleted file mode 100644 index be52524..0000000 --- a/core/libraries/Console/Commands/Utilities/AddProvider.php +++ /dev/null @@ -1,167 +0,0 @@ - 'service alias', - '--class' => 'FQN of the service provider class.', - '--description' => 'Library description. (Opcional)', - '--paths' => 'Address of the framework service provider file. (Optional)' - ]; - - /** - * The Command's Options - * @var array - */ - protected $options = []; - - /** - * Actually execute a command. - * @param array $params - */ - public function run(array $params) - { - $serviceName = $params[1] ?? CLI::getOption('alias') ?? $params['alias'] ?? CLI::prompt(self::ARROW_SYMBOL . 'Add alias: ', null, 'required|text'); - $serviceClass = $params[2] ?? CLI::getOption('class') ?? $params['class'] ?? CLI::prompt(self::ARROW_SYMBOL . 'Class Name (note: with namespace): ', null, 'required|text'); - $description = $params[3] ?? CLI::getOption('description') ?? $params['description'] ?? CLI::prompt(self::ARROW_SYMBOL . 'Description (optional): ', null, 'text') ?? []; - $configFile = config('paths.providersPath') . DIRECTORY_SEPARATOR . 'providers.php'; - - $this->addService($serviceName, $serviceClass, $description, $configFile); - } - - /** - * Add a new service to the configuration file if it doesn't already exist. - * - * @param string $serviceName The name of the service to add. - * @param string $serviceClass The class of the service to add. - * @param string $configFile The path to the configuration file. - */ - public function addService(string $serviceName, string $serviceClass, string $description = null, string $configFile) - { - // Check if the service already exists in the file - if ($this->serviceExists($serviceName, $configFile)) { - return; - } - - // Read the current content of the file - $currentConfig = file_get_contents($configFile); - - // Define the new service - $newService = $this->renderProvider($serviceName, $serviceClass, $description); - - // Replace the last closing bracket with the new service and the original closing bracket - $modifiedConfig = preg_replace('/\];/', $newService, $currentConfig, 1); - - // Write the modified configuration back to the file - file_put_contents($configFile, $modifiedConfig); - - CLI::write(self::ARROW_SYMBOL . "Service '{$serviceName}' added successfully.", 'green'); - CLI::newLine(); - } - - /** - * Check if a service already exists in the configuration file. - * - * @param string $serviceName The name of the service to check. - * @param string $configFile The path to the configuration file. - * @return bool True if the service already exists, false otherwise. - */ - public function serviceExists(string $serviceName, string $configFile) - { - $currentConfig = file_get_contents($configFile); - return strpos($currentConfig, "'{$serviceName}'") !== false; - } - - /** - * Render a service provider array for the given service name and class. - * - * @param string $serviceName - * @param string $serviceClass - * @return string - */ - public function renderProvider(string $serviceName, string $serviceClass, string $description = null): string - { - $service = strtoupper($serviceName); - $descriptionClass = ucfirst($serviceName); - $description = $this->addComment($description ?? "The $descriptionClass Class is a service provider.", 60); - $newService = << {$serviceClass}::class, - EOT; - - return $newService . "\n\n];"; - } - - /** - * addComment into lines with a specified line length, ensuring that words are not cut off. - * - * @param string $text The input text. - * @param int $lineLength The desired length of each line. - * @return string The formatted text with line breaks. - */ - public function addComment(string $text, int $lineLength) - { - $words = explode(" ", $text); - $lines = []; - $currentLine = ''; - - foreach ($words as $word) { - // Check if adding the word exceeds the line length - if (strlen($currentLine . $word) <= $lineLength) { - $currentLine .= $word . ' '; - } else { - // Add the current line to the lines array and reset the line - $lines[] = rtrim($currentLine); - $currentLine = $word . ' '; - } - } - - // Add the last line to the lines array - $lines[] = rtrim($currentLine); - - // Join the lines with line breaks - $formattedText = implode("\n * ", $lines); - - return $formattedText; - } -} diff --git a/core/libraries/Console/Commands/Utilities/Colors.php b/core/libraries/Console/Commands/Utilities/Colors.php deleted file mode 100644 index 0e5f951..0000000 --- a/core/libraries/Console/Commands/Utilities/Colors.php +++ /dev/null @@ -1,148 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - -namespace Console\Commands\Utilities; - -use Console\BaseCommand; -use Console\CLI; - -/** - * [Description Colors] - */ -class Colors extends BaseCommand -{ - /** - * The group the command is lumped under - * when listing commands. - * - * @var string - */ - protected $group = 'Utilities'; - - /** - * The Command's name - * @var string - */ - protected $name = 'colors'; - - /** - * the Command's short description - * @var string - */ - protected $description = 'Displays the colors supported by the console.'; - - /** - * the Command's usage - * @var string - */ - protected $usage = 'colors [options]'; - - /** - * The Command's arguments - * @var array - */ - protected $options = [ - '--256' => 'Displays 256 colors if supported, otherwise it will display the console default colors.', - ]; - - /** - * @var string - */ - protected $driver = null; - - /** - * Creates a new database. - * - * @param array $params - */ - public function run(array $params) - { - $this->printFormattedBlock('reset'); - CLI::newLine(); - CLI::newLine(); - CLI::newLine(); - - $c_256 = array_key_exists('256', $params) || CLI::getOption('256'); - if ($c_256) { - $this->colors_256(); - } - } - - /** - * Function for printing a block of formatted text in the console - * - * @param mixed $format - * @return void - */ - protected function printFormattedBlock($format) - { - // Códigos ANSI para formato - $formatCodes = [ - 'reset' => "\033[0m", - 'bold' => "\033[1m", - 'underline' => "\033[4m", - 'blink' => "\033[5m", - 'reverse' => "\033[7m", - ]; - - // ANSI codes for background and foreground colors - $backgroundColors = range(40, 47); - $foregroundColors = range(30, 37); - - foreach ($backgroundColors as $clbg) { - foreach ($foregroundColors as $clfg) { - foreach ($formatCodes as $attr => $code) { - $txt = '' . $attr; - $output = sprintf("\033[%d;%d;%dm %s %s \033[0m", $attr, $clbg, $clfg, $code, $txt); - echo $output; - } - echo PHP_EOL; - } - } - } - - /** - * colors_256 - * - * @return void - */ - public function colors_256() - { - CLI::write('256 colors: '); - CLI::newLine(); - - // Loop for background and foreground colors - foreach ([38, 48] as $fgbg) { - foreach (range(0, 255) as $color) { - - $this->printColorBlocks($fgbg, $color); - } - - echo PHP_EOL; - } - } - - /** - * Function for printing text blocks with foreground/background colors - * @param mixed $fgbg - * @param mixed $color - */ - function printColorBlocks($fgbg, $color) - { - // Imprimir el color - printf("\033[%d;5;%sm %3s \033[0m", $fgbg, $color, $color); - - // Imprimir 6 colores por línea - if (($color + 1) % 6 == 4) { - echo PHP_EOL; - } - } -} diff --git a/core/libraries/Console/Commands/Utilities/Environment.php b/core/libraries/Console/Commands/Utilities/Environment.php deleted file mode 100644 index fe398f3..0000000 --- a/core/libraries/Console/Commands/Utilities/Environment.php +++ /dev/null @@ -1,165 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - -namespace Console\Commands\Utilities; - -use Axm; -use Console\BaseCommand; -use Console\CLI; - -/** - * 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 = 'Axm'; - - /** - * 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 []'; - - /** - * The Command's arguments - * - * @var array - */ - 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 - */ - protected $options = []; - - /** - * Allowed values for environment. `testing` is excluded - * since spark won't work on it. - * - * @var array - */ - private static $knownTypes = [ - 'production', - 'debug', - ]; - - /** - * {@inheritDoc} - */ - public function run(array $params) - { - array_shift($params); - - $environment = app()->getEnvironment(); - if ($params === []) { - CLI::write(sprintf(self::ARROW_SYMBOL . 'Your environment is currently set as %s.', CLI::color($environment, 'green'))); - CLI::newLine(); - return; - } - - $env = strtolower(array_shift($params)); - - if ($env === 'testing') { - CLI::error(self::ARROW_SYMBOL . 'The "testing" environment is reserved for PHPUnit testing.', 'light_gray', 'red'); - CLI::error(self::ARROW_SYMBOL . 'You will not be able to run axm under a "testing" environment.', 'light_gray', 'red'); - CLI::newLine(); - return; - } - - if (!in_array($env, self::$knownTypes, true)) { - CLI::error(sprintf(self::ARROW_SYMBOL . 'Invalid environment type "%s". Expected one of "%s".', $env, implode('" and "', self::$knownTypes)), 'light_gray', 'red'); - CLI::newLine(); - return; - } - - if (!$this->writeNewEnvironmentToConfigFile($env)) { - CLI::error(self::ARROW_SYMBOL . 'Error in writing new environment to .env file.', 'light_gray', 'red'); - CLI::newLine(); - return; - } - - CLI::write(sprintf(self::ARROW_SYMBOL . 'Environment is successfully changed to "%s".', $env), 'green'); - CLI::write(self::ARROW_SYMBOL . '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 - */ - function writeNewEnvironmentToConfigFile($mode) - { - // Define the allowed values for the mode - $modeValues = [ - 'production' => 'production', - 'debug' => 'debug', - ]; - - // Check if the mode is valid - if (!array_key_exists($mode, $modeValues)) { - return false; - } - - // Ruta al archivo .env - $envFilePath = ROOT_PATH . DIRECTORY_SEPARATOR . '.env'; - - // Check if the .env file exists - if (!is_file($envFilePath)) { - CLI::write(self::ARROW_SYMBOL . 'The configuration file ".env" is not found.', 'yellow'); - CLI::newLine(); - - return false; - } - - // Read the contents of the .env file - $envContents = file_get_contents($envFilePath); - - // Replace APP_ENVIRONMENT value in .env file - $envContents = preg_replace( - '/(^APP_ENVIRONMENT\s*=\s*).*/m', - '${1}' . $modeValues[$mode], - $envContents - ); - - // Write changes back to the .env file - if (file_put_contents($envFilePath, $envContents) !== false) { - return true; - } - - return false; - } -} diff --git a/core/libraries/Console/Commands/Utilities/Install.php b/core/libraries/Console/Commands/Utilities/Install.php deleted file mode 100644 index 79bdafd..0000000 --- a/core/libraries/Console/Commands/Utilities/Install.php +++ /dev/null @@ -1,103 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - -namespace Console\Commands\Utilities; - -use Console\BaseCommand; -use Console\CLI; -use Exception; - -class Install extends BaseCommand -{ - /** - * The Command's Group - * - * @var string - */ - protected $group = 'Utilities'; - - /** - * The Command's Name - * - * @var string - */ - protected $name = 'install'; - - /** - * The Command's Description - * - * @var string - */ - protected $description = 'Install a new package'; - - /** - * The Command's Usage - * - * @var string - */ - protected $usage = 'install [name] --option'; - - /** - * 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) - { - $composerLockPath = ROOT_PATH . DIRECTORY_SEPARATOR . 'composer.lock'; - $composerJsonPath = ROOT_PATH . DIRECTORY_SEPARATOR . 'composer.json'; - - if (!is_file($composerJsonPath)) { - CLI::error(self::ARROW_SYMBOL . 'composer.json file not found in current directory.'); - CLI::newLine(); - return; - } - - if (!is_file($composerLockPath)) { - $this->runCommand('composer update'); - } else { - $this->runCommand('composer install'); - } - } - - /** - * Run a command and handle errors. - * - * @param string $command - * @return void - */ - private function runCommand(string $command) - { - try { - exec($command); - CLI::success(self::ARROW_SYMBOL . 'The package was successfully installed.'); - CLI::newLine(); - } catch (Exception $e) { - CLI::error(self::ARROW_SYMBOL . 'An error occurred while installing the package:'); - CLI::error($e->getMessage()); - CLI::newLine(); - } - } -} diff --git a/core/libraries/Console/Commands/Utilities/Maintenance.php b/core/libraries/Console/Commands/Utilities/Maintenance.php deleted file mode 100644 index 6ee5ed2..0000000 --- a/core/libraries/Console/Commands/Utilities/Maintenance.php +++ /dev/null @@ -1,120 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - -namespace Console\Commands\Utilities; - -use Axm; -use Console\BaseCommand; -use Console\CLI; - -/** - * Command to display the current environment, - * or set a new one in the `.env` file. - */ -final class Maintenance extends BaseCommand -{ - /** - * The group the command is lumped under - * when listing commands. - * - * @var string - */ - protected $group = 'Axm'; - - /** - * The Command's name - * - * @var string - */ - protected $name = 'maintenance'; - - /** - * The Command's short description - * - * @var string - */ - protected $description = 'Activate or desactivate the app maintenance.'; - - /** - * The Command's usage - * - * @var string - */ - protected $usage = 'maintenance [options]'; - - /** - * The Command's arguments - * - * @var array - */ - protected $arguments = []; - - /** - * The Command's options - * - * @var array - */ - protected $options = []; - - /** - * {@inheritDoc} - */ - public function run(array $params) - { - $mode = $params[1] ?? []; - if (!$mode) { - $mode = CLI::prompt(self::ARROW_SYMBOL . 'Add the operation to follow.', ['true', 'false']); - } - - if (!$this->writeNewEnvironmentToConfigFile($mode)) { - CLI::error(self::ARROW_SYMBOL . 'Error in writing new environment to .env file.', 'light_gray', 'red'); - CLI::newLine(); - return; - } - - CLI::write(sprintf(self::ARROW_SYMBOL . 'Maintenance mode: "%s".', $mode), 'green'); - CLI::newLine(); - } - - /** - * @see https://regex101.com/r/4sSORp/1 for the regex in action - */ - function writeNewEnvironmentToConfigFile($mode) - { - // Ruta al archivo .env - $envFilePath = ROOT_PATH . DIRECTORY_SEPARATOR . '.env'; - - // Check if the .env file exists - if (!is_file($envFilePath)) { - CLI::write(self::ARROW_SYMBOL . 'The configuration file ".env" is not found.', 'yellow'); - CLI::newLine(); - - return false; - } - - // Read the contents of the .env file - $envContents = file_get_contents($envFilePath); - - // Replace APP_DOWN value in .env file - $envContents = preg_replace( - '/(^APP_DOWN\s*=\s*).*/m', - '${1}' . $mode, - $envContents - ); - - // Write changes back to the .env file - if (file_put_contents($envFilePath, $envContents) !== false) { - return true; - } - - return false; - } -} diff --git a/core/libraries/Console/Commands/Utilities/Namespaces.php b/core/libraries/Console/Commands/Utilities/Namespaces.php deleted file mode 100644 index f60d010..0000000 --- a/core/libraries/Console/Commands/Utilities/Namespaces.php +++ /dev/null @@ -1,104 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - -namespace Console\Commands\Utilities; - -use Axm; -use Console\BaseCommand; -use Console\CLI; - -/** - * Lists namespaces set in Config\Autoload with their - * full server path. Helps you to verify that you have - * the namespaces setup correctly. - */ -class Namespaces extends BaseCommand -{ - /** - * The group the command is lumped under - * when listing commands. - * - * @var string - */ - protected $group = 'Axm'; - - /** - * 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 - */ - protected $arguments = []; - - /** - * the Command's Options - * - * @var array - */ - protected $options = []; - - /** - * Displays the help for the spark cli script itself. - */ - public function run(array $params) - { - $config = $this->getFromComposer(); - - $tbody = []; - foreach ($config as $ns => $path) { - $path = realpath($path) ?: $path; - - $tbody[] = [ - $ns, - realpath($path) ?: $path, - is_dir($path) ? CLI::color('Yes', 'blue') : CLI::color('MISSING', 'red'), - ]; - } - - $thead = [ - CLI::color('Namespace', 'green'), - CLI::color('Path', 'green'), - CLI::color('Found?', 'green'), - ]; - - CLI::table($tbody, $thead); - } - - - public function getFromComposer() - { - // Carga la configuración de espacios de nombres PSR-4 desde el archivo composer.json - $composerJson = json_decode(file_get_contents(ROOT_PATH . DIRECTORY_SEPARATOR . 'composer.json'), true); - $autoloadConfig = $composerJson['autoload']['psr-4']; - - return (array) $autoloadConfig; - } -} diff --git a/core/libraries/Console/Commands/Utilities/Publish.php b/core/libraries/Console/Commands/Utilities/Publish.php deleted file mode 100644 index 3f33c45..0000000 --- a/core/libraries/Console/Commands/Utilities/Publish.php +++ /dev/null @@ -1,103 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - -namespace Console\Commands\Utilities; - -use Axm; -use Console\BaseCommand; -use Console\CLI; -use 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 = 'Axm'; - - /** - * 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 []'; - - /** - * The Command's arguments - * - * @var array - */ - protected $arguments = [ - 'directory' => '[Optional] The directory to scan within each namespace. Default: "Publishers".', - ]; - - /** - * the Command's Options - * - * @var array - */ - protected $options = []; - - /** - * Displays the help for the axm cli script itself. - */ - public function run(array $params) - { - $directory = array_shift($params) ?? 'Publishers'; - if ([] === $publishers = (new Publisher)->discover($directory)) { - CLI::write(self::ARROW_SYMBOL . 'Publish Missing %s ', [$directory]); - return; - } - - foreach ($publishers as $publisher) { - if ($publisher->publish()) { - CLI::write(self::ARROW_SYMBOL . '%s published %s file(s) to %s.', [ - get_class($publisher), - count($publisher->getPublished()), - $publisher->getDestination(), - ], 'green'); - } else { - CLI::error(self::ARROW_SYMBOL . '%s failed to publish to %s!', [ - get_class($publisher), - $publisher->getDestination(), - ], 'light_gray', 'red'); - - foreach ($publisher->getErrors() as $file => $e) { - CLI::write($file); - CLI::error($e->getMessage()); - CLI::newLine(); - } - } - } - } -} diff --git a/core/libraries/Console/Commands/Utilities/Routes.php b/core/libraries/Console/Commands/Utilities/Routes.php deleted file mode 100644 index d82a0a7..0000000 --- a/core/libraries/Console/Commands/Utilities/Routes.php +++ /dev/null @@ -1,145 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - -namespace Console\Commands\Utilities; - -use Console\BaseCommand; -use Console\CLI; -use Http\Router; - -/** - * Lists all of the user-defined routes. This will include any Routes files - * that can be discovered, but will NOT include any routes that are not defined - * in a routes file, 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 = 'Axm'; - - /** - * The Command's name - * @var string - */ - protected $name = 'routes'; - - /** - * the Command's short description - * @var string - */ - protected $description = 'Displays all of user-defined routes. Does NOT display auto-detected routes.'; - - /** - * the Command's usage - * @var string - */ - protected $usage = 'routes'; - - /** - * the Command's Arguments - * @var array - */ - protected $arguments = []; - - /** - * the Command's Options - * @var array - */ - protected $options = []; - - /** - * @var Router|null - */ - private ?Router $router; - - - /** - * Displays the help for the spark cli script itself. - */ - public function run(array $params) - { - $collection = $this->router(); - $methods = $collection::$verbs; // get the verbs ['get',post,head....] - - $tbody = []; - foreach ($methods as $method) { - $routes = $collection->getRoutes($method); - - foreach ($routes as $route => $handler) { - // filter for strings, as callbacks aren't displayable - if (is_string($handler)) { - $tbody[] = [ - $method, - $route, - $handler, - ]; - } - if (is_array($handler) || is_object($handler)) { - $tbody[] = [ - $method, - $route, - $this->getProcessAddressHandle($handler) - ]; - } - } - } - - $thead = [ - CLI::color('Method', 'green'), - CLI::color('Route', 'green'), - CLI::color('Handler|Dir', 'green'), - ]; - - CLI::table($tbody, $thead); - } - - /** - * router - * @return Router - */ - private function router(): Router - { - if (!isset($this->routes)) { - $this->router = app()->router; - } - - return $this->router; - } - - /** - * Returns a string representing the handler of a path based on the data - * provided, used to identify the controller and method associated with a given - * path in a web system. - */ - public function getProcessAddressHandle($data): string - { - if (is_object($data)) { - $output = 'Object(' . get_class($data) . ')'; - } elseif (is_array($data)) { - $output = ''; - if (is_object($data[0])) { - $output .= 'Object(' . get_class($data[0]) . ')'; - } else { - $output .= $data[0]; - } - $output .= '::' . $data[1]; - } else { - $output = ''; - } - - return $output; - } -} diff --git a/core/libraries/Console/Commands/Utilities/Test.php b/core/libraries/Console/Commands/Utilities/Test.php deleted file mode 100644 index 1e93055..0000000 --- a/core/libraries/Console/Commands/Utilities/Test.php +++ /dev/null @@ -1,95 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - -namespace Console\Commands\Utilities; - -use Axm; -use Console\BaseCommand; -use Console\CLI; - -class Test extends BaseCommand -{ - /** - * The Command's Group - * - * @var string - */ - protected $group = 'Utilities'; - - /** - * The Command's Name - * - * @var string - */ - protected $name = 'test'; - - /** - * The Command's Description - * - * @var string - */ - protected $description = 'This command executes the unit tests'; - - /** - * The Command's Usage - * - * @var string - */ - protected $usage = 'test'; - - /** - * The Command's Arguments - * - * @var array - */ - protected $arguments = []; - - /** - * The Command's Options - * - * @var array - */ - protected $options = []; - - /** - * Allowed test - * @var array - */ - private static $testTypes = [ - 'phpunit', - 'pestphp', - ]; - - - /** - * @param array $params - */ - public function run(array $params) - { - array_shift($params); - - if (empty($params)) { - $testEngine = self::$testTypes[0]; - } else { - $testEngine = strtolower(array_shift($params)); - - if (!in_array($testEngine, self::$testTypes)) { - CLI::error(self::ARROW_SYMBOL .'Invalid test engine. Please use "phpunit" or "pestphp".', 'light_gray', 'red'); - CLI::newLine(); - return; - } - } - - $basePath = VENDOR_PATH; - $command = "php $basePath/bin/$testEngine"; - passthru($command); - } -} diff --git a/core/libraries/Console/Commands/Utilities/Update.php b/core/libraries/Console/Commands/Utilities/Update.php deleted file mode 100644 index 9c793ca..0000000 --- a/core/libraries/Console/Commands/Utilities/Update.php +++ /dev/null @@ -1,100 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - -namespace Console\Commands\Utilities; - -use Console\BaseCommand; -use Console\CLI; -use Exception; - -class Update extends BaseCommand -{ - protected $group = 'Utilities'; - protected $name = 'update'; - protected $description = 'Updates all application dependencies to their latest version.'; - protected $usage = 'update'; - protected $arguments = []; - protected $options = []; - private const COMPOSER_JSON_PATH = 'composer.json'; - private const SEARCH_STRING = 'axm/app'; - - - /** - * run - * - * @param mixed $params - * @return void - */ - public function run(array $params) - { - $this->excludePackageFromUpdate(); - $this->runCommand("@php -r \"\""); - } - - /** - * excludePackageFromUpdate - * - * @return void - */ - private function excludePackageFromUpdate() - { - if ($this->isPackageExcluded()) { - echo self::ARROW_SYMBOL . 'Excluding ' . self::SEARCH_STRING . ' from update.' . PHP_EOL; - $this->modifyComposerJson(); - } - } - - /** - * isPackageExcluded - * - * @return bool - */ - private function isPackageExcluded(): bool - { - return str_contains(file_get_contents(self::COMPOSER_JSON_PATH), self::SEARCH_STRING); - } - - /** - * modifyComposerJson - * - * @return void - */ - private function modifyComposerJson() - { - $composerContent = file_get_contents(self::COMPOSER_JSON_PATH); - $updateKey = '\"update\": ['; - $replacement = '\"update\": [\"--no-plugins\", \"--ignore-platform-reqs\",'; - $modifiedContent = str_replace($updateKey, $replacement, $composerContent); - - file_put_contents(self::COMPOSER_JSON_PATH, $modifiedContent); - } - - /** - * runCommand - * - * @param mixed $command - * @return void - */ - private function runCommand(string $command) - { - try { - exec($command, $output, $exitCode); - - if ($exitCode == 0) { - CLI::success(self::ARROW_SYMBOL . 'The packages have been updated correctly.'); - CLI::newLine(); - } - } catch (Exception $e) { - CLI::error(self::ARROW_SYMBOL . 'An error occurred while updating the package: ' . $e->getMessage()); - CLI::newLine(); - } - } -} diff --git a/core/libraries/Console/Commands/Utilities/VendorPublish.php b/core/libraries/Console/Commands/Utilities/VendorPublish.php deleted file mode 100644 index 6627b87..0000000 --- a/core/libraries/Console/Commands/Utilities/VendorPublish.php +++ /dev/null @@ -1,104 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - -namespace Console\Commands\Utilities; - -use Console\BaseCommand; -use Console\CLI; - - -class VendorPublish extends BaseCommand -{ - /** - * The group the command is lumped under - * when listing commands. - * - * @var string - */ - protected $group = 'Axm'; - - /** - * The Command's name - * @var string - */ - protected $name = 'vendor:publish'; - - /** - * The Command's short description - * @var string - */ - protected $description = 'Publish any publishable assets from vendor packages.'; - - /** - * The Command's usage - * @var string - */ - protected $usage = 'vendor:publish [options]'; - - /** - * The Command's Arguments - * @var array - */ - protected $arguments = []; - - /** - * The Command's Options - * @var array - */ - protected $options = []; - - - public function run($params) - { - if (is_file($path = ROOT_PATH . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'composer' . DIRECTORY_SEPARATOR . 'installed.json')) { - $packages = json_decode(@file_get_contents($path), true); - - // Compatibility with Composer 2.0 - if (isset($packages['packages'])) { - $packages = $packages['packages']; - } - - foreach ($packages as $package) { - // Verifica si la clave 'extra' está presente en el paquete actual - if (isset($package['extra']['axm']['config'])) { - $installPath = ROOT_PATH . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . $package['name'] . DIRECTORY_SEPARATOR; - foreach ((array) $package['extra']['axm']['config'] as $name => $file) { - $configDir = config('paths.configPath'); - if (!is_dir($configDir)) { - throw new \InvalidArgumentException("The directory $configDir does not exist."); - } - - $target = $configDir . $name . '.php'; - $source = $installPath . $file; - - static::publish($source, $target); - CLI::newLine(2); - } - } - } - } - } - - - public static function publish(string $source, string $target): void - { - if (file_exists($target)) { - CLI::write(self::ARROW_SYMBOL . " File {$target} already exists. Skipping.", 'yellow'); - $files[] = $target; - } else { - copy($source, $target); - $files[] = $target; - CLI::write(self::ARROW_SYMBOL . " File {$target} successfully published.", 'green'); - } - - CLI::write(self::ARROW_SYMBOL . " File {$target} successfully published.", 'green'); - } -} diff --git a/core/libraries/Console/ConsoleApplication.php b/core/libraries/Console/ConsoleApplication.php deleted file mode 100644 index ae0b3c9..0000000 --- a/core/libraries/Console/ConsoleApplication.php +++ /dev/null @@ -1,153 +0,0 @@ - - * @link http://www.axm.com/ - * @license http://www.axm.com/license/ - * @package Console - */ - -use Exception; -use Console\CLI; -use Console\Commands; -use \Composer\InstalledVersions; - -/** - * Clas ConsoleApplication - */ -final class ConsoleApplication -{ - private string $version; - private static $instance; - private string $axmRaw; - private $commands; - private $colors = [ - 'dark_gray' => '1;30', - 'blue' => '0;34', - 'light_blue' => '1;34', - 'green' => '0;32', - 'light_green' => '1;32', - 'cyan' => '0;36', - 'light_cyan' => '1;36', - 'purple' => '0;35', - 'light_purple' => '1;35', - ]; - - /** - * Constructor - */ - public function __construct() - { - $this->commands = new Commands(); - $this->version ??= 1.0 ?? $this->getVersion(); - } - - /** - * Get the singleton instance of the Application class - */ - public static function getInstance($config): self - { - if (self::$instance === null) { - self::$instance = new self($config); - } - - return self::$instance; - } - - /** - * Runs the current command discovered on the CLI. - * - * @throws Exception - * @param bool $useSafeOutput Indicates whether to use safe output - * @return mixed The result of command execution - */ - public function run() - { - try { - - $this->processRequest(); - - // Obtain commands and segments - $command = CLI::getSegment(1) ?? 'list'; - $params = CLI::getSegments(); - - return $this->commands->run($command, $params); - } catch (Exception $e) { - throw $e; - } - } - - /** - * Returns the Ax logo. - * @return string - */ - public function rawLogo() - { - $this->axmRaw = $this->buildLogo(); - return $this->axmRaw; - } - - /** - * Build Axm's logo in ASCII art format. - * @return string - */ - private function buildLogo() - { - return <<version} -/ | \\> <| Y Y \\ | |__| |__ | | -\\____|__ /__/\\_ \\__|_| / \\___|____|___| - \\/ \\/ \\/ -LOGO; - } - - /** - * Displays basic console information - * @param bool $suppress If set to true, the output is suppressed. - */ - public function showprocessRequest(bool $suppress = false) - { - if ($suppress) return; - - $color = array_rand($this->colors); - $logo = $this->rawLogo(); - $serverTime = date('Y-m-d H:i:s'); - $timeZone = date('P'); - - CLI::write($logo, $color); - CLI::write(CLI::color("Axm Command Line Tool - Server Time: {$serverTime} UTC{$timeZone}", $color, null, 'bold')); - CLI::newLine(); - } - - /** - * processRequest - * @return void - */ - public function processRequest() - { - // Option to suppress the processRequest information - if (is_int($suppress = array_search('--no-header', $_SERVER['argv'], true))) { - unset($_SERVER['argv'][$suppress]); - $suppress = true; - } - - self::showprocessRequest($suppress); - } - - /** - * Get the version of a library from the composer.json file and remove the '^' character - * - * @param string $libraryName The name of the library for which you want to get the version - * @return string|null - */ - function getVersion() - { - return app()->version(); - } -} diff --git a/core/libraries/Console/GeneratorTrait.php b/core/libraries/Console/GeneratorTrait.php deleted file mode 100644 index c7d8e49..0000000 --- a/core/libraries/Console/GeneratorTrait.php +++ /dev/null @@ -1,392 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - -namespace Console; - -use Console\CLI; - -/** - * GeneratorTrait contains a collection of methods - * to build the commands that generates a file. - */ -trait GeneratorTrait -{ - /** - * Component Name - * @var string - */ - protected $component; - - /** - * File directory - * @var string - */ - protected $directory; - - /** - * View template name - * @var string - */ - protected $template; - - /** - * namespace the class - */ - protected $namespace; - - /** - * Language string key for required class names. - * @var string - */ - protected $classNameLang = ''; - - /** - * class names. - * @var string - */ - protected $className; - - /** - * Whether to require class name. - * - * @internal - * @var bool - */ - private $hasClassName = true; - - /** - * Whether to sort class imports. - * - * @internal - * @var bool - */ - private $sortImports = true; - - /** - * Whether the `--suffix` option has any effect. - * - * @internal - * @var bool - */ - private $enabledSuffixing = true; - - /** - * The params array for easy access by other methods. - * - * @internal - * @var array - */ - private $params = []; - - /** - * Data passed to templates - */ - protected $data = []; - - /** - * Data passed to templates - */ - protected $replace = []; - - protected bool $phpOutputOnly = true; - - protected const ARROW_SYMBOL = '➜ '; - - /** - * Execute the command. - */ - protected function execute(array $params): void - { - $this->params = $params; - if ($this->getOption('namespace') === 'Axm') { - CLI::write(self::ARROW_SYMBOL . 'CLI generator using [ Axm ] Namespace. ⚠ ', 'yellow'); - CLI::newLine(); - - if (CLI::prompt(self::ARROW_SYMBOL . 'Are you sure you want to continue? ❓ ', ['y', 'n'], 'required') === 'n') { - CLI::newLine(); - CLI::write(self::ARROW_SYMBOL . 'CLI generator cancel Operation ', 'yellow'); - CLI::newLine(); - return; - } - - CLI::newLine(); - } - - // Get the fully qualified class name from the input. - $class = $this->qualifyClassName(); - - // Get the file path from class name. - $path = $this->buildPath($this->className ?? $class); - - // Check if path is empty. - if (empty($path)) { - return; - } - - $isFile = is_file($path); - - // Overwriting files unknowingly is a serious annoyance, So we'll check if - // we are duplicating things, If 'force' option is not supplied, we bail. - if (!$this->getOption('force') && $isFile) { - CLI::error(self::ARROW_SYMBOL . 'File exist: ' . realpath($path) . ' ❌ ', 'light_gray', 'red'); - CLI::newLine(); - return; - } - - // Check if the directory to save the file is existing. - $dir = dirname($path); - if (!is_dir($dir)) { - mkdir($dir, 0755, true); - } - - helpers('filesystem'); - - // Build the class based on the details we have, We'll be getting our file - // contents from the template, and then we'll do the necessary replacements. - if (!writeFile($path, $this->buildContent($class, $this->replace ?? [], $this->data ?? []))) { - CLI::error(self::ARROW_SYMBOL . 'File Error: ' . realpath($path) . ' ❌ ', 'light_gray', 'red'); - CLI::newLine(); - return; - } - - if ($this->getOption('force') && $isFile) { - CLI::write(self::ARROW_SYMBOL . 'File Over write: ' . realpath($path) . ' 🤙', 'yellow'); - CLI::newLine(); - return; - } - - CLI::write(self::ARROW_SYMBOL . 'File Create: ' . realpath($path) . ' 🤙', 'green'); - CLI::newLine(); - } - - /** - * Prepare options and do the necessary replacements. - */ - protected function prepare(string $class, array $replace = [], array $data = []): string - { - return $this->parseTemplate($class, $replace, $data ?? $this->data ?? []); - } - - /** - * Change file basename before saving. - * Useful for components where the file name has a date. - */ - protected function basename(string $filename): string - { - return basename($filename); - } - - /** - * Parses the class name and checks if it is already qualified. - */ - protected function qualifyClassName(): string - { - // Gets the class name from input. - $class = CLI::getSegment(2) ?? $this->className ?? null; - - if ($class === null && $this->hasClassName) { - $nameLang = $this->classNameLang ?: ' Class name ❓'; - $class = CLI::prompt($nameLang, null, 'required'); - CLI::newLine(); - } - - helpers('inflector'); - - $component = singular($this->component); - - /** - * @see https://regex101.com/r/a5KNCR/1 - */ - $pattern = sprintf('/([a-z][a-z0-9_\/\\\\]+)(%s)/i', $component); - - if (preg_match($pattern, $class, $matches) === 1) { - $class = $matches[1] . ucfirst($matches[2]); - } - - if ($this->enabledSuffixing && $this->getOption('suffix') && !strripos($class, $component)) { - $class .= ucfirst($component); - } - - // Trims input, normalize separators, and ensure that all paths are in Pascalcase. - $class = ltrim(implode('\\', array_map('pascalize', explode('\\', str_replace('/', '\\', trim($class))))), '\\/'); - - // Gets the namespace from input. Don't forget the ending backslash! - $namespace = trim(str_replace('/', '\\', $this->getOption('namespace') ?? $this->namespace ?? APP_NAMESPACE), '\\') . '\\'; - - if (strncmp($class, $namespace, strlen($namespace)) === 0) { - return $class; - } - - return $namespace . $this->directory . '\\' . str_replace('/', '\\', $class); - } - - /** - * Gets the generator view as defined in the `Config\Generators::$views`, - * with fallback to `$template` when the defined view does not exist. - */ - protected function renderTemplate(array $data = []): string - { - $templatePath = (is_file($this->template)) ? $this->template : - config('paths.consoleTemplatePath') . DIRECTORY_SEPARATOR . $this->template; - - $output = app()->view->file($templatePath, $data); - - return ($this->phpOutputOnly) ? "getNamespace($class); - - // Add namespace and class name replacements. - $replacements['{namespace}'] = $namespace; - $replacements['{class}'] = str_replace($namespace . '\\', '', $class); - - // Perform the replacements on the template and return it. - return str_replace( - array_keys($replacements), - $replacements, - $this->renderTemplate($data ?? $this->data ?? []) - ); - } - - /** - * Get the full namespace for a given class, without the class name. - * - * @param string $name - * @return string - */ - protected function getNamespace($name) - { - return trim(implode('\\', array_slice(explode('\\', $name), 0, -1)), '\\'); - } - - /** - * Builds the contents for class being generated, doing all - * the replacements necessary, and alphabetically sorts the - * imports for a given template. - */ - protected function buildContent(string $class, array $replace = [], array $data = []): string - { - $template = $this->prepare($class, $replace, $data); - - if ($this->sortImports && preg_match('/(?P(?:^use [^;]+;$\n?)+)/m', $template, $match)) { - $imports = explode(PHP_EOL, trim($match['imports'])); - sort($imports); - - return str_replace(trim($match['imports']), implode(PHP_EOL, $imports), $template); - } - - return $template; - } - - /** - * Builds the file path from the class name. - */ - protected function buildPath(string $class): string - { - $namespace = trim(str_replace('/', '\\', $this->getOption('namespace') ?? $this->namespace ?? APP_NAMESPACE), '\\'); - - // Check if the namespace is actually defined and we are not just typing gibberish. - $base = [$namespace]; - if (!$base = reset($base)) { - CLI::error(self::ARROW_SYMBOL . 'Namespace not defined: ' . $namespace . ' ❌ ', 'light_gray', 'red'); - CLI::newLine(); - return ''; - } - - $base = realpath($base) ?: $base; - $file = $base . DIRECTORY_SEPARATOR - . str_replace('\\', DIRECTORY_SEPARATOR, trim(str_replace($namespace . '\\', '', $class), '\\')) - . $this->component . '.php'; - - return implode(DIRECTORY_SEPARATOR, array_slice(explode(DIRECTORY_SEPARATOR, $file), 0, -1)) - . DIRECTORY_SEPARATOR . $this->basename($file); - } - - /** - * Allows child generators to modify the internal `$hasClassName` flag. - * @return $this - */ - protected function setHasClassName(bool $hasClassName) - { - $this->hasClassName = $hasClassName; - return $this; - } - - /** - * Allows child generators to modify the internal `$sortImports` flag. - * @return $this - */ - protected function setSortImports(bool $sortImports) - { - $this->sortImports = $sortImports; - return $this; - } - - /** - * Allows child generators to modify the internal `$enabledSuffixing` flag. - * @return $this - */ - protected function setEnabledSuffixing(bool $enabledSuffixing) - { - $this->enabledSuffixing = $enabledSuffixing; - return $this; - } - - /** - * Gets a single command-line option. Returns TRUE if the option exists, - * but doesn't have a value, and is simply acting as a flag. - * - * @return mixed - */ - protected function getOption(string $name) - { - if (!array_key_exists($name, $this->params)) { - return CLI::getOption($name); - } - - return $this->params[$name] ?? true; - } - - /** - * Filters and formats a given string to a valid PHP class name. - * - * The function applies the following rules: - * - Removes any characters that are not letters, numbers, or underscores. - * - Ensures the first character is a letter or underscore. - * - Converts the name to CamelCase format. - * @param string $className The name of the class to be formatted. - * @return string The formatted and valid PHP class name. - */ - function formatClassName($className) - { - // Remove characters that are not letters, numbers, or underscores - $filteredName = preg_replace('/[^\p{L}\p{N}_]/u', '', $className); - - // Ensure the first character is a letter or underscore - $filteredName = preg_replace('/^[^a-zA-Z_]+/', '', $filteredName); - - // Convert to CamelCase format - $filteredName = str_replace('_', '', ucwords($filteredName, '_')); - - return $filteredName; - } -} diff --git a/core/libraries/Console/HelpConsole.php b/core/libraries/Console/HelpConsole.php deleted file mode 100644 index d8e0998..0000000 --- a/core/libraries/Console/HelpConsole.php +++ /dev/null @@ -1,83 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - - use Console\BaseCommand; - -/** - * CI Help command for the axm script. - * - * Lists the basic usage information for the axm 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 = 'Axm'; - - /** - * 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 - */ - protected $arguments = [ - 'command_name' => 'The command name [default: "help"]', - ]; - - /** - * the Command's Options - * - * @var array - */ - protected $options = []; - - /** - * Displays the help for axm commands. - */ - public function run(array $params) - { - $command = array_shift($params); - $command = $command ?? 'help'; - $commands = $this->commands->getCommands(); - - if (! $this->commands->verifyCommand($command, $commands)) { - return; - } - - $class = new $commands[$command]['class']('', $this->commands); - $class->showHelp(); - } -} diff --git a/core/libraries/Console/LICENSE b/core/libraries/Console/LICENSE deleted file mode 100644 index a25daa2..0000000 --- a/core/libraries/Console/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -MIT License - - -Copyright (c) 2023 Axm-framework - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/core/libraries/Console/README.md b/core/libraries/Console/README.md deleted file mode 100644 index 45f429b..0000000 --- a/core/libraries/Console/README.md +++ /dev/null @@ -1,20 +0,0 @@ -

- - Total Downloads - - - Latest Stable Version - - - License - -

- -## Introduction - -The class CLIException shows in a nice and detailed way the error in the console, -this code has been inspired by the Laravel Colision library. - - - -Raxm is open-sourced software licensed under the [MIT license](LICENSE). diff --git a/core/libraries/Database/Database.php b/core/libraries/Database/Database.php deleted file mode 100644 index 2c8942d..0000000 --- a/core/libraries/Database/Database.php +++ /dev/null @@ -1,78 +0,0 @@ - - * @link http://www.axm.com/ - * @license http://www.axm.com/license/ - * @package Axm - */ -class Database -{ - /** - * @var Illuminate\Database\Capsule\Manager - */ - private static $connection; - - /** - * Create a new database connection for models - * @param string|null $driver - */ - public static function connect(string $driver = null) - { - // If there is already a connection, do nothing - if (isset(static::$connection)) return; - - $config = config('DataBase'); - $driver = $driver ?? $config['default'] ?? 'mysql'; - - $capsule = static::$connection = new Capsule; - $capsule->addConnection( - $config['connections'][$driver] - ); - - // Set the event dispatcher used by Eloquent models... (optional) - // $capsule->setEventDispatcher(new Dispatcher(new Container)); - - $capsule->setAsGlobal(); - $capsule->bootEloquent(); - - // if (php_sapi_name() === 'cli') { - // Schema::$capsule = $capsule; - // } - } - - /** - * Get database connection - * @return Illuminate\Database\Capsule\Manager - */ - public static function db() - { - if (static::$connection) { - return static::$connection; - } - - static::connect(); - - return static::$connection; - } - - /** - * Database disconnect - */ - public static function disconnect(string $driver) - { - return static::$connection - ->getConnection() - ->disconnect($driver); - } -} diff --git a/core/libraries/Encryption/Encrypter.php b/core/libraries/Encryption/Encrypter.php deleted file mode 100644 index f159483..0000000 --- a/core/libraries/Encryption/Encrypter.php +++ /dev/null @@ -1,133 +0,0 @@ -key = $encryption_key ?? $this->generateKeyRandom($keyLength); - - if (mb_strlen($this->key, '8bit') !== $this->lengthKey) { - throw new RuntimeException("The encryption key must be {$this->lengthKey} characters long."); - } - - return $this; - } - - /** - * Get Key - */ - public function getKey() - { - return $this->key; - } - - /** - * Generate Key - */ - public function newKey(int $keyLength = 32) - { - $this->key = $this->generateKeyRandom($keyLength); - return $this; - } - - /** - * Encrypt data. - * - * @param string $data - * @return string - */ - public function encrypt(string $data): string - { - $iv = random_bytes(16); - $encryptedData = openssl_encrypt( - serialize($data), - $this->cipher, - $this->key, - 0, - $iv - ); - - if ($encryptedData === false) { - throw new RuntimeException('Could not encrypt the data.'); - } - - $payload = [ - 'iv' => base64_encode($iv), - 'data' => base64_encode($encryptedData), - 'hash' => $this->generateHash($iv), - ]; - - $json = json_encode($payload, JSON_UNESCAPED_SLASHES); - - if (json_last_error() !== JSON_ERROR_NONE) { - throw new RuntimeException('Could not encrypt the data.'); - } - - return base64_encode($json); - } - - /** - * Decrypt data. - * - * @param string $encrypted_payload - * @return mixed - */ - public function decrypt(string $encrypted_payload) - { - $payload = json_decode(base64_decode($encrypted_payload), true); - - $iv = base64_decode($payload['iv']); - $decrypted = openssl_decrypt( - base64_decode($payload['data']), - $this->cipher, - $this->key, - 0, - $iv - ); - - if ($decrypted === false) { - throw new RuntimeException('Could not decrypt the data.'); - } - - if (!hash_equals($this->generateHash($iv), $payload['hash'])) { - throw new RuntimeException('Invalid payload hash. Possible tampering.'); - } - - return unserialize($decrypted); - } - - /** - * Generate a hash based on the IV and encryption key. - * - * @param string $iv - * @return string - */ - protected function generateHash(string $iv): string - { - return hash_hmac('sha256', $iv, $this->key); - } - - /** - * Generate a random encryption key. - * - * @param int $length - * @return string - */ - public function generateKeyRandom(int $length): string - { - $key = random_bytes($length); - if (mb_strlen($key, '8bit') !== $length) { - throw new RuntimeException('The encryption key could not be generated.'); - } - - return $key; - } -} diff --git a/core/libraries/Encryption/README.md b/core/libraries/Encryption/README.md deleted file mode 100644 index 68abca0..0000000 --- a/core/libraries/Encryption/README.md +++ /dev/null @@ -1,64 +0,0 @@ -

- - Total Downloads - - - Latest Stable Version - - - License - -

- -Axm Encrypter -This library provides a simple and secure way to encrypt and decrypt data using the AES-256-CBC cipher. It also includes integrity checking to ensure that the data has not been tampered with. - -Installation - -```bash -composer require axm/encryption -``` - -## Usage - -Example: - -```php -use Encryption\Encrypter; - -$encrypter = new Encrypter('your-encryption-key'); -$encryptedData = $encrypter->encrypt('This is some sensitive data'); - -$decryptedData = $encrypter->decrypt($encryptedData); -echo $decryptedData; - -``` - -Example: - -```php -// Encrypting a string -$encryptedString = $encrypter->encrypt('My secret message'); - -// Encrypting an array -$data = ['username' => 'johndoe', 'password' => 'secretpassword']; -$encryptedData = $encrypter->encrypt(serialize($data)); - -// Decrypting data -$decryptedData = $encrypter->decrypt($encryptedData); -$data = unserialize($decryptedData); -echo $data['username']; - -``` - -## Security Considerations - -- It is important to keep your encryption key secret. If someone else has access to your encryption key, they will be able to decrypt your data. -- You should also make sure that your encryption key is long enough. A 32-character key is recommended. - -## License - - - -Raxm is open-sourced software licensed under the [MIT license](LICENSE). - diff --git a/core/libraries/Fluent/Fluent.php b/core/libraries/Fluent/Fluent.php deleted file mode 100644 index 087ad65..0000000 --- a/core/libraries/Fluent/Fluent.php +++ /dev/null @@ -1,508 +0,0 @@ - - * @link http://www.axm.com/ - * @license http://www.axm.com/license/ - * @package Axm - * - * Fluent Interface class for method chaining and control flow. - * - * The Fluent class provides a fluent interface to facilitate method chaining and flow control - * in your PHP applications. and flow control in your PHP applications. You can use this class to perform a variety - * of operations by chaining methods in a concise and readable way. - */ -class Fluent -{ - private $obj; - private $result = []; - private $condition = true; - private $isReturn = false; - private $customMethods = []; - - - /** - * Constructor that accepts a class name or object. - * - * @param mixed $obj The class name or object to work with. - * @throws InvalidArgumentException If the argument is not a valid class name or object. - */ - public function __construct(string|object|array $data) - { - $this->createInstance($data); - } - - /** - * Checks whether the argument is a string or an existing class and creates an instance. - * - * @param mixed $data The argument to be used to create the instance. - * @return void - */ - private function createInstance(string|object|array $data) - { - $this->obj = match (true) { - is_string($data) && class_exists($data) => new $data(), - is_object($data) => $data, - is_array($data) => new Collection($data), - }; - - $this->result[get_class($this->obj)] = $this->obj; - } - - /** - * Create a new instance of a class and set it as the current object. - * - * @param array $args An associative array where the keys represent the class name and constructor arguments. - * @param bool $return Whether to return the created object or $this. - * @return $this|object - */ - public function new(string|object|array $data, bool $return = true): Fluent - { - if ($this->condition && !$this->isReturn) { - $this->createInstance($data); - - return $return ? $this->obj : $this; - } - } - - /** - * Get the current result. - * @return mixed The current result. - */ - public function all(): array - { - return $this->result; - } - - /** - * Get the current result, which is the last element in the results array. - * @return mixed|null The current result. - */ - public function get(string $key = null) - { - if ($key !== null && array_key_exists($key, $this->result)) { - return $this->result[$key]; - } - - return end($this->result); - } - - /** - * Add a key-value pair to the result array. - * - * @param string $key The key to add to the array. - * @param mixed $value The value associated with the key. - * @return $this Returns the current instance of Fluent for method chaining. - */ - public function addValue($key, $value) - { - $this->result[$key] = $value; - return $this; - } - - /** - * Iterates over the elements of the collection and applies a callback function to each element. - * - * @param callable $callback The callback function to apply to each element. - * @param string|null $key (Optional) The key under which you want to store the results in the buffer. - * @return $this The current Fluent object. - */ - public function each(callable $callback, $key = null) - { - if ($this->condition) { - $result = $this->result; - - // Apply the callback function to each element - $processedResult = array_map($callback, $result); - - if ($key !== null) { - // Store the results under a custom key if provided - $this->result[$key] = $processedResult; - } else { - // Store the results in the buffer - $this->result['each'] = $processedResult; - } - } - - return $this; - } - - /** - * Get the value of a property of the object. - * - * @param string $name The name of the property. - * @return mixed|null The value of the property, or null if it doesn't exist. - */ - public function getProperty($name) - { - if (property_exists($this->obj, $name)) { - return $this->obj->$name; - } - - throw new InvalidArgumentException(sprintf('The property [ %s ] does not exist on the object [ %s ] .', $name, get_class($this->obj))); - } - - /** - * Set the value of a property of the object. - * - * @param string $name The name of the property. - * @param mixed $value The value to set. - * @return $this - */ - public function setProperty($name, $value) - { - if (property_exists($this->obj, $name)) { - $this->obj->$name = $value; - return $this; - } - - throw new InvalidArgumentException(sprintf('The property [ %s ] does not exist on the object [ %s ] .', $name, get_class($this->obj))); - } - - /** - * Check if a service exists in the container. - * - * @param string $id - * @return bool - */ - public function hasProperty($name): bool - { - return isset($this->obj->$name); - } - - /** - * Set a condition for method execution and specify a callback to execute if the condition is true. - * - * @param callable $callback The callback to execute if the condition is true. - * @return $this - */ - public function if(callable $callback) - { - $condition = $callback($this); - $this->result['if'] = $condition; - - if ($this->condition && $condition) { - $this->condition = true; - } else { - $this->condition = false; - } - - return $this; - } - - /** - * Set a condition for method execution if the previous condition is false. - * - * @param callable $callback The callback to execute if the condition is true. - * @return $this - */ - public function elseif(callable $callback) - { - $condition = $callback($this); - $this->result['elseif'] = $condition; - - if (!$this->condition && $condition) { - $this->condition = true; - } else { - $this->condition = false; - } - - return $this; - } - - /** - * Execute a callback if no previous conditions were met. - * - * @param callable $callback The callback to execute if no previous conditions were met. - * @return $this - */ - public function else() - { - if ($this->condition = !$this->condition) { - $this->condition = true; - } - - return $this; - } - - /** - * Set a value or execute a closure as the result. - * - * @param mixed|callable|null $valueOrClosure The value or closure to set as the result. - * @return $this - */ - public function return($valueOrClosure = null) - { - if ($this->condition) { - $this->isReturn = true; - - if (is_callable($valueOrClosure)) { - $this->result['return'] = $valueOrClosure(); - } elseif ($valueOrClosure !== null) { - $this->result['return'] = $valueOrClosure; - } - } - - return $this; - } - - /** - * Chain method execution with a provided closure. - * - * @param callable $callback The closure to call with the current object. - * @return $this The current instance of Fluent. - */ - public function chain(callable $callback) - { - if ($this->condition) { - // Clone the current object to avoid affecting the original object with modifications. - $newInstance = clone $this; - - $this->result['chain'] = $callback($newInstance); - - return $newInstance; - } - - return $this; - } - - /** - * Throw an exception if a condition is met. - * - * @param callable $callback The callback to execute if the condition is true. - * @param string $exceptionMessage The exception message. - * @return $this - * @throws Exception If the condition is met. - */ - public function throwIf(callable $callback, $exceptionMessage) - { - $condition = $callback($this); - if ($condition) { - throw new Exception($exceptionMessage); - } - return $this; - } - - /** - * Reflect and create an instance of a class with constructor arguments. - * - * This method allows you to create an instance of a specified class - * using reflection and provide constructor arguments. - * @param string $className The name of the class to reflect and instantiate. - * @param mixed ...$constructorArgs Arguments to pass to the class constructor. - * @return $this Returns the current instance of Fluent, allowing for method chaining. - * @throws Exception If there is an error creating an instance of the class. - */ - public function reflect($className, ...$constructorArgs) - { - if ($this->condition) { - try { - $reflection = new ReflectionClass($className); - $this->result['reflect'] = $reflection->newInstanceArgs($constructorArgs); - } catch (ReflectionException $e) { - throw new InvalidArgumentException(sprintf('Error creating instance of %s: [ %s ] .', $className, $e->getMessage())); - } - } - - return $this; - } - - /** - * Loop through a callback for a specified number of iterations. - * - * @param callable $callback The callback to execute. - * @param int $iterations The number of iterations. - * @return $this - * @throws Exception If an exception occurs within the callback. - */ - public function loop(callable $callback, $iterations) - { - if ($this->condition) { - for ($i = 0; $i < $iterations; $i++) { - try { - $this->result['loop'] = $callback($i); - } catch (Exception $e) { - throw new InvalidArgumentException(sprintf('Error in loop iteration %s: [ %s ] .', $i, $e->getMessage())); - } - } - } - - return $this; - } - - /** - * Dump the result data using the Laravel "dd" function. - * - * @param string|null $key The optional key to retrieve a specific result entry. - * @return $this The current instance of Fluent. - */ - public function dd($key = null) - { - $value = isset($key) ? $this->result[$key] : $this->result; - dd($value); - - return $this; - } - - /** - * Dump the result data using the Laravel "dump" function. - * - * @param string|null $key The optional key to retrieve a specific result entry. - * @return $this The current instance of Fluent. - */ - public function dump($key = null) - { - $value = isset($key) ? $this->result[$key] : $this->result; - dump($value); - - return $this; - } - - /** - * Echo the result data if the condition is met. - * - * @param mixed $value The optional value to echo. If not provided, the entire result is echoed. - * @return $this The current instance of Fluent. - */ - public function write($value = null) - { - if ($this->condition) { - $value = isset($value) ? $value : $this->result; - if (is_string($value)) { - echo $value; - } elseif (is_array($value) || is_object($value)) { - print_r($value); - } - } - - return $this; - } - - /** - * The Fluent with custom methods. - * @param callable $extension A closure that defines the new method. - */ - public function addCustomMethod($method, $callback) - { - if (!is_callable($callback)) { - throw new InvalidArgumentException('The callback must be callable.'); - } - - $this->customMethods[$method] = $callback; - - return $this; - } - - /** - * Reset the Fluent state. - * - * @param string|null $name The optional name of the result entry to reset. - * If provided, only that entry will be removed. - * If not provided, all entries are cleared, resetting the state. - * @return $this The current instance of Fluent. - */ - public function reset($name = null) - { - if ($name !== null) { - unset($this->result[$name]); - } else { - $this->result = []; - } - - return $this; - } - - /** - * Magic method to call methods on the object. - * - * @param string $name The method name. - * @param array $arguments The method arguments. - * @return $this - * @throws Exception If an exception is thrown while calling the method. - */ - public function __call($name, $arguments): Fluent - { - if ($this->condition && !$this->isReturn) { - $result = null; - - $result = match (true) { - $this->obj instanceof Collection, is_callable([$this->obj, $name]) => $this->callMethod($this->obj, $name, $arguments), - array_key_exists($name, $this->customMethods) => call_user_func_array($this->customMethods[$name], $arguments), - - default => $this->callMethod($this, $name, $arguments), - }; - - if ($result !== null) { - $this->result[$name] = $result; - } - } - - return $this; - } - - /** - * Method to call a method on a service - */ - private function callMethod(Object $obj, string $method, $arguments) - { - if (method_exists($obj, $method)) { - return call_user_func_array([$obj, $method], $arguments); - } - // If the method does not exist, throw an exception - throw new InvalidArgumentException(sprintf('The method [ %s ] does not exist in the Collection context.', $method)); - } - - /** - * Magic method to get a service by property access. - * - * @param string $key - * @return mixed - */ - public function __get($name) - { - if (property_exists($this->obj, $name)) { - return $this->obj->$name; - } - // If the property does not exist, throw an exception - throw new RuntimeException(sprintf('The property [ %s ] does not exist on the object [ %s ]', $name, get_class($this->obj))); - } - - /** - * Magic method to set a service by property access. - * - * @param string $key - * @param mixed $value - */ - public function __set($name, $value) - { - if (property_exists($this->obj, $name)) { - return $this->obj->$name = $value; - } - // If the property does not exist, throw an exception - throw new RuntimeException(sprintf('The property [ %s ] does not exist on the object [ %s ]', $name, get_class($this->obj))); - } - - /** - * Magic method to check if a service exists by property access. - * - * @param string $key - * @return bool - */ - public function __isset($key) - { - return $this->has($key); - } -} diff --git a/core/libraries/Fluent/LICENSE b/core/libraries/Fluent/LICENSE deleted file mode 100644 index 303f99b..0000000 --- a/core/libraries/Fluent/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -MIT License - - -Copyright (c) 2023 Axm-framework - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/core/libraries/Fluent/README.md b/core/libraries/Fluent/README.md deleted file mode 100644 index 5a5338a..0000000 --- a/core/libraries/Fluent/README.md +++ /dev/null @@ -1,327 +0,0 @@ - -

-

- -
-

- -

- Latest Stable Version - Total Downloads - License -

-
-
- -# Fluent Interface - -The `FluentInterface` class is a PHP tool that provides a fluent interface for chaining methods and controlling the flow of operations concisely in PHP fluentlications. Its main purpose is to improve code readability, allowing you to chain a series of methods coherently and easy to follow. This is the greatest potential of this interface, it is no longer necessary to repeatedly return the $this object, now `FluentInterface` handles it automatically for you. - -## 📦 Installation - -You can also use [Composer](https://getcomposer.org/) to install Axm in your project quickly. - -```bash -composer require axm/fluent -``` - -## Method Chaining - -You can chain multiple methods, which facilitates executing multiple operations sequentially. For example: - -```php -$result = __(new MyClass) - ->method1() - ->method2() - ->method3() - ->get(); -``` - -## Flow Control - -The class allows precise control of the method execution flow using if, elseif and else conditions. This is useful to run specific operations based on certain conditions. For example: - -```php -$result = __(User::class) - ->if(fn ($user) => $user->isAdmin()) - ->setProperty('type', 'admin') - ->return('You are an admin.') - - ->elseif(fn ($user) => $user->isUser()) - ->setProperty('type', 'user') - ->return('You are a user.') - - ->else() - ->setProperty('type', 'unknown') - ->return('You are not logged in.') - ->get(); -``` - -## Dynamic Instance Creation - -You can dynamically create class instances and set them as the current object. This is useful when you need to work with different objects flexibly. For example: - -```php -$result = __(new MyClass) //new instance - ->method1() - ->method2() - ->new('SomeClass') //resets the previous intent with a new `FluentInterface` class to continue chaining. - ->method1() - ->method2() - ->method3() - ->new('OtherClass') //resets the previous intent with a new `FluentInterface` class to continue chaining. - ->method1() - ->method2() - ->method3() - - ->get('method1'); //gets the value in this case of the return of method 1 of the last instance -``` - -## Exception Handling - -The FluentInterface class handles exceptions and allows you to throw exceptions during execution, making condition-based decisions and thrown exceptions easier. - -```php -$result = __(new MyClass) - ->boot() - ->getErrors() - ->throwIf(fn ($user) => $fi->get('getError') > 0, , 'An error has occurred while initializing.') -``` - -## Debugging Functions - -It offers methods like dd(), dump(), and echo() to aid in debugging and analyzing results. - -##### dd() method - -The dd() method (dump and die) is used to debug and analyze the results of the FluentInterface class. You can use it to show the contents of the current variable in detail. If you provide a key ($key), only that specific entry in the results will be shown. -Usage example: - -```php -$result = __(new MyClass) - ->increment(10) - ->duplicate() - ->dd(); -``` - -##### dump() method - -The dump() method is used to debug and analyze the results of the FluentInterface class. As with dd(), you can provide a key ($key) to show only a specific entry in the results. -Usage example: - -```php -$result = __(new MyClass) - ->increment(10) - ->duplicate() - ->dump('duplicate'); //will return the value of the duplicate method call. -``` - -##### echo() method - -The echo() method is used to print the results of the FluentInterface class. You can optionally provide a value ($value) to print something specific. If no value is provided, it will print all current results. -Usage example: - -```php -$result = __(new MyClass) - ->increment(10) - ->duplicate() - ->echo(); -``` - -## Using Custom Methods - -In addition to the built-in methods, you can add your own custom methods using addCustomMethod(). This extends the functionality of the class according to your specific needs. - -```php -$result = __(new MyClass) - ->addCustomMethod('call', function ($obj) { - // Define your own logic here - }) - ->addCustomMethod('getName', function ($obj) { - // Define your own logic here - }) - ->call() - ->getName() - ->all(); -``` - -## Interface with Laravel Collections - -The class can work with Laravel collections and run collection methods on them. You just need to pass an array as an argument to the FluentInterface class. -For example: - -```php -$collection = [ - ['name' => 'John Doe', 'age' => 30], - ['name' => 'Jane Doe', 'age' => 25], - ['name' => 'John Smith','age' => 40], -]; - -$result = __($collection) - ->filter(function ($item) { - return $item['age'] > 25; - }) - ->sort(function ($a, $b) { - return $a['name'] <=> $b['name']; - }); - -$result->all(); -``` - -##### Usage Examples of Fluent Interface with an object. - -Example 1: Operations on a Numeric Value -This example illustrates the use of the MyClass class, which provides a fluent interface to perform operations on a numeric value. The MyClass class has three main methods: `increment()`, `duplicate()`, and `getValue()`. - -```php -class MiClase -{ - public $value = 0; - - public function increment($cantidad) - { - return $this->value += $cantidad; - } - - public function duplicate() - { - return $this->value *= 2; - } - - public function getValue() - { - return $this->value; - } -} -``` - -Fluent Interface implementation: - -```php -$res = __(MiClase::class) - ->increment(5) - ->duplicate() - ->increment(5) - - ->if(fn ($fi) => $fi->value > 20) - ->increment(5) - - ->elseif(fn ($fi) => $fi->value < 15) - ->increment(10) - - ->else() - ->increment(10) - - ->getValue()->dd('getValue'); -``` - -Example 2: User Input Validation -This example shows how to validate user input and make decisions based on specific conditions. The FluentInterface class provides a fluent interface to chain methods and control the execution flow effectively. - -```php -__($request->input()) //input array - - ->if(fn ($item) => $item['name'] === '') - ->throwIf(true, 'The name field is required.') - - ->if(fn ($item) => $item['email'] === '') - ->throwIf(true, 'The email field is required.'); - - ->new(Auth::class) // Create a new instance of the class 'Auth'. - ->if(fn ($user) => $user->hasPermission('admin')) - ->return(['Admin Dashboard', 'User Management','Role Management',]) - - ->elseif(fn ($user) => $user->hasPermission('user')) - ->return(['My Profile','My Orders','My Account',]) - - ->else() - ->return(['Login','Register',]) - ->get('return'); -``` - -Example 3: Dynamic Report Generation -Imagine you are developing a report generation fluentlication that allows users to configure and customize reports according to their needs. In this situation, FluentInterface can simplify the building of dynamic reports. - -Suppose you have a ReportBuilder class that is used to build reports. You can use FluentInterface to chain methods and dynamically configure report components like headers, charts, data, and output formats. - -```php -// Create a custom report using FluentInterface. -__(ReportBuilder::class) - ->setHeader('Informe de Ventas') - ->setSubtitle('Resultados mensuales') - ->setChart('Ventas por mes', 'bar') - ->addData('Enero', 1000) - ->addData('Febrero', 1200) - ->addData('Marzo', 800) - ->setFooter('© 2023 Mi Empresa') - ->setFormat('PDF') - ->generateReport(); -``` - -Example 4: Building Configurable Forms -Imagine you are developing a form builder platform where users can design their own forms with custom fields. FluentInterface can simplify the creation and manipulation of dynamic forms. - -```php - __(FormBuilder::class) - ->setTitle('Contact Form') - ->addField('Name', 'text') - ->addField('Email', 'email') - ->addField('Message', 'textarea') - ->addButton('Submit', 'submit') - ->setAction('/submit-form') - ->setMethod('POST') - ->generateForm(); -``` - -Example 5: Sending Custom Emails -Suppose you are developing an fluentlication that sends custom emails to users. FluentInterface can simplify the building of these emails. - -```php -__(EmailBuilder::class) - ->setRecipient('user@example.com') - ->setSubject('Welcome!') - ->setBody('Hi, [name]. Thank you for joining our website.') - ->addAttachment('invoice.pdf') - ->setSender('info@mycompany.com') - ->send(); -``` - -Example 6: Generating Dynamic SQL Queries -Suppose you are developing a web fluentlication that needs to generate dynamic SQL queries to interact with a database. You can use FluentInterface to build these queries programmatically and readably: - -```php -$query = __(QueryBuilder::class) - ->select('name', 'email') - ->from('users') - ->where('age', '>', 25) - ->andWhere('city', '=', 'New York') - ->orderBy('name', 'ASC') - ->limit(10) - ->execute(); -``` - -Example 7: Creating Interactive Charts -Suppose you are developing a web fluentlication that displays interactive charts to users. FluentInterface can help you construct and configure these charts flexibly: - -```php -$chart = __(ChartBuilder::class) - ->setType('line') - ->setTitle('Monthly Sales') - ->addData('January', [100, 150, 200, 120]) - ->addData('February', [120, 160, 180, 140]) - ->setXAxisLabels(['Week 1', 'Week 2', 'Week 3', 'Week 4']) - ->setYAxisLabel('Sales (in thousands)') - ->render(); -``` diff --git a/core/libraries/Fluent/art/readme_logo.png b/core/libraries/Fluent/art/readme_logo.png deleted file mode 100644 index 40a9aee..0000000 Binary files a/core/libraries/Fluent/art/readme_logo.png and /dev/null differ diff --git a/core/libraries/Fluent/es.md b/core/libraries/Fluent/es.md deleted file mode 100644 index e972e8c..0000000 --- a/core/libraries/Fluent/es.md +++ /dev/null @@ -1,301 +0,0 @@ -# Clase FluentInterface - -La clase `FluentInterface` es una herramienta para PHP que proporciona una interfaz fluida para encadenar métodos y controlar el flujo de operaciones de manera concisa en aplicaciones PHP. Su propósito principal es mejorar la legibilidad del código, permitiéndote encadenar una serie de métodos de manera coherente y fácil de seguir.Esta es la mayor pontencialidad que tiene esta interfaz, ya no es necesario devolver repetidamente el objeto $this, ahora `FluentInterface` lo gestiona automáticamente por ti. - -## Encadenamiento de Métodos - -Puedes encadenar varios métodos, lo que facilita la ejecución de múltiples operaciones de manera secuencial. Por ejemplo: - -```php -$result = __(new MyClass) - ->method1() - ->method2() - ->method3() - ->get(); -``` - -## Control de Flujo - -La clase permite un control preciso del flujo de ejecución de métodos utilizando condiciones if, elseif y else. Esto es útil para ejecutar operaciones específicas en función de condiciones determinadas. Por ejemplo: - -```php -$result = __(User::class) - - ->if(fn ($user) => $user->isAdmin()) - ->setProperty('type', 'admin') - ->return('You are an admin.') - - ->elseif(fn ($user) => $user->isUser()) - ->setProperty('type', 'user') - ->return('You are a user.') - - ->else() - ->setProperty('type', 'unknown') - ->return('You are not logged in.') - ->get(); -``` - -## Creación Dinámica de Instancias - -Puedes crear instancias de clases dinámicamente y establecerlas como el objeto actual. Esto es útil cuando necesitas trabajar con diferentes objetos de manera flexible. Por ejemplo: - -```php -$result = __(new MyClass) //new instance -->method1() -->method2() -->new('SomeClass') //resets the previous intent with a new `FluentInterface` class to continue chaining. -->method1() -->method2() -->method3() -->new('OtherClass') //resets the previous intent with a new `FluentInterface` class to continue chaining. -->method1() -->method2() -->method3() - -->get('method1'); //gets the value in this case of the return of method 1 of the last instance -``` - -## Manejo de Excepciones - -La clase FluentInterface maneja excepciones y te permite lanzar excepciones durante la ejecución, lo que facilita la toma de decisiones basadas en condiciones y excepciones arrojadas. - -```php -$result = __(new MyClass) - ->boot() - ->getErrors() - ->throwIf(fn ($user) => $fi->get('getError') > 0, , 'Ha ocurrido un error al inicializar.') -``` - -## Funciones de Depuración - -Ofrece métodos como dd(), dump(), y echo() para ayudar en la depuración y el análisis de resultados. - -##### Método dd($key = null) - -El método dd() (dump and die) se utiliza para depurar y analizar los resultados de la clase FluentInterface. Puedes usarlo para mostrar de manera detallada el contenido de la variable actual. Si proporcionas una clave ($key), se mostrará solo esa entrada específica de los resultados. - -Ejemplo de uso: - -```php -$result = __(new MyClass) -->incrementar(10) -->duplicate() -->dd(); -``` - -##### Método dump($key = null) - -El método dump() se utiliza para depurar y analizar los resultados de la clase FluentInterface. Al igual que dd(), puedes proporcionar una clave ($key) para mostrar solo una entrada específica de los resultados. - -Ejemplo de uso: - -```php -$result = __(new MyClass) -->incrementar(10) -->duplicate() -->dump('duplicate'); //will return the value of the duplicate method call. -``` - -##### Método echo($value = null) - -El método echo() se utiliza para imprimir los resultados de la clase FluentInterface. Puedes proporcionar un valor ($value) opcional para imprimir algo específico. Si no se proporciona un valor, imprimirá todos los resultados actuales. - -Ejemplo de uso: - -```php -$result = __(new MyClass) -->incrementar(10) -->duplicate() -->echo(); -``` - -## Uso de Métodos Personalizados - -Además de los métodos incorporados, puedes agregar tus propios métodos personalizados utilizando addCustomMethod(). Esto extiende la funcionalidad de la clase según tus necesidades específicas. - -```php -$result = __(new MyClass) -->addCustomMethod('call', function ($obj) { -// Define your own logic here -}) -->addCustomMethod('getName', function ($obj) { -// Define your own logic here -}) -->call() -->getName() - -->all(); -``` - -## Interfaz con Colecciones Laravel - -La clase puede trabajar con colecciones Laravel y ejecutar métodos de colección en ellas. Solo tienes que pasar un array como argumento de la clase FluentInterface. -Por ejemplo: - -```php -$collection = [ - ['name' => 'John Doe', 'age' => 30], - ['name' => 'Jane Doe', 'age' => 25], - ['name' => 'John Smith','age' => 40], -]; - -$result = __($collection) -->filter(function ($item) { - return $item['age'] > 25; -}) -->sort(function ($a, $b) { - return $a['name'] <=> $b['name']; -}); - -$result->all(); -``` - -##### Ejemplos de Uso de Fluent Interface con un objeto. - -Ejemplo 1: Operaciones en un Valor Numérico -Este ejemplo ilustra el uso de la clase MiClase, que proporciona una interfaz fluida para realizar operaciones en un valor numérico. La clase MiClase tiene tres métodos principales: `incrementar()`, `duplicate()`, y `getValue()`. - -```php - class MiClase - { - public $value = 0; - - public function incrementar($cantidad) - { - return $this->value += $cantidad; - } - - public function duplicate() - { - return $this->value *= 2; - } - - public function getValue() - { - return $this->value; - } - } -``` - -Implementación de Fluent Interface: - -```php -$res = __(MiClase::class) - ->incrementar(5) - ->duplicate() - ->incrementar(5) - - ->if(fn ($fi) => $fi->value > 20) - ->incrementar(5) - - ->elseif(fn ($fi) => $fi->value < 15) - ->incrementar(10) - - ->else() - ->incrementar(10) - - ->getValue()->dd('getValue'); - -``` - -Ejemplo 2: Validación de Entrada de Usuario -Este ejemplo muestra cómo validar la entrada de usuario y tomar decisiones basadas en condiciones específicas. La clase FluentInterface proporciona una interfaz fluida para encadenar métodos y controlar el flujo de ejecución de manera efectiva. - -```php -__($request->input()) //input array - - ->if(fn ($item) => $item['name'] === '') - ->throwIf(true, 'The name field is required.') - - ->if(fn ($item) => $item['email'] === '') - ->throwIf(true, 'The email field is required.'); - - ->new(Auth::class) // Create a new instance of the class 'Auth'. - ->if(fn ($user) => $user->hasPermission('admin')) - ->return(['Admin Dashboard', 'User Management','Role Management',]) - - ->elseif(fn ($user) => $user->hasPermission('user')) - ->return(['My Profile','My Orders','My Account',]) - - ->else() - ->return(['Login','Register',]) - ->get('return'); -``` - -Ejemplo 3: Generación de Informes Dinámicos -Imagina que estás desarrollando una aplicación de generación de informes que permite a los usuarios configurar y personalizar informes según sus necesidades. En esta situación, FluentInterface puede simplificar la construcción de informes dinámicos. - -Supongamos que tienes una clase ReportBuilder que se utiliza para construir informes. Puedes utilizar FluentInterface para encadenar métodos y configurar dinámicamente los componentes del informe, como encabezados, gráficos, datos y formatos de salida. - -```php -// Crear un informe personalizado utilizando FluentInterface. -__(ReportBuilder::class) - ->setHeader('Informe de Ventas') - ->setSubtitle('Resultados mensuales') - ->setChart('Ventas por mes', 'bar') - ->addData('Enero', 1000) - ->addData('Febrero', 1200) - ->addData('Marzo', 800) - ->setFooter('© 2023 Mi Empresa') - ->setFormat('PDF') - ->generateReport(); -``` - -En este ejemplo, FluentInterface permite una configuración fluida del informe. Puedes establecer el encabezado, el subtítulo, agregar datos mensuales, configurar el formato de salida y generar el informe, todo en una secuencia coherente de métodos encadenados.Este enfoque facilita la construcción de informes personalizados de manera programática y permite a los usuarios finales crear informes de manera eficiente según sus necesidades específicas. Además, mejora la legibilidad y mantenibilidad del código relacionado con la generación de informes. - -Ejemplo 4: Construcción de Formularios Configurables -Imagina que estás desarrollando una plataforma de creación de formularios en la que los usuarios pueden diseñar sus propios formularios con campos personalizados. FluentInterface puede simplificar la creación y manipulación de formularios dinámicos. - -```php -$form = __(FormBuilder::class) - ->setTitle('Formulario de Contacto') - ->addField('Nombre', 'text') - ->addField('Correo Electrónico', 'email') - ->addField('Mensaje', 'textarea') - ->addButton('Enviar', 'submit') - ->setAction('/submit-form') - ->setMethod('POST') - ->generateForm(); -``` - -Ejemplo 4: Envío de Correos Electrónicos Personalizados -Supongamos que estás desarrollando una aplicación que envía correos electrónicos personalizados a los usuarios. FluentInterface puede simplificar la construcción de estos correos electrónicos. - -```php -$email = __(EmailBuilder::class) - ->setRecipient('usuario@example.com') - ->setSubject('¡Bienvenido!') - ->setBody('Hola, [nombre]. Gracias por unirte a nuestro sitio web.') - ->addAttachment('factura.pdf') - ->setSender('info@miempresa.com') - ->send(); - -``` - -Ejemplo 5: Generación de Consultas SQL Dinámicas -Supongamos que estás desarrollando una aplicación web que necesita generar consultas SQL dinámicas para interactuar con una base de datos. Puedes usar FluentInterface para construir estas consultas de manera programática y legible: - -```php -$query = __(QueryBuilder::class) - ->select('nombre', 'email') - ->from('usuarios') - ->where('edad', '>', 25) - ->andWhere('ciudad', '=', 'Nueva York') - ->orderBy('nombre', 'ASC') - ->limit(10) - ->execute(); -``` - -Ejemplo 6: Creación de Gráficos Interactivos -Supongamos que estás desarrollando una aplicación web que muestra gráficos interactivos a los usuarios. FluentInterface puede ayudarte a construir y configurar estos gráficos de manera flexible: - -```php -$chart = __(ChartBuilder::class) - ->setType('line') - ->setTitle('Ventas Mensuales') - ->addData('Enero', [100, 150, 200, 120]) - ->addData('Febrero', [120, 160, 180, 140]) - ->setXAxisLabels(['Semana 1', 'Semana 2', 'Semana 3', 'Semana 4']) - ->setYAxisLabel('Ventas (en miles)') - ->render(); -``` diff --git a/core/libraries/Fluent/fluent_helper b/core/libraries/Fluent/fluent_helper deleted file mode 100644 index b14669f..0000000 --- a/core/libraries/Fluent/fluent_helper +++ /dev/null @@ -1,21 +0,0 @@ - - - Total Downloads - - - Latest Stable Version - - - License - -

- -# Helpers Axm -Helpers diff --git a/core/libraries/Helpers/array_helper.php b/core/libraries/Helpers/array_helper.php deleted file mode 100644 index b851198..0000000 --- a/core/libraries/Helpers/array_helper.php +++ /dev/null @@ -1,148 +0,0 @@ - $sortFlag) { - $columns[] = $column; - $sortFlags[] = $sortFlag; - } - - $args = array_merge($array, $columns, $sortFlags); - - return array_multisort(...$args); - } -} - -if (!function_exists('flattenArrayWithDots')) { - /** - * Flattens a multidimensional array using dot notation as separators. - * - * @param iterable $array The multidimensional array - * @param string $prefix Something to initially prepend to the flattened keys - * @return array The flattened array - */ - function flattenArrayWithDots(iterable $array, string $prefix = ''): array - { - $flattened = []; - - foreach ($array as $key => $value) { - if (is_array($value)) { - $flattened += flattenArrayWithDots($value, $prefix . $key . '.'); - } else { - $flattened[$prefix . $key] = $value; - } - } - - return $flattened; - } -} diff --git a/core/libraries/Helpers/cookie_helper.php b/core/libraries/Helpers/cookie_helper.php deleted file mode 100644 index d8a6529..0000000 --- a/core/libraries/Helpers/cookie_helper.php +++ /dev/null @@ -1,89 +0,0 @@ -response->setCookie($name, $value, $expire, $domain, $path, $prefix, $secure, $httpOnly, $sameSite); - } -} - -if (!function_exists('getCookie')) { - - /** - * Retrieves the value of a cookie. - * - * @param string $name The name of the cookie - * @param bool $xssClean Specifies whether to sanitize the value for protection against cross-site scripting (optional) - * @return string|null The value of the cookie or null if not found - */ - function getCookie(string $name, bool $xssClean = false): ?string - { - $request = Axm::app()->request; - $prefix = $request->getCookiePrefix(); - - if (isset($_COOKIE[$name])) { - $cookieValue = $_COOKIE[$name]; - } else { - $cookieValue = $request->getCookie($prefix . $name); - } - - if ($xssClean) { - $cookieValue = filter_var($cookieValue, FILTER_SANITIZE_SPECIAL_CHARS); - } - - return $cookieValue; - } -} - -if (!function_exists('deleteCookie')) { - /** - * Deletes a cookie with the specified options. - * - * @param string $name The name of the cookie - * @param string $domain The domain of the cookie (optional) - * @param string $path The path of the cookie (optional) - * @param string $prefix The prefix for the cookie name (optional) - */ - function deleteCookie(string $name, string $domain = '', string $path = '/', string $prefix = '') - { - Axm::app()->response->deleteCookie($name, $domain, $path, $prefix); - } -} - -if (!function_exists('hasCookie')) { - /** - * Checks if a cookie exists with the specified options. - * - * @param string $name The name of the cookie - * @param string|null $value The value to match (optional) - * @param string $prefix The prefix for the cookie name (optional) - * - * @return bool True if the cookie exists, false otherwise - */ - function hasCookie(string $name, ?string $value = null, string $prefix = ''): bool - { - return Axm::app()->response->hasCookie($name, $value, $prefix); - } -} diff --git a/core/libraries/Helpers/date_helper.php b/core/libraries/Helpers/date_helper.php deleted file mode 100644 index 683867a..0000000 --- a/core/libraries/Helpers/date_helper.php +++ /dev/null @@ -1,46 +0,0 @@ -getTimestamp(); - } -} - -if (!function_exists('timeZoneSelect')) { - /** - * Genera un elemento select HTML para seleccionar la zona horaria. - * - * @param string $class La clase CSS para el elemento select (opcional) - * @param string $default La zona horaria predeterminada seleccionada (opcional) - * @param int $what Tipo de zonas horarias a incluir en la lista (opcional) - * @param string $country El código de país para filtrar las zonas horarias (opcional) - * @return string El elemento select HTML - */ - function timeZoneSelect(string $class = '', string $default = '', int $what = DateTimeZone::ALL, ?string $country = null): string - { - $timezones = DateTimeZone::listIdentifiers($what, $country); - - $buffer = "' . PHP_EOL; - } -} diff --git a/core/libraries/Helpers/filesystem_helper.php b/core/libraries/Helpers/filesystem_helper.php deleted file mode 100644 index ab55d52..0000000 --- a/core/libraries/Helpers/filesystem_helper.php +++ /dev/null @@ -1,474 +0,0 @@ - $fileInfo) { - // Saltar archivos ocultos si no se incluyen - if (!$hidden && $fileInfo->isHidden()) { - continue; - } - - // Agregar el separador de directorio al final si es un directorio - if ($fileInfo->isDir()) { - $path .= DIRECTORY_SEPARATOR; - } - - // Comprobar si se ha alcanzado la profundidad máxima del directorio - if ($directoryDepth === 0 || $depth < $directoryDepth) { - // Agregar el archivo o directorio a la matriz - $fileData[$path] = $fileInfo->isDir() ? [] : $fileInfo->getFilename(); - } else { - // Salir del bucle si se alcanza la profundidad máxima - break; - } - - // Actualizar la profundidad actual del directorio - $depth = $iterator->getDepth(); - } - - // Devolver la matriz de archivos y directorios - return $fileData; - } catch (Throwable $e) { - // En caso de error, devolver una matriz vacía - return []; - } - } -} - -if (!function_exists('directoryMirror')) { - /** - * Copia recursivamente los archivos y directorios desde el directorio de origen al directorio de destino. - * - * @param string $originDir Directorio de origen. - * @param string $targetDir Directorio de destino. - * @param bool $overwrite (Opcional) Indica si se sobrescribirán los archivos existentes en el directorio de destino. Por defecto es true. - * @throws InvalidArgumentException Si el directorio de origen no existe. - * @return void - */ - function directoryMirror(string $originDir, string $targetDir, bool $overwrite = true): void - { - // Comprobar si el directorio de origen existe - if (!is_dir($originDir = rtrim($originDir, '\\/'))) { - throw new InvalidArgumentException(sprintf('El directorio de origen "%s" no se encontró.', $originDir)); - } - - // Crear el directorio de destino si no existe - if (!is_dir($targetDir = rtrim($targetDir, '\\/'))) { - @mkdir($targetDir, 0755, true); - } - - // Longitud del directorio de origen - $dirLen = strlen($originDir); - - // Iterar sobre los archivos y directorios en el directorio de origen - foreach (new RecursiveIteratorIterator( - new RecursiveDirectoryIterator($originDir, FilesystemIterator::SKIP_DOTS), - RecursiveIteratorIterator::SELF_FIRST - ) as $file) { - $origin = $file->getPathname(); - $target = $targetDir . substr($origin, $dirLen); - - // Comprobar si es un directorio - if ($file->isDir()) { - // Crear el directorio en el directorio de destino si no existe - if (!is_dir($target)) { - mkdir($target, 0755); - } - } else { - // Comprobar si el archivo no existe en el directorio de destino o si se permite sobrescribir - if (!is_file($target) || ($overwrite && is_file($target))) { - // Copiar el archivo al directorio de destino - copy($origin, $target); - } - } - } - } -} - -if (!function_exists('writeFile')) { - /** - * Escribe datos en un archivo. - * - * @param string $path Ruta del archivo. - * @param string $data Datos a escribir en el archivo. - * @param string $mode (Opcional) Modo de apertura del archivo. Por defecto es 'wb'. - * @return bool True si se escribió correctamente, false en caso contrario. - */ - function writeFile(string $path, string $data, string $mode = 'wb'): bool - { - try { - $fp = fopen($path, $mode); - - if (!$fp) { - throw new RuntimeException(sprintf('No se pudo abrir el archivo "%s".', $path)); - } - - if (flock($fp, LOCK_EX)) { - $result = fwrite($fp, $data); - flock($fp, LOCK_UN); - } else { - throw new RuntimeException(sprintf('No se pudo bloquear el archivo "%s".', $path)); - } - - fclose($fp); - - return $result !== false; - } catch (Throwable $e) { - return false; - } - } -} - -if (!function_exists('deleteFiles')) { - /** - * Elimina archivos y directorios de manera recursiva. - * - * @param string $path Ruta del directorio o archivo a eliminar. - * @param bool $delDir (Opcional) Indica si se deben eliminar los directorios. Por defecto es false. - * @param bool $htdocs (Opcional) Indica si se deben excluir los archivos especiales de htdocs. Por defecto es false. - * @param bool $hidden (Opcional) Indica si se deben excluir los archivos ocultos. Por defecto es false. - * @return bool True si se eliminaron los archivos y directorios correctamente, false en caso contrario. - */ - function deleteFiles(string $path, bool $delDir = false, bool $htdocs = false, bool $hidden = false): bool - { - $path = realpath($path) ?: $path; - $path = rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; - - try { - $iterator = new RecursiveIteratorIterator( - new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS), - RecursiveIteratorIterator::CHILD_FIRST - ); - - foreach ($iterator as $object) { - $filename = $object->getFilename(); - - if (!$hidden && $filename[0] === '.') { - continue; - } - - if (!$htdocs || !preg_match('/^(\.htaccess|index\.(html|htm|php)|web\.config)$/i', $filename)) { - if ($object->isDir() && $delDir) { - rmdir($object->getPathname()); - } else { - unlink($object->getPathname()); - } - } - } - - return true; - } catch (Throwable $e) { - return false; - } - } -} - -if (!function_exists('getFileNames')) { - /** - * Obtiene los nombres de archivo de un directorio y sus subdirectorios de manera recursiva. - * - * @param string $sourceDir Ruta del directorio fuente. - * @param bool|null $includePath (Opcional) Indica si se debe incluir la ruta completa de los archivos. - * false para solo incluir el nombre de archivo, null para incluir la ruta relativa - * al directorio fuente y true para incluir la ruta completa. - * Por defecto es false. - * @param bool $hidden (Opcional) Indica si se deben incluir los archivos ocultos. Por defecto es false. - * @return array Un array con los nombres de archivo. - */ - function getFileNames(string $sourceDir, ?bool $includePath = false, bool $hidden = false): array - { - $files = []; - - $sourceDir = realpath($sourceDir) ?: $sourceDir; - $sourceDir = rtrim($sourceDir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; - - try { - $iterator = new RecursiveIteratorIterator( - new RecursiveDirectoryIterator($sourceDir, RecursiveDirectoryIterator::SKIP_DOTS), - RecursiveIteratorIterator::SELF_FIRST - ); - - foreach ($iterator as $name => $object) { - $basename = pathinfo($name, PATHINFO_BASENAME); - - if (!$hidden && $basename[0] === '.') { - continue; - } - - if ($includePath === false) { - $files[] = $basename; - } elseif ($includePath === null) { - $relativePath = str_replace($sourceDir, '', $name); - $files[] = ltrim($relativePath, DIRECTORY_SEPARATOR); - } else { - $files[] = $name; - } - } - } catch (Throwable $e) { - return []; - } - - sort($files); - - return $files; - } -} - -if (!function_exists('getDirFileInfo')) { - /** - * Obtiene información sobre los archivos y directorios de un directorio. - * - * @param string $sourceDir Ruta del directorio fuente. - * @param bool $topLevelOnly (Opcional) Indica si se debe obtener información solo del nivel superior del directorio. - * Por defecto es true. - * @param bool $recursion (Opcional) Indica si se debe realizar una búsqueda recursiva en los subdirectorios. - * Por defecto es false. - * @return array Un array con información sobre los archivos y directorios. - */ - function getDirFileInfo(string $sourceDir, bool $topLevelOnly = true, bool $recursion = false): array - { - $fileData = []; - $relativePath = rtrim($sourceDir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; - - try { - $fp = opendir($sourceDir); - - while (false !== ($file = readdir($fp))) { - if ($file === '.' || $file === '..') { - continue; - } - - $filePath = $sourceDir . $file; - - if (is_dir($filePath) && !$topLevelOnly && $recursion) { - $fileData[$file] = getDirFileInfo($filePath, false, true); - } else { - $fileData[$file] = getFileInfo($filePath); - $fileData[$file]['relative_path'] = $relativePath; - } - } - - closedir($fp); - - return $fileData; - } catch (Throwable $fe) { - return []; - } - } -} - -if (!function_exists('getFileInfo')) { - /** - * Get File Info - * - * Given a file and path, returns the name, path, size, date modified - * Second parameter allows you to explicitly declare what information you want returned - * Options are: name, serverPath, size, date, readable, writable, executable, fileperms - * Returns false if the file cannot be found. - * - * @param string $file Path to file - * @param mixed $returnedValues Array or comma separated string of information returned - * @return array|null - */ - function getFileInfo(string $file, $returnedValues = ['name', 'serverPath', 'size', 'date']) - { - if (!is_file($file)) { - return null; - } - - $fileInfo = []; - - if (is_string($returnedValues)) { - $returnedValues = explode(',', $returnedValues); - } - - foreach ($returnedValues as $key) { - switch ($key) { - case 'name': - $fileInfo['name'] = basename($file); - break; - - case 'serverPath': - $fileInfo['serverPath'] = $file; - break; - - case 'size': - $fileInfo['size'] = filesize($file); - break; - - case 'date': - $fileInfo['date'] = filemtime($file); - break; - - case 'readable': - $fileInfo['readable'] = is_readable($file); - break; - - case 'writable': - $fileInfo['writable'] = is_writable($file); - break; - - case 'executable': - $fileInfo['executable'] = is_executable($file); - break; - - case 'fileperms': - $fileInfo['fileperms'] = fileperms($file); - break; - } - } - - return $fileInfo; - } -} - -if (!function_exists('symbolicPermissions')) { - /** - * Convierte los permisos numéricos en una representación simbólica. - * - * @param int $perms Los permisos numéricos. - * @return string La representación simbólica de los permisos. - */ - function symbolicPermissions(int $perms): string - { - $symbolic = ''; - $types = [ - 0xC000 => 's', // Socket - 0xA000 => 'l', // Symbolic Link - 0x8000 => '-', // Regular - 0x6000 => 'b', // Block special - 0x4000 => 'd', // Directory - 0x2000 => 'c', // Character special - 0x1000 => 'p', // FIFO pipe - ]; - - foreach ($types as $type => $char) { - if (($perms & $type) === $type) { - $symbolic = $char; - break; - } - } - - $permissions = [ - 0x0100 => 'r', // Owner read - 0x0080 => 'w', // Owner write - 0x0040 => 'x', // Owner execute - 0x0800 => 's', // Setuid - 0x0020 => 'r', // Group read - 0x0010 => 'w', // Group write - 0x0008 => 'x', // Group execute - 0x0400 => 's', // Setgid - 0x0004 => 'r', // World read - 0x0002 => 'w', // World write - 0x0001 => 'x', // World execute - 0x0200 => 't', // Sticky bit - ]; - - foreach ($permissions as $permission => $char) { - if (($perms & $permission) === $permission) { - $symbolic .= $char; - } else { - $symbolic .= '-'; - } - } - - return $symbolic; - } -} - -if (!function_exists('octalPermissions')) { - /** - * Convierte los permisos numéricos en una representación octal de tres dígitos. - * - * @param int $perms Los permisos numéricos. - * @return string La representación octal de los permisos. - */ - function octalPermissions(int $perms): string - { - return str_pad(octdec(sprintf('%o', $perms)), 3, '0', STR_PAD_LEFT); - } -} - -if (!function_exists('sameFile')) { - /** - * Verifica si dos archivos son iguales comparando sus sumas de verificación MD5. - * - * @param string $file1 Ruta del primer archivo. - * @param string $file2 Ruta del segundo archivo. - * @return bool True si los archivos son iguales, False en caso contrario. - */ - function sameFile(string $file1, string $file2): bool - { - if (!is_file($file1) || !is_file($file2)) { - return false; - } - - if (filesize($file1) !== filesize($file2)) { - return false; - } - - return md5_file($file1) === md5_file($file2); - } -} - -if (!function_exists('setRealpath')) { - /** - * Resuelve la ruta absoluta de un archivo o directorio y agrega una barra diagonal al final si es un directorio. - * - * @param string $path Ruta a resolver. - * @param bool $checkExistence Indica si se debe verificar la existencia del archivo o directorio. - * @return string Ruta absoluta resuelta. - * @throws InvalidArgumentException Si la ruta es una URL. - * @throws InvalidArgumentException Si la ruta no es válida y la verificación de existencia está habilitada. - */ - function setRealpath(string $path, bool $checkExistence = false): string - { - // Security check to make sure the path is NOT a URL. No remote file inclusion! - if (preg_match('#^(https?|ftp)://#i', $path)) { - throw new InvalidArgumentException('The path you submitted must be a local server path, not a URL'); - } - - // Resolve the path - $resolvedPath = realpath($path); - if ($resolvedPath === false) { - if ($checkExistence) { - throw new InvalidArgumentException('Not a valid path: ' . $path); - } else { - return $path; - } - } - - // Add a trailing slash, if this is a directory - if (is_dir($resolvedPath)) { - $resolvedPath = rtrim($resolvedPath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; - } - - return $resolvedPath; - } -} diff --git a/core/libraries/Helpers/form_helper.php b/core/libraries/Helpers/form_helper.php deleted file mode 100644 index 51f50ef..0000000 --- a/core/libraries/Helpers/form_helper.php +++ /dev/null @@ -1,687 +0,0 @@ - $action, 'method' => strtolower($method)], $attributes); - $formTag = createHtmlTag('form', $formAttrs, true); - - // Agregar token CSRF - $csrfToken = Axm::app()->getCsrfToken(); - $csrfTag = createHtmlTag('input', ['type' => 'hidden', 'name' => '_csrf_token_', 'value' => $csrfToken], false); - $_SESSION['_csrf_token_'] = $csrfToken; - - $html = new self(); - $html->addElement($formTag . $csrfTag); - - return $html; - } -} - -if (!function_exists('end')) { - function end(): string - { - return '' . PHP_EOL; - } -} - - -if (!function_exists('createHtmlTag')) { - function createHtmlTag($tagName, $attributes = [], $closingTag = true): string - { - $tag = '<' . $tagName; - $tag .= attrs($attributes); - - if ($closingTag && $tagName !== 'form') { - $tag .= '>'; - } else { - $tag .= ' />'; - } - - return $tag; - } -} - -if (!function_exists('formHidden')) { - /** - * Hidden Input Field - * - * Generates hidden fields. You can pass a simple key/value string or - * an associative array with multiple values. - * - * @param array|string $name Field name or associative array to create multiple fields - * @param array|string $value Field value - */ - function formHidden($name, $value = '', bool $recursing = false): string - { - static $form; - - if ($recursing === false) { - $form = "\n"; - } - - if (is_array($name)) { - foreach ($name as $key => $val) { - formHidden($key, $val, true); - } - - return $form; - } - - if (!is_array($value)) { - $form .= formInput($name, $value, '', 'hidden'); - } else { - foreach ($value as $k => $v) { - $k = is_int($k) ? '' : $k; - formHidden($name . '[' . $k . ']', $v, true); - } - } - - return $form; - } -} - -if (!function_exists('formInput')) { - /** - * Text Input Field. If 'type' is passed in the $type field, it will be - * used as the input type, for making 'email', 'phone', etc input fields. - * - * @param mixed $data - * @param mixed $extra - */ - function formInput($data = '', string $value = '', $extra = '', string $type = 'text'): string - { - $defaults = [ - 'type' => $type, - 'name' => is_array($data) ? '' : $data, - 'value' => $value, - ]; - - return '\n"; - } -} - -if (!function_exists('formPassword')) { - /** - * Password Field - * - * Identical to the input function but adds the "password" type - * - * @param mixed $data - * @param mixed $extra - */ - function formPassword($data = '', string $value = '', $extra = ''): string - { - if (!is_array($data)) { - $data = ['name' => $data]; - } - $data['type'] = 'password'; - - return formInput($data, $value, $extra); - } -} - -if (!function_exists('formUpload')) { - /** - * Upload Field - * - * Identical to the input function but adds the "file" type - * - * @param mixed $data - * @param mixed $extra - */ - function formUpload($data = '', string $value = '', $extra = ''): string - { - $defaults = [ - 'type' => 'file', - 'name' => '', - ]; - - if (!is_array($data)) { - $data = ['name' => $data]; - } - - $data['type'] = 'file'; - - return '\n"; - } -} - -if (!function_exists('formTextarea')) { - /** - * Textarea field - * - * @param mixed $data - * @param mixed $extra - */ - function formTextarea($data = '', string $value = '', $extra = ''): string - { - $defaults = [ - 'name' => is_array($data) ? '' : $data, - 'cols' => '40', - 'rows' => '10', - ]; - if (!is_array($data) || !isset($data['value'])) { - $val = $value; - } else { - $val = $data['value']; - unset($data['value']); // textareas don't use the value attribute - } - - // Unsets default rows and cols if defined in extra field as array or string. - if ((is_array($extra) && array_key_exists('rows', $extra)) || (is_string($extra) && stripos(preg_replace('/\s+/', '', $extra), 'rows=') !== false)) { - unset($defaults['rows']); - } - - if ((is_array($extra) && array_key_exists('cols', $extra)) || (is_string($extra) && stripos(preg_replace('/\s+/', '', $extra), 'cols=') !== false)) { - unset($defaults['cols']); - } - - return '\n"; - } -} - -if (!function_exists('formMultiselect')) { - /** - * Multi-select menu - * - * @param mixed $name - * @param mixed $extra - */ - function formMultiselect($name = '', array $options = [], array $selected = [], $extra = ''): string - { - $extra = stringifyAttributes($extra); - - if (stripos($extra, 'multiple') === false) { - $extra .= ' multiple="multiple"'; - } - - return formDropdown($name, $options, $selected, $extra); - } -} - -if (!function_exists('formDropdown')) { - /** - * Drop-down Menu - * - * @param mixed $data - * @param mixed $options - * @param mixed $selected - * @param mixed $extra - */ - function formDropdown($data = '', $options = [], $selected = [], $extra = ''): string - { - $defaults = []; - if (is_array($data)) { - if (isset($data['selected'])) { - $selected = $data['selected']; - unset($data['selected']); // select tags don't have a selected attribute - } - if (isset($data['options'])) { - $options = $data['options']; - unset($data['options']); // select tags don't use an options attribute - } - } else { - $defaults = ['name' => $data]; - } - - if (!is_array($selected)) { - $selected = [$selected]; - } - if (!is_array($options)) { - $options = [$options]; - } - - // If no selected state was submitted we will attempt to set it automatically - if (empty($selected)) { - if (is_array($data)) { - if (isset($data['name'], $_POST[$data['name']])) { - $selected = [$_POST[$data['name']]]; - } - } elseif (isset($_POST[$data])) { - $selected = [$_POST[$data]]; - } - } - - // Standardize selected as strings, like the option keys will be - foreach ($selected as $key => $item) { - $selected[$key] = (string) $item; - } - - $extra = stringifyAttributes($extra); - $multiple = (count($selected) > 1 && stripos($extra, 'multiple') === false) ? ' multiple="multiple"' : ''; - $form = '\n"; - } -} - -if (!function_exists('formCheckbox')) { - /** - * Checkbox Field - * - * @param mixed $data - * @param mixed $extra - */ - function formCheckbox($data = '', string $value = '', bool $checked = false, $extra = ''): string - { - $defaults = [ - 'type' => 'checkbox', - 'name' => (!is_array($data) ? $data : ''), - 'value' => $value, - ]; - - if (is_array($data) && array_key_exists('checked', $data)) { - $checked = $data['checked']; - if ($checked === false) { - unset($data['checked']); - } else { - $data['checked'] = 'checked'; - } - } - - if ($checked === true) { - $defaults['checked'] = 'checked'; - } elseif (isset($defaults['checked'])) { - unset($defaults['checked']); - } - - return '\n"; - } -} - -if (!function_exists('formRadio')) { - /** - * Radio Button - * - * @param mixed $data - * @param mixed $extra - */ - function formRadio($data = '', string $value = '', bool $checked = false, $extra = ''): string - { - if (!is_array($data)) { - $data = ['name' => $data]; - } - $data['type'] = 'radio'; - - return formCheckbox($data, $value, $checked, $extra); - } -} - -if (!function_exists('formSubmit')) { - /** - * Submit Button - * - * @param mixed $data - * @param mixed $extra - */ - function formSubmit($data = '', string $value = '', $extra = ''): string - { - return formInput($data, $value, $extra, 'submit'); - } -} - -if (!function_exists('formReset')) { - /** - * Reset Button - * - * @param mixed $data - * @param mixed $extra - */ - function formReset($data = '', string $value = '', $extra = ''): string - { - return formInput($data, $value, $extra, 'reset'); - } -} - -if (!function_exists('formButton')) { - /** - * Form Button - * - * @param mixed $data - * @param mixed $extra - */ - function formButton($data = '', string $content = '', $extra = ''): string - { - $defaults = [ - 'name' => is_array($data) ? '' : $data, - 'type' => 'button', - ]; - - if (is_array($data) && isset($data['content'])) { - $content = $data['content']; - unset($data['content']); // content is not an attribute - } - - return '\n"; - } -} - -if (!function_exists('formLabel')) { - /** - * Form Label Tag - * - * @param string $labelText The text to appear onscreen - * @param string $id The id the label applies to - * @param array $attributes Additional attributes - */ - function formLabel(string $labelText = '', string $id = '', array $attributes = []): string - { - $label = ' $val) { - $label .= ' ' . $key . '="' . $val . '"'; - } - } - - return $label . '>' . $labelText . ''; - } -} - -if (!function_exists('formDatalist')) { - /** - * Datalist - * - * The element specifies a list of pre-defined options for an element. - * Users will see a drop-down list of pre-defined options as they input data. - * The list attribute of the element, must refer to the id attribute of the element. - */ - function formDatalist(string $name, string $value, array $options): string - { - $data = [ - 'type' => 'text', - 'name' => $name, - 'list' => $name . '_list', - 'value' => $value, - ]; - - $out = formInput($data) . "\n"; - - $out .= ""; - - foreach ($options as $option) { - $out .= "' . "\n"); - } -} - -if (!function_exists('formFieldset')) { - /** - * Fieldset Tag - * - * Used to produce
text. To close fieldset - * use formFieldsetClose() - * - * @param string $legendText The legend text - * @param array $attributes Additional attributes - */ - function formFieldset(string $legendText = '', array $attributes = []): string - { - $fieldset = '\n"; - - if ($legendText !== '') { - return $fieldset . '' . $legendText . "\n"; - } - - return $fieldset; - } -} - -if (!function_exists('formFieldsetClose')) { - /** - * Fieldset Close Tag - */ - function formFieldsetClose(string $extra = ''): string - { - return '
' . $extra; - } -} - -if (!function_exists('formClose')) { - /** - * Form Close Tag - */ - function formClose(string $extra = ''): string - { - return '' . $extra; - } -} - -if (!function_exists('setValue')) { - /** - * Form Value - * - * Grabs a value from the POST array for the specified field so you can - * re-populate an input field or textarea - * @param string $field Field name - * @param string|string[] $default Default value - * @param bool $htmlEscape Whether to escape HTML special characters or not - * @return string|string[] - */ - function setValue(string $field, $default = '', bool $htmlEscape = true) - { - $request = app()->request; - - // Try any old input data we may have first - $value = $request->getOldInput($field); - - if ($value === null) { - $value = $request->getPost($field) ?? $default; - } - - return ($htmlEscape) ? esc($value) : $value; - } -} - -if (!function_exists('setSelect')) { - /** - * Set Select - * - * Let's you set the selected value of a - - - - - -``` - -As you can see, we are "binding" the public `$title` and `$content` properties in the form above using `axm:model`. This is one of the most commonly used and powerful features of Raxm. - -In addition to binding `$title` and `$content`, we are using `axm:submit` to capture the `submit` event when the "Save" button is clicked and invoking the `save()` action. This action will persist the form input to the database. - -After the new post is created in the database, we redirect the user to the `ShowPosts` component page and show them a "flash" message that the new post was created. - -### Adding validation - -To avoid storing incomplete or dangerous user input, most forms need some sort of input validation. - -Raxm makes validating your forms as simple as adding `#[Rule]` attributes above the properties you want to be validated. - -Once a property has a `#[Rule]` attribute attached to it, the validation rule will be applied to the property's value any time it's updated server-side. - -Let's add some basic validation rules to the `$title` and `$content` properties in our `CreatePost` component: - -```php -validate(); // [tl! highlight] - - Post::create( - $this->only(['title', 'content']) - ); - - return $this->redirect('/posts'); - } - - public function render() - { - return view('raxm.create-post'); - } -} -``` - -We'll also modify our Blade template to show any validation errors on the page. - -```html -
- -
- - -
- - -
- - -
- - -
-``` - -Now, if the user tries to submit the form without filling in any of the fields, they will see validation messages telling them which fields are required before saving the post. - -Raxm has a lot more validation features to offer. For more information, visit our [dedicated documentation page on Validation](/docs/validation). - -### Extracting a form object - -If you are working with a large form and prefer to extract all of its properties, validation logic, etc., into a separate class, Raxm offers form objects. - -Form objects allow you to re-use form logic across components and provide a nice way to keep your component class cleaner by grouping all form-related code into a separate class. - -You can either create a form class by hand or use the convenient axm command: - -```shell -php axm raxm:form PostForm -``` - -The above command will create a file called `app/Raxm/Forms/PostForm.php`. - -Let's rewrite the `CreatePost` component to use a `PostForm` class: - -```php -validate(); - - Post::create( - $this->form->all() - ); - - return $this->redirect('/posts'); - } - - public function render() - { - return view('raxm.create-post'); - } -} -``` - -```html -
- -
- -
- - -
- -
- - -
-``` - -If you'd like, you can also extract the post creation logic into the form object like so: - -```php -all()); - } -} -``` - -Now you can call `$this->form->store()` from the component: - -```php -class CreatePost extends Component -{ - public PostForm $form; - - public function save() - { - $this->form->store(); - - return $this->redirect('/posts'); - } - - // ... -} -``` - -If you want to use this form object for both a create and update form, you can easily adapt it to handle both use cases. - -Here's what it would look like to use this same form object for an `UpdatePost` component and fill it with initial data: - -```php -form->setPost($post); - } - - public function save() - { - $this->form->update(); - - return $this->redirect('/posts'); - } - - public function render() - { - return view('raxm.create-post'); - } -} -``` - -```php -post = $post; - - $this->title = $post->title; - - $this->content = $post->content; - } - - public function store() - { - Post::create($this->only(['title', 'content'])); - } - - public function update() - { - $this->post->update( - $this->all() - ); - } -} -``` - -As you can see, we've added a `setPost()` method to the `PostForm` object to optionally allow for filling the form with existing data as well as storing the post on the form object for later use. We've also added an `update()` method for updating the existing post. - -Form objects are not required when working with Raxm, but they do offer a nice abstraction for keeping your components free of repetitive boilerplate. - -### Resetting form fields - -If you are using a form object, you may want to reset the form after it has been submitted. This can be done by calling the `reset()` method: - -```php -all()); - - $this->reset(); // [tl! highlight] - } -} -``` - -You can also reset specific properties by passing the property names into the `reset()` method: - -```php -$this->reset('title'); - -// Or multiple at once... - -$this->reset('title', 'content'); -``` - -### Showing a loading indicator - -By default, Raxm will automatically disable submit buttons and mark inputs as `readonly` while a form is being submitted, preventing the user from submitting the form again while the first submission is being handled. - -However, it can be difficult for users to detect this "loading" state without extra affordances in your application's UI. - -Here's an example of adding a small loading spinner to the "Save" button via `axm:loading` so that a user understands that the form is being submitted: - -```html - -``` - -Now, when a user presses "Save", a small, inline spinner will show up. - -Raxm's `axm:loading` feature has a lot more to offer. Visit the [Loading documentation to learn more.](/docs/loading) - -## Live-updating fields - -By default, Raxm only sends a network request when the form is submitted (or any other [action](/docs/actions) is called), not while the form is being filled out. - -Take the `CreatePost` component, for example. If you want to make sure the "title" input field is synchronized with the `$title` property on the backend as the user types, you may add the `.live` modifier to `axm:model` like so: - -```html - -``` - -Now, as a user types into this field, network requests will be sent to the server to update `$title`. This is useful for things like a real-time search, where a dataset is filtered as a user types into a search box. - -## Only updating fields on _blur_ - -For most cases, `axm:model.live` is fine for real-time form field updating; however, it can be overly network resource-intensive on text inputs. - -If instead of sending network requests as a user types, you want to instead only send the request when a user "tabs" out of the text input (also referred to as "blurring" an input), you can use the `.blur` modifier instead: - -```html - -``` - -Now the component class on the server won't be updated until the user presses tab or clicks away from the text input. - -## Real-time validation - -Sometimes, you may want to show validation errors as the user fills out the form. This way, they are alerted early that something is wrong instead of having to wait until the entire form is filled out. - -Raxm handles this sort of thing automatically. By using `.live` or `.blur` on `axm:model`, Raxm will send network requests as the user fills out the form. Each of those network requests will run the appropriate validation rules before updating each property. If validation fails, the property won't be updated on the server and a validation message will be shown to the user: - -```html - - -
- - -
-``` - -```php -#[Rule('required|min:5')] -public $title = ''; -``` - -Now, if the user only types three characters into the "title" input, then clicks on the next input in the form, a validation message will be shown to them indicating there is a five character minimum for that field. - -For more information, check out the [validation documentation page](/docs/validation). - -## Real-time form saving - -If you want to automatically save a form as the user fills it out rather than wait until the user clicks "submit", you can do so using Raxm's `updated()` hook: - -```php -post = $post; - } - - public function updated($name, $value) - { - $this->post->update([ - $name => $value, - ]); - } - - public function render() - { - return view('raxm.create-post'); - } -} -``` - -```html -
- -
- - -
- - -
- - -
-
-``` - -In the above example, when a user completes a field (by clicking or tabbing to the next field), a network request is sent to update that property on the component. Immediately after the property is updated on the class, the `updated()` hook is called for that specific property name and its new value. - -We can use this hook to update only that specific field in the database. - -Additionally, because we have the `#[Rule]` attributes attached to those properties, the validation rules will be run before the property is updated and the `updated()` hook is called. - -To learn more about the "updated" lifecycle hook and other hooks, [visit the lifecycle hooks documentation](/docs/lifecycle-hooks). - -## Showing dirty indicators - -In the real-time saving scenario discussed above, it may be helpful to indicate to users when a field hasn't been persisted to the database yet. - -For example, if a user visits an `UpdatePost` page and starts modifying the title of the post in a text input, it may be unclear to them when the title is actually being updated in the database, especially if there is no "Save" button at the bottom of the form. - -Raxm provides the `axm:dirty` directive to allow you to toggle elements or modify classes when an input's value diverges from the server-side component: - -```html - -``` - -In the above example, when a user types into the input field, a yellow border will appear around the field. When the user tabs away, the network request is sent and the border will disappear; signaling to them that the input has been persisted and is no longer "dirty". - -If you want to toggle an entire element's visibility, you can do so by using `axm:dirty` in conjunction with `axm:target`. `axm:target` is used to specify which piece of data you want to watch for "dirtiness". In this case, the "title" field: - -```html - - -
Unsaved...
-``` - -## Debouncing input - -When using `.live` on a text input, you may want more fine-grained control over how often a network request is sent. By default, a debounce of "250ms" is applied to the input; however, you can customize this using the `.debounce` modifier: - -```html - -``` - -Now that `.debounce.150ms` has been added to the field, a shorter debounce of "150ms" will be used when handling input updates for this field. In other words, as a user types, a network request will only be sent if the user stops typing for at least 150 milliseconds. - -## Throttling input - -As stated previously, when an input debounce is applied to a field, a network request will not be sent until the user has stopped typing for a certain amount of time. This means if the user continues typing a long message, a network request won't be sent until the user is finished. - -Sometimes this isn't the desired behavior, and you would rather send a request as the user types, not when they've finished or taken a break. - -In these cases, you can instead use `.throttle` to signify a time interval to send network requests: - -```html - -``` - -In the above example, as a user is typing continuously in the "title" field, a network request will be sent every 150 milliseconds until the user is finished. - -## Extracting input fields to Blade components - -Even in a small component such as the `CreatePost` example we've been discussing, we end up duplicating lots of form field boilerplate like validation messages and labels. - -It can be helpful to extract repetitive UI elements such as these into dedicated [Blade components](https://laravel.com/docs/blade#components) to be shared across your application. - -For example, below is the original Blade template from the `CreatePost` component. We will be extracting the following two text inputs into dedicated Blade components: - -```html -
- - -
- - -
- - - -
- - -
- - -
-``` - -## Input fields - -Raxm supports most native input elements out of the box. Meaning you should just be able to attach `axm:model` to any input element in the browser and easily bind properties to them. - -Here's a comprehensive list of the different available input types and how you use them in a Raxm context. - -### Text inputs - -First and foremost, text inputs are the bedrock of most forms. Here's how to bind a property named "title" to one: - -```html - -``` - -### Textarea inputs - -Textarea elements are similarly straightforward. Simply add `axm:model` to a textarea and the value will be bound: - -```html - -``` - -If the "content" value is initialized with a string, Raxm will fill the textarea with that value - there's no need to do something like the following: - -```html - - - -``` - -### Checkboxes - -Checkboxes can be used for single values, such as when toggling a boolean property. Or, checkboxes may be used to toggle a single value in a group of related values. We'll discuss both scenarios: - -#### Single checkbox - -At the end of a signup form, you might have a checkbox allowing the user to opt-in to email updates. You might call this property `$receiveUpdates`. You can easily bind this value to the checkbox using `axm:model`: - -```html - -``` - -Now when the `$receiveUpdates` value is `false`, the checkbox will be unchecked. Of course, when the value is `true`, the checkbox will be checked. - -#### Multiple checkboxes - -Now, let's say in addition to allowing the user to decide to receive updates, you have an array property in your class called `$updateTypes`, allowing the user to choose from a variety of update types: - -```php -public $updateTypes = []; -``` - -By binding multiple checkboxes to the `$updateTypes` property, the user can select multiple update types and they will be added to the `$updateTypes` array property: - -```html - - - -``` - -For example, if the user checks the first two boxes but not the third, the value of `$updateTypes` will be: `["email", "sms"]` - -### Radio buttons - -To toggle between two different values for a single property, you may use radio buttons: - -```html - - -``` - -### Select dropdowns - -Raxm makes it simple to work with ` - - - - - ... - -``` - -When a specific state is selected, for example, "Alaska", the `$state` property on the component will be set to `AK`. If you would prefer the value to be set to "Alaska" instead of "AK", you can leave the `value=""` attribute off the `