From 1c7aad8a8dee1849254a4ffeb5ac5697c83425be Mon Sep 17 00:00:00 2001 From: Michael Darko Date: Tue, 21 Dec 2021 19:18:33 +0000 Subject: [PATCH 01/14] :memo: updated readme --- README.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8fcc951..0231e44 100644 --- a/README.md +++ b/README.md @@ -161,8 +161,6 @@ To report a security vulnerability, you can reach out to [@mychidarko](https://t Tobias Herber - - @@ -170,6 +168,8 @@ To report a security vulnerability, you can reach out to [@mychidarko](https://t Pjotr Savitski + + @@ -184,6 +184,13 @@ To report a security vulnerability, you can reach out to [@mychidarko](https://t jess + + + +
+ Sergey Romanenko +
+ From 981c4c3c88d91fd3a3c345ec0898d061ede79278 Mon Sep 17 00:00:00 2001 From: Michael Darko Date: Tue, 21 Dec 2021 19:18:56 +0000 Subject: [PATCH 02/14] :wrench: updated gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index be5f234..7f40cfa 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ composer.lock package-lock.json vendor/ +test/ # OS Generated .DS_Store* From 037c1260182ae99c3c8d1367658a5da03286443c Mon Sep 17 00:00:00 2001 From: Michael Darko Date: Fri, 31 Dec 2021 12:36:27 +0000 Subject: [PATCH 03/14] :wrench: added nested config --- src/Config.php | 65 ++++++++++++++++++++++++++++++------------- tests/config.test.php | 10 +++++++ 2 files changed, 56 insertions(+), 19 deletions(-) diff --git a/src/Config.php b/src/Config.php index 891ff4e..a40135a 100644 --- a/src/Config.php +++ b/src/Config.php @@ -12,20 +12,19 @@ class Config { protected static $settings = [ - "app" => null, - "app.down" => false, - "mode" => "development", - "debug" => true, - "log.writer" => null, - "log.level" => null, - "log.enabled" => false, - "log.dir" => __DIR__ . "/../../../../storage/logs/", - "log.file" => "log.txt", - "log.open" => true, - "http.version" => "1.1", - // views - "views.path" => null, - "views.cachePath" => null, + 'app' => ['down' => false, 'instance' => null], + 'mode' => 'development', + 'debug' => true, + 'log' => [ + 'writer' => null, + 'level' => null, + 'enabled' => false, + 'dir' => __DIR__ . '/../../../../storage/logs/', + 'file' => 'log.txt', + 'open' => true, + ], + 'http' => ['version' => '1.1'], + 'views' => ['path' => null, 'cachePath' => null], ]; /** @@ -37,16 +36,38 @@ class Config public static function set($item, $value = null) { if (is_string($item)) { - static::$settings[$item] = $value; - } else { - if ($value === true) { - static::$settings = array_merge_recursive(static::$settings, $item); + if (!strpos($item, '.')) { + static::$settings[$item] = $value; } else { - static::$settings = array_merge(static::$settings, $item); + static::$settings = array_merge( + static::$settings, + static::mapConfig($item, $value) + ); + } + } else { + foreach ($item as $k => $v) { + static::set($k, $v); } } } + /** + * Map nested config to their parents recursively + */ + protected static function mapConfig(string $item, $value = null) + { + $config = explode('.', $item); + + if (count($config) > 2) { + trigger_error('Nested config can\'t be more than 1 level deep'); + } + + return [$config[0] => array_merge( + static::$settings[$config[0]], + [$config[1] => $value] + )]; + } + /** * Get configuration * @@ -55,6 +76,12 @@ public static function set($item, $value = null) public static function get($item = null) { if ($item) { + $items = explode('.', $item); + + if (count($items) > 1) { + return static::$settings[$items[0]][$items[1]]; + } + return static::$settings[$item] ?? null; } diff --git a/tests/config.test.php b/tests/config.test.php index a0bd4d0..a1e7a87 100644 --- a/tests/config.test.php +++ b/tests/config.test.php @@ -19,3 +19,13 @@ expect($appMode)->toBe($testMode); }); + +test('nested config', function () { + app()->config('app.key', '2'); + + $appConfig = app()->config('app'); + + expect(isset($appConfig['key']))->toBeTrue(); + expect($appConfig['key'])->toBe('2'); + expect(app()->config('app.key'))->toBe('2'); +}); From a70be5c59870561f9394d674f020f59de21d45d5 Mon Sep 17 00:00:00 2001 From: Michael Darko Date: Fri, 31 Dec 2021 12:49:18 +0000 Subject: [PATCH 04/14] :sparkles: added support for custom groups --- src/Config.php | 2 +- tests/config.test.php | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/Config.php b/src/Config.php index a40135a..24b30b3 100644 --- a/src/Config.php +++ b/src/Config.php @@ -63,7 +63,7 @@ protected static function mapConfig(string $item, $value = null) } return [$config[0] => array_merge( - static::$settings[$config[0]], + static::$settings[$config[0]] ?? [], [$config[1] => $value] )]; } diff --git a/tests/config.test.php b/tests/config.test.php index a1e7a87..059cf37 100644 --- a/tests/config.test.php +++ b/tests/config.test.php @@ -29,3 +29,23 @@ expect($appConfig['key'])->toBe('2'); expect(app()->config('app.key'))->toBe('2'); }); + +test('nested config (array)', function () { + app()->config(['app.key' => '2']); + + $appConfig = app()->config('app'); + + expect(isset($appConfig['key']))->toBeTrue(); + expect($appConfig['key'])->toBe('2'); + expect(app()->config('app.key'))->toBe('2'); +}); + +test('nested config (custom group)', function () { + app()->config(['home.key' => '2']); + + $homeConfig = app()->config('home'); + + expect(isset($homeConfig['key']))->toBeTrue(); + expect($homeConfig['key'])->toBe('2'); + expect(app()->config('home.key'))->toBe('2'); +}); From 83363d3ca97dad3c23c45ad091924fc2c2f8ccd9 Mon Sep 17 00:00:00 2001 From: Michael Darko Date: Fri, 31 Dec 2021 15:40:07 +0000 Subject: [PATCH 05/14] :ambulance: fixed error on unset key --- src/Config.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Config.php b/src/Config.php index 24b30b3..f6e1e3b 100644 --- a/src/Config.php +++ b/src/Config.php @@ -79,7 +79,7 @@ public static function get($item = null) $items = explode('.', $item); if (count($items) > 1) { - return static::$settings[$items[0]][$items[1]]; + return static::$settings[$items[0]][$items[1]] ?? null; } return static::$settings[$item] ?? null; From 128d7fc2ee29fd277d52b5dc3f3d3aa3f8611803 Mon Sep 17 00:00:00 2001 From: Michael Darko Date: Wed, 26 Jan 2022 17:10:06 +0000 Subject: [PATCH 06/14] =?UTF-8?q?=F0=9F=94=A5=20removed=20unused=20files?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Exception/General.php | 232 -------------------------------------- src/Exception/Pass.php | 21 ---- src/Exception/Stop.php | 19 ---- 3 files changed, 272 deletions(-) delete mode 100755 src/Exception/General.php delete mode 100755 src/Exception/Pass.php delete mode 100755 src/Exception/Stop.php diff --git a/src/Exception/General.php b/src/Exception/General.php deleted file mode 100755 index 082cd61..0000000 --- a/src/Exception/General.php +++ /dev/null @@ -1,232 +0,0 @@ -response = new Response(); - $this->handleException($throwable); - } - - /** - * Configure exception handler - */ - public function configure($config) - { - $configuration = array_merge($this->config, $config); - $this->config = $configuration; - } - - /** - * Handles an exception - */ - protected function handleException($throwable) - { - $this->response->throwErr($throwable); - } - - /** - * Convert errors into ErrorException objects - * - * This method catches PHP errors and converts them into \ErrorException objects; - * these \ErrorException objects are then thrown and caught by Leaf's - * built-in or custom error handlers. - * - * @param int $errno The numeric type of the Error - * @param string $errstr The error message - * @param string $errfile The absolute path to the affected file - * @param int $errline The line number of the error in the affected file - * @return bool - * @throws \ErrorException - */ - public static function handleErrors($errno, $errstr = '', $errfile = '', $errline = '') - { - if (!($errno & error_reporting())) { - return; - } - - try { - throw new \ErrorException($errstr, $errno, 0, $errfile, $errline); - } catch (\Throwable $th) { - $app = \Leaf\Config::get("app")["instance"]; - - if ($app && $app->config("log.enabled")) { - $app->logger()->error($th); - } - - exit(static::renderBody($th)); - } - } - - /** - * Returns ErrorException objects from errors - * - * This method catches PHP errors and converts them into \ErrorException objects; - * these \ErrorException objects are then thrown and caught by Leaf's - * built-in or custom error handlers. - * - * @param int $errno The numeric type of the Error - * @param string $errstr The error message - * @param string $errfile The absolute path to the affected file - * @param int $errline The line number of the error in the affected file - * @return void|\ErrorException - */ - public static function toException($errno, $errstr = '', $errfile = '', $errline = '') - { - if (!($errno & error_reporting())) { - return; - } - - try { - throw new \ErrorException($errstr, $errno, 0, $errfile, $errline); - } catch (\Throwable $th) { - return $th; - } - } - - /** - * Render response body - * - * @param array $env - * @param \Exception $exception - * - * @return string - */ - protected static function renderBody($exception) - { - $title = static::$config['ERROR_TITLE'] ?? 'Leaf Application Error'; - $code = $exception->getCode(); - $message = htmlspecialchars($exception->getMessage()); - $file = $exception->getFile(); - $line = $exception->getLine(); - - $trace = str_replace( - ['#', "\n"], - ['
#', '
'], - htmlspecialchars($exception->getTraceAsString()) - ); - $trace = str_replace(['): ', ''], ['): ', ''], $trace); - $body = "

$title

"; - $body .= '

The application could not run because of the following error:

'; - $body .= '

Details

'; - $body .= sprintf('
Type: %s
', get_class($exception)); - - if ($code) { - $body .= "
Code: $code
"; - } - - if ($message) { - $body .= "
Message: $message
"; - } - - if ($file) { - $body .= "
File: $file
"; - } - - if ($line) { - $body .= "
Line: $line
"; - } - - if ($trace) { - $body .= '

Trace

'; - $body .= "
$trace
"; - } - - return static::exceptionMarkup($title, $body); - } - - /** - * Generate diagnostic template markup - * - * This method accepts a title and body content to generate an HTML document layout. - * - * @param string $title The title of the HTML template - * @param string $body The body content of the HTML template - * @return string - */ - protected static function errorMarkup($title, $body) - { - return "$title

$title

$body
"; - } - - /** - * Generate diagnostic template markup - * - * This method accepts a title and body content to generate an HTML document layout. - * - * @param string $title The title of the HTML template - * @param string $body The body content of the HTML template - * @return string - */ - protected static function exceptionMarkup($title, $body) - { - return "$title$body"; - } - - /** - * Default Not Found handler - */ - public static function defaultDown() - { - echo static::errorMarkup( - 'Oops!', - '

App is under maintainance, please check back soon.

' - ); - } - - /** - * Default Not Found handler - */ - public static function default404() - { - echo static::errorMarkup( - '404', - '

The page you are looking for could not be found.

' - ); - } - - /** - * CSRF error - */ - public static function csrf($error = null) - { - echo static::errorMarkup( - 'Invalid request', - "

$error

" ?? '

The page you are looking for has expired.

' - ); - } - - /** - * Default Error handler - */ - public static function defaultError($e = null) - { - if ($e) { - $app = \Leaf\Config::get("app")["instance"]; - - if ($app && $app->config("log.enabled")) { - $app->logger()->error($e); - } - } - - echo self::errorMarkup('Oops!', '

A website error has occurred, our team has been notified.

'); - } -} diff --git a/src/Exception/Pass.php b/src/Exception/Pass.php deleted file mode 100755 index 3c5f4c6..0000000 --- a/src/Exception/Pass.php +++ /dev/null @@ -1,21 +0,0 @@ - Date: Wed, 26 Jan 2022 17:12:32 +0000 Subject: [PATCH 07/14] =?UTF-8?q?=E2=9C=A8=20switched=20to=20leaf=20except?= =?UTF-8?q?ions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.php | 758 +++++++++++++++++++++++++--------------------------- 1 file changed, 370 insertions(+), 388 deletions(-) diff --git a/src/App.php b/src/App.php index fd60e1a..23b24dc 100755 --- a/src/App.php +++ b/src/App.php @@ -17,392 +17,374 @@ */ class App extends Router { - /** - * Leaf container instance - * @var \Leaf\Helpers\Container - */ - protected $container; - - /** - * Callable to be invoked on application error - */ - protected $errorHandler; - - /******************************************************************************** - * Instantiation and Configuration - *******************************************************************************/ - - /** - * Constructor - * @param array $userSettings Associative array of application settings - */ - public function __construct(array $userSettings = []) - { - $this->setupErrorHandler(); - - if (count($userSettings) > 0) { - Config::set($userSettings); - } - - if (class_exists('\Leaf\Anchor\CSRF')) { - if (!Anchor\CSRF::token()) { - Anchor\CSRF::init(); - } - - if (!Anchor\CSRF::verify()) { - $csrfError = Anchor\CSRF::errors()['token']; - Http\Response::status(400); - echo Exception\General::csrf($csrfError); - exit(); - } - } - - $this->container = new \Leaf\Helpers\Container(); - - $this->setupDefaultContainer(); - - if (class_exists('\Leaf\BareUI')) { - View::attach(\Leaf\BareUI::class, 'template'); - } - - $this->loadViewEngines(); - } - - protected function setupErrorHandler() - { - if ($this->config('debug')) { - $debugConfig = [E_ALL, 1, ['\Leaf\Exception\General', 'handleErrors'], false]; - } else { - $debugConfig = [0, 0, ['\Leaf\Exception\General', 'defaultError'], true]; - } - - error_reporting($debugConfig[0]); - ini_set('display_errors', (string) $debugConfig[1]); - - $this->setErrorHandler($debugConfig[2], $debugConfig[3]); - } - - /** - * Set a custom error screen. - * - * @param callable|array $handler The function to be executed - */ - public function setErrorHandler($handler, bool $wrapper = true) - { - $errorHandler = $handler; - - if ($wrapper) { - $errorHandler = function ($errno, $errstr = '', $errfile = '', $errline = '') use ($handler) { - $exception = Exception\General::toException($errno, $errstr, $errfile, $errline); - Http\Response::status(500); - call_user_func_array($handler, [$exception]); - exit(); - }; - } - - set_error_handler($errorHandler); - } - - /** - * This method adds a method to the global leaf instance - * Register a method and use it globally on the Leaf Object - */ - public function register($name, $value) - { - $this->container->singleton($name, $value); - } - - public function loadViewEngines() - { - $views = View::$engines; - - if (count($views) > 0) { - foreach ($views as $key => $value) { - $this->container->singleton($key, function () use ($value) { - return $value; - }); - } - } - } - - private function setupDefaultContainer() - { - // Default request - $this->container->singleton('request', function () { - return new \Leaf\Http\Request(); - }); - - // Default response - $this->container->singleton('response', function () { - return new \Leaf\Http\Response(); - }); - - // Default headers - $this->container->singleton('headers', function () { - return new \Leaf\Http\Headers(); - }); - - if ($this->config('log.enabled')) { - if (class_exists('Leaf\Log')) { - // Default log writer - $this->container->singleton('logWriter', function ($c) { - $logWriter = Config::get('log.writer'); - - $file = $this->config('log.dir') . $this->config('log.file'); - - return is_object($logWriter) ? $logWriter : new \Leaf\LogWriter($file, $this->config('log.open') ?? true); - }); - - // Default log - $this->container->singleton('log', function ($c) { - $log = new \Leaf\Log($c['logWriter']); - $log->enabled($this->config('log.enabled')); - $log->level($this->config('log.level')); - - return $log; - }); - } - } - - // Default mode - (function () { - $mode = $this->config('mode'); - - if (_env('APP_ENV')) { - $mode = _env('APP_ENV'); - } - - if (_env('LEAF_MODE')) { - $mode = _env('LEAF_MODE'); - } - - if (isset($_ENV['LEAF_MODE'])) { - $mode = $_ENV['LEAF_MODE']; - } else { - $envMode = getenv('LEAF_MODE'); - - if ($envMode !== false) { - $mode = $envMode; - } - } - - $this->config('mode', $mode); - })(); - - Config::set('app', [ - 'instance' => $this, - 'container' => $this->container, - ]); - } - - public function __get($name) - { - return $this->container->get($name); - } - - public function __set($name, $value) - { - $this->container->set($name, $value); - } - - public function __isset($name) - { - return $this->container->has($name); - } - - public function __unset($name) - { - $this->container->remove($name); - } - - /** - * Configure Leaf Settings - * - * This method defines application settings and acts as a setter and a getter. - * - * If only one argument is specified and that argument is a string, the value - * of the setting identified by the first argument will be returned, or NULL if - * that setting does not exist. - * - * If only one argument is specified and that argument is an associative array, - * the array will be merged into the existing application settings. - * - * If two arguments are provided, the first argument is the name of the setting - * to be created or updated, and the second argument is the setting value. - * - * @param string|array $name If a string, the name of the setting to set or retrieve. Else an associated array of setting names and values - * @param mixed $value If name is a string, the value of the setting identified by $name - * @return mixed The value of a setting if only one argument is a string - */ - public function config($name, $value = null) - { - if ($value === null && is_string($name)) { - return Config::get($name); - } - - Config::set($name, $value); - } - - /******************************************************************************** - * Logging - *******************************************************************************/ - - /** - * Get application log - * - * @return \Leaf\Log|null|void - */ - public function logger() - { - if (!$this->log) { - trigger_error('You need to enable logging to use this feature! Set log.enabled to true and install the logger module'); - } - - return $this->log; - } - - /******************************************************************************** - * Application Accessors - *******************************************************************************/ - - /** - * Get the Request Headers - * @return \Leaf\Http\Headers - */ - public function headers() - { - return $this->headers; - } - - /** - * Get the Request object - * @return \Leaf\Http\Request - */ - public function request() - { - return $this->request; - } - - /** - * Get the Response object - * @return \Leaf\Http\Response - */ - public function response() - { - return $this->response; - } - - - /** - * Create mode-specific code - * - * @param string $mode The mode to run code in - * @param callable $callback The code to run in selected mode. - */ - public static function script($mode, $callback) - { - static::hook('router.before', function () use ($mode, $callback) { - $appMode = Config::get('mode') ?? 'development'; - - if ($mode === $appMode) { - return $callback(); - } - }); - } - - /******************************************************************************** - * Helper Methods - *******************************************************************************/ - - /** - * Get the absolute path to this Leaf application's root directory - * - * This method returns the absolute path to the Leaf application's - * directory. If the Leaf application is installed in a public-accessible - * sub-directory, the sub-directory path will be included. This method - * will always return an absolute path WITH a trailing slash. - * - * @return string - */ - public function root() - { - return rtrim($_SERVER['DOCUMENT_ROOT'], '/') . rtrim($this->request->getRootUri(), '/') . '/'; - } - - /** - * Clean current output buffer - */ - protected function cleanBuffer() - { - if (ob_get_level() !== 0) { - ob_clean(); - } - } - - /** - * Halt - * - * Stop the application and immediately send the response with a - * specific status and body to the HTTP client. This may send any - * type of response: info, success, redirect, client error, or server error. - * - * @param int $status The HTTP response status - * @param string $message The HTTP response body - */ - public static function halt($status, $message = '') - { - if (ob_get_level() !== 0) { - ob_clean(); - } - - Http\Headers::status($status); - Http\Response::markup($message); - - exit(); - } - - /** - * Stop - * - * The thrown exception will be caught in application's `call()` method - * and the response will be sent as is to the HTTP client. - * - * @throws \Leaf\Exception\Stop - */ - public function stop() - { - throw new \Leaf\Exception\Stop(); - } - - /** - * Pass - * - * The thrown exception is caught in the application's `call()` method causing - * the router's current iteration to stop and continue to the subsequent route if available. - * If no subsequent matching routes are found, a 404 response will be sent to the client. - * - * @throws \Leaf\Exception\Pass - */ - public function pass() - { - $this->cleanBuffer(); - - throw new \Leaf\Exception\Pass(); - } - - /** - * Evade CORS errors - * - * Cors handler - * - * @param $options Config for cors - */ - public function cors($options = []) - { - if (class_exists('Leaf\Http\Cors')) { - Http\Cors::config($options); - } else { - trigger_error('Cors module not found! Run `composer require leafs/cors` to install the CORS module. This is required to configure CORS.'); - } - } + /** + * Leaf container instance + * @var \Leaf\Helpers\Container + */ + protected $container; + + /** + * Callable to be invoked on application error + */ + protected $errorHandler; + + /******************************************************************************** + * Instantiation and Configuration + *******************************************************************************/ + + /** + * Constructor + * @param array $userSettings Associative array of application settings + */ + public function __construct(array $userSettings = []) + { + $this->setupErrorHandler(); + + if (count($userSettings) > 0) { + Config::set($userSettings); + } + + if (class_exists('\Leaf\Anchor\CSRF')) { + if (!Anchor\CSRF::token()) { + Anchor\CSRF::init(); + } + + if (!Anchor\CSRF::verify()) { + $csrfError = Anchor\CSRF::errors()['token']; + Http\Response::status(400); + echo Exception\General::csrf($csrfError); + exit(); + } + } + + $this->container = new \Leaf\Helpers\Container(); + + $this->setupDefaultContainer(); + + if (class_exists('\Leaf\BareUI')) { + View::attach(\Leaf\BareUI::class, 'template'); + } + + $this->loadViewEngines(); + } + + protected function setupErrorHandler() + { + if ($this->config('debug') === true) { + $debugConfig = [E_ALL, 1]; + $this->errorHandler = (new \Leaf\Exception\Run); + $this->errorHandler->register(); + } else { + $debugConfig = [0, 0]; + $this->setErrorHandler(['\Leaf\Exception\General', 'defaultError'], true); + } + + error_reporting($debugConfig[0]); + ini_set('display_errors', (string) $debugConfig[1]); + } + + /** + * Set a custom error screen. + * + * @param callable|array $handler The function to be executed + */ + public function setErrorHandler($handler, bool $wrapper = true) + { + $errorHandler = $handler; + + if ($this->errorHandler instanceof \Leaf\Exception\Run) { + $this->errorHandler->unregister(); + } + + if ($handler instanceof \Leaf\Exception\Handler\Handler) { + $this->errorHandler = new \Leaf\Exception\Run; + $this->errorHandler->pushHandler($handler)->register(); + } + + if ($wrapper) { + $errorHandler = function ($errno, $errstr = '', $errfile = '', $errline = '') use ($handler) { + $exception = Exception\General::toException($errno, $errstr, $errfile, $errline); + Http\Response::status(500); + call_user_func_array($handler, [$exception]); + exit(); + }; + } + + set_error_handler($errorHandler); + } + + /** + * This method adds a method to the global leaf instance + * Register a method and use it globally on the Leaf Object + */ + public function register($name, $value) + { + $this->container->singleton($name, $value); + } + + public function loadViewEngines() + { + $views = View::$engines; + + if (count($views) > 0) { + foreach ($views as $key => $value) { + $this->container->singleton($key, function () use ($value) { + return $value; + }); + } + } + } + + private function setupDefaultContainer() + { + // Default request + $this->container->singleton('request', function () { + return new \Leaf\Http\Request(); + }); + + // Default response + $this->container->singleton('response', function () { + return new \Leaf\Http\Response(); + }); + + // Default headers + $this->container->singleton('headers', function () { + return new \Leaf\Http\Headers(); + }); + + if ($this->config('log.enabled')) { + if (class_exists('Leaf\Log')) { + // Default log writer + $this->container->singleton('logWriter', function ($c) { + $logWriter = Config::get('log.writer'); + + $file = $this->config('log.dir') . $this->config('log.file'); + + return is_object($logWriter) ? $logWriter : new \Leaf\LogWriter($file, $this->config('log.open') ?? true); + }); + + // Default log + $this->container->singleton('log', function ($c) { + $log = new \Leaf\Log($c['logWriter']); + $log->enabled($this->config('log.enabled')); + $log->level($this->config('log.level')); + + return $log; + }); + } + } + + // Default mode + (function () { + $mode = $this->config('mode'); + + if (_env('APP_ENV')) { + $mode = _env('APP_ENV'); + } + + if (_env('LEAF_MODE')) { + $mode = _env('LEAF_MODE'); + } + + if (isset($_ENV['LEAF_MODE'])) { + $mode = $_ENV['LEAF_MODE']; + } else { + $envMode = getenv('LEAF_MODE'); + + if ($envMode !== false) { + $mode = $envMode; + } + } + + $this->config('mode', $mode); + })(); + + Config::set('app', [ + 'instance' => $this, + 'container' => $this->container, + ]); + } + + public function __get($name) + { + return $this->container->get($name); + } + + public function __set($name, $value) + { + $this->container->set($name, $value); + } + + public function __isset($name) + { + return $this->container->has($name); + } + + public function __unset($name) + { + $this->container->remove($name); + } + + /** + * Configure Leaf Settings + * + * This method defines application settings and acts as a setter and a getter. + * + * If only one argument is specified and that argument is a string, the value + * of the setting identified by the first argument will be returned, or NULL if + * that setting does not exist. + * + * If only one argument is specified and that argument is an associative array, + * the array will be merged into the existing application settings. + * + * If two arguments are provided, the first argument is the name of the setting + * to be created or updated, and the second argument is the setting value. + * + * @param string|array $name If a string, the name of the setting to set or retrieve. Else an associated array of setting names and values + * @param mixed $value If name is a string, the value of the setting identified by $name + * @return mixed The value of a setting if only one argument is a string + */ + public function config($name, $value = null) + { + if ($value === null && is_string($name)) { + return Config::get($name); + } + + Config::set($name, $value); + $this->setupErrorHandler(); + } + + /******************************************************************************** + * Logging + *******************************************************************************/ + + /** + * Get application log + * + * @return \Leaf\Log|null|void + */ + public function logger() + { + if (!$this->log) { + trigger_error('You need to enable logging to use this feature! Set log.enabled to true and install the logger module'); + } + + return $this->log; + } + + /******************************************************************************** + * Application Accessors + *******************************************************************************/ + + /** + * Get the Request Headers + * @return \Leaf\Http\Headers + */ + public function headers() + { + return $this->headers; + } + + /** + * Get the Request object + * @return \Leaf\Http\Request + */ + public function request() + { + return $this->request; + } + + /** + * Get the Response object + * @return \Leaf\Http\Response + */ + public function response() + { + return $this->response; + } + + + /** + * Create mode-specific code + * + * @param string $mode The mode to run code in + * @param callable $callback The code to run in selected mode. + */ + public static function script($mode, $callback) + { + static::hook('router.before', function () use ($mode, $callback) { + $appMode = Config::get('mode') ?? 'development'; + + if ($mode === $appMode) { + return $callback(); + } + }); + } + + /******************************************************************************** + * Helper Methods + *******************************************************************************/ + + /** + * Get the absolute path to this Leaf application's root directory + * + * This method returns the absolute path to the Leaf application's + * directory. If the Leaf application is installed in a public-accessible + * sub-directory, the sub-directory path will be included. This method + * will always return an absolute path WITH a trailing slash. + * + * @return string + */ + public function root() + { + return rtrim($_SERVER['DOCUMENT_ROOT'], '/') . rtrim($this->request->getRootUri(), '/') . '/'; + } + + /** + * Clean current output buffer + */ + protected function cleanBuffer() + { + if (ob_get_level() !== 0) { + ob_clean(); + } + } + + /** + * Halt + * + * Stop the application and immediately send the response with a + * specific status and body to the HTTP client. This may send any + * type of response: info, success, redirect, client error, or server error. + * + * @param int $status The HTTP response status + * @param string $message The HTTP response body + */ + public static function halt($status, $message = '') + { + if (ob_get_level() !== 0) { + ob_clean(); + } + + Http\Headers::status($status); + Http\Response::markup($message); + + exit(); + } + + /** + * Evade CORS errors + * + * Cors handler + * + * @param $options Config for cors + */ + public function cors($options = []) + { + if (class_exists('Leaf\Http\Cors')) { + Http\Cors::config($options); + } else { + trigger_error('Cors module not found! Run `composer require leafs/cors` to install the CORS module. This is required to configure CORS.'); + } + } } From 5187992fb99c7227e4575b0548c44c52138330bd Mon Sep 17 00:00:00 2001 From: Michael Darko Date: Wed, 26 Jan 2022 17:12:54 +0000 Subject: [PATCH 08/14] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20upgraded=20dependenc?= =?UTF-8?q?ies?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- composer.json | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/composer.json b/composer.json index da13f49..096c483 100644 --- a/composer.json +++ b/composer.json @@ -31,9 +31,10 @@ "minimum-stability": "dev", "prefer-stable": true, "require": { - "leafs/http": "^1.1", - "leafs/router": "^0.1.4", - "leafs/anchor": "^1.1" + "leafs/http": "^1.2", + "leafs/router": "^0.1.5", + "leafs/anchor": "^1.2", + "leafs/exception": "dev-master" }, "require-dev": { "pestphp/pest": "^1.21", @@ -42,5 +43,10 @@ "scripts": { "format": "vendor/bin/php-cs-fixer fix --config=.php_cs.dist.php --allow-risky=yes", "test": "vendor/bin/pest" - } -} \ No newline at end of file + }, + "config": { + "allow-plugins": { + "pestphp/pest-plugin": true + } + } +} From 3f88dbfb43e45dd58511479fcafa8d97dd865277 Mon Sep 17 00:00:00 2001 From: mychidarko Date: Wed, 26 Jan 2022 17:13:23 +0000 Subject: [PATCH 09/14] Fix styling --- src/App.php | 740 ++++++++++++++++++++++++------------------------- src/Config.php | 6 +- 2 files changed, 373 insertions(+), 373 deletions(-) diff --git a/src/App.php b/src/App.php index 23b24dc..26c87e3 100755 --- a/src/App.php +++ b/src/App.php @@ -17,374 +17,374 @@ */ class App extends Router { - /** - * Leaf container instance - * @var \Leaf\Helpers\Container - */ - protected $container; - - /** - * Callable to be invoked on application error - */ - protected $errorHandler; - - /******************************************************************************** - * Instantiation and Configuration - *******************************************************************************/ - - /** - * Constructor - * @param array $userSettings Associative array of application settings - */ - public function __construct(array $userSettings = []) - { - $this->setupErrorHandler(); - - if (count($userSettings) > 0) { - Config::set($userSettings); - } - - if (class_exists('\Leaf\Anchor\CSRF')) { - if (!Anchor\CSRF::token()) { - Anchor\CSRF::init(); - } - - if (!Anchor\CSRF::verify()) { - $csrfError = Anchor\CSRF::errors()['token']; - Http\Response::status(400); - echo Exception\General::csrf($csrfError); - exit(); - } - } - - $this->container = new \Leaf\Helpers\Container(); - - $this->setupDefaultContainer(); - - if (class_exists('\Leaf\BareUI')) { - View::attach(\Leaf\BareUI::class, 'template'); - } - - $this->loadViewEngines(); - } - - protected function setupErrorHandler() - { - if ($this->config('debug') === true) { - $debugConfig = [E_ALL, 1]; - $this->errorHandler = (new \Leaf\Exception\Run); - $this->errorHandler->register(); - } else { - $debugConfig = [0, 0]; - $this->setErrorHandler(['\Leaf\Exception\General', 'defaultError'], true); - } - - error_reporting($debugConfig[0]); - ini_set('display_errors', (string) $debugConfig[1]); - } - - /** - * Set a custom error screen. - * - * @param callable|array $handler The function to be executed - */ - public function setErrorHandler($handler, bool $wrapper = true) - { - $errorHandler = $handler; - - if ($this->errorHandler instanceof \Leaf\Exception\Run) { - $this->errorHandler->unregister(); - } - - if ($handler instanceof \Leaf\Exception\Handler\Handler) { - $this->errorHandler = new \Leaf\Exception\Run; - $this->errorHandler->pushHandler($handler)->register(); - } - - if ($wrapper) { - $errorHandler = function ($errno, $errstr = '', $errfile = '', $errline = '') use ($handler) { - $exception = Exception\General::toException($errno, $errstr, $errfile, $errline); - Http\Response::status(500); - call_user_func_array($handler, [$exception]); - exit(); - }; - } - - set_error_handler($errorHandler); - } - - /** - * This method adds a method to the global leaf instance - * Register a method and use it globally on the Leaf Object - */ - public function register($name, $value) - { - $this->container->singleton($name, $value); - } - - public function loadViewEngines() - { - $views = View::$engines; - - if (count($views) > 0) { - foreach ($views as $key => $value) { - $this->container->singleton($key, function () use ($value) { - return $value; - }); - } - } - } - - private function setupDefaultContainer() - { - // Default request - $this->container->singleton('request', function () { - return new \Leaf\Http\Request(); - }); - - // Default response - $this->container->singleton('response', function () { - return new \Leaf\Http\Response(); - }); - - // Default headers - $this->container->singleton('headers', function () { - return new \Leaf\Http\Headers(); - }); - - if ($this->config('log.enabled')) { - if (class_exists('Leaf\Log')) { - // Default log writer - $this->container->singleton('logWriter', function ($c) { - $logWriter = Config::get('log.writer'); - - $file = $this->config('log.dir') . $this->config('log.file'); - - return is_object($logWriter) ? $logWriter : new \Leaf\LogWriter($file, $this->config('log.open') ?? true); - }); - - // Default log - $this->container->singleton('log', function ($c) { - $log = new \Leaf\Log($c['logWriter']); - $log->enabled($this->config('log.enabled')); - $log->level($this->config('log.level')); - - return $log; - }); - } - } - - // Default mode - (function () { - $mode = $this->config('mode'); - - if (_env('APP_ENV')) { - $mode = _env('APP_ENV'); - } - - if (_env('LEAF_MODE')) { - $mode = _env('LEAF_MODE'); - } - - if (isset($_ENV['LEAF_MODE'])) { - $mode = $_ENV['LEAF_MODE']; - } else { - $envMode = getenv('LEAF_MODE'); - - if ($envMode !== false) { - $mode = $envMode; - } - } - - $this->config('mode', $mode); - })(); - - Config::set('app', [ - 'instance' => $this, - 'container' => $this->container, - ]); - } - - public function __get($name) - { - return $this->container->get($name); - } - - public function __set($name, $value) - { - $this->container->set($name, $value); - } - - public function __isset($name) - { - return $this->container->has($name); - } - - public function __unset($name) - { - $this->container->remove($name); - } - - /** - * Configure Leaf Settings - * - * This method defines application settings and acts as a setter and a getter. - * - * If only one argument is specified and that argument is a string, the value - * of the setting identified by the first argument will be returned, or NULL if - * that setting does not exist. - * - * If only one argument is specified and that argument is an associative array, - * the array will be merged into the existing application settings. - * - * If two arguments are provided, the first argument is the name of the setting - * to be created or updated, and the second argument is the setting value. - * - * @param string|array $name If a string, the name of the setting to set or retrieve. Else an associated array of setting names and values - * @param mixed $value If name is a string, the value of the setting identified by $name - * @return mixed The value of a setting if only one argument is a string - */ - public function config($name, $value = null) - { - if ($value === null && is_string($name)) { - return Config::get($name); - } - - Config::set($name, $value); - $this->setupErrorHandler(); - } - - /******************************************************************************** - * Logging - *******************************************************************************/ - - /** - * Get application log - * - * @return \Leaf\Log|null|void - */ - public function logger() - { - if (!$this->log) { - trigger_error('You need to enable logging to use this feature! Set log.enabled to true and install the logger module'); - } - - return $this->log; - } - - /******************************************************************************** - * Application Accessors - *******************************************************************************/ - - /** - * Get the Request Headers - * @return \Leaf\Http\Headers - */ - public function headers() - { - return $this->headers; - } - - /** - * Get the Request object - * @return \Leaf\Http\Request - */ - public function request() - { - return $this->request; - } - - /** - * Get the Response object - * @return \Leaf\Http\Response - */ - public function response() - { - return $this->response; - } - - - /** - * Create mode-specific code - * - * @param string $mode The mode to run code in - * @param callable $callback The code to run in selected mode. - */ - public static function script($mode, $callback) - { - static::hook('router.before', function () use ($mode, $callback) { - $appMode = Config::get('mode') ?? 'development'; - - if ($mode === $appMode) { - return $callback(); - } - }); - } - - /******************************************************************************** - * Helper Methods - *******************************************************************************/ - - /** - * Get the absolute path to this Leaf application's root directory - * - * This method returns the absolute path to the Leaf application's - * directory. If the Leaf application is installed in a public-accessible - * sub-directory, the sub-directory path will be included. This method - * will always return an absolute path WITH a trailing slash. - * - * @return string - */ - public function root() - { - return rtrim($_SERVER['DOCUMENT_ROOT'], '/') . rtrim($this->request->getRootUri(), '/') . '/'; - } - - /** - * Clean current output buffer - */ - protected function cleanBuffer() - { - if (ob_get_level() !== 0) { - ob_clean(); - } - } - - /** - * Halt - * - * Stop the application and immediately send the response with a - * specific status and body to the HTTP client. This may send any - * type of response: info, success, redirect, client error, or server error. - * - * @param int $status The HTTP response status - * @param string $message The HTTP response body - */ - public static function halt($status, $message = '') - { - if (ob_get_level() !== 0) { - ob_clean(); - } - - Http\Headers::status($status); - Http\Response::markup($message); - - exit(); - } - - /** - * Evade CORS errors - * - * Cors handler - * - * @param $options Config for cors - */ - public function cors($options = []) - { - if (class_exists('Leaf\Http\Cors')) { - Http\Cors::config($options); - } else { - trigger_error('Cors module not found! Run `composer require leafs/cors` to install the CORS module. This is required to configure CORS.'); - } - } + /** + * Leaf container instance + * @var \Leaf\Helpers\Container + */ + protected $container; + + /** + * Callable to be invoked on application error + */ + protected $errorHandler; + + /******************************************************************************** + * Instantiation and Configuration + *******************************************************************************/ + + /** + * Constructor + * @param array $userSettings Associative array of application settings + */ + public function __construct(array $userSettings = []) + { + $this->setupErrorHandler(); + + if (count($userSettings) > 0) { + Config::set($userSettings); + } + + if (class_exists('\Leaf\Anchor\CSRF')) { + if (!Anchor\CSRF::token()) { + Anchor\CSRF::init(); + } + + if (!Anchor\CSRF::verify()) { + $csrfError = Anchor\CSRF::errors()['token']; + Http\Response::status(400); + echo Exception\General::csrf($csrfError); + exit(); + } + } + + $this->container = new \Leaf\Helpers\Container(); + + $this->setupDefaultContainer(); + + if (class_exists('\Leaf\BareUI')) { + View::attach(\Leaf\BareUI::class, 'template'); + } + + $this->loadViewEngines(); + } + + protected function setupErrorHandler() + { + if ($this->config('debug') === true) { + $debugConfig = [E_ALL, 1]; + $this->errorHandler = (new \Leaf\Exception\Run()); + $this->errorHandler->register(); + } else { + $debugConfig = [0, 0]; + $this->setErrorHandler(['\Leaf\Exception\General', 'defaultError'], true); + } + + error_reporting($debugConfig[0]); + ini_set('display_errors', (string) $debugConfig[1]); + } + + /** + * Set a custom error screen. + * + * @param callable|array $handler The function to be executed + */ + public function setErrorHandler($handler, bool $wrapper = true) + { + $errorHandler = $handler; + + if ($this->errorHandler instanceof \Leaf\Exception\Run) { + $this->errorHandler->unregister(); + } + + if ($handler instanceof \Leaf\Exception\Handler\Handler) { + $this->errorHandler = new \Leaf\Exception\Run(); + $this->errorHandler->pushHandler($handler)->register(); + } + + if ($wrapper) { + $errorHandler = function ($errno, $errstr = '', $errfile = '', $errline = '') use ($handler) { + $exception = Exception\General::toException($errno, $errstr, $errfile, $errline); + Http\Response::status(500); + call_user_func_array($handler, [$exception]); + exit(); + }; + } + + set_error_handler($errorHandler); + } + + /** + * This method adds a method to the global leaf instance + * Register a method and use it globally on the Leaf Object + */ + public function register($name, $value) + { + $this->container->singleton($name, $value); + } + + public function loadViewEngines() + { + $views = View::$engines; + + if (count($views) > 0) { + foreach ($views as $key => $value) { + $this->container->singleton($key, function () use ($value) { + return $value; + }); + } + } + } + + private function setupDefaultContainer() + { + // Default request + $this->container->singleton('request', function () { + return new \Leaf\Http\Request(); + }); + + // Default response + $this->container->singleton('response', function () { + return new \Leaf\Http\Response(); + }); + + // Default headers + $this->container->singleton('headers', function () { + return new \Leaf\Http\Headers(); + }); + + if ($this->config('log.enabled')) { + if (class_exists('Leaf\Log')) { + // Default log writer + $this->container->singleton('logWriter', function ($c) { + $logWriter = Config::get('log.writer'); + + $file = $this->config('log.dir') . $this->config('log.file'); + + return is_object($logWriter) ? $logWriter : new \Leaf\LogWriter($file, $this->config('log.open') ?? true); + }); + + // Default log + $this->container->singleton('log', function ($c) { + $log = new \Leaf\Log($c['logWriter']); + $log->enabled($this->config('log.enabled')); + $log->level($this->config('log.level')); + + return $log; + }); + } + } + + // Default mode + (function () { + $mode = $this->config('mode'); + + if (_env('APP_ENV')) { + $mode = _env('APP_ENV'); + } + + if (_env('LEAF_MODE')) { + $mode = _env('LEAF_MODE'); + } + + if (isset($_ENV['LEAF_MODE'])) { + $mode = $_ENV['LEAF_MODE']; + } else { + $envMode = getenv('LEAF_MODE'); + + if ($envMode !== false) { + $mode = $envMode; + } + } + + $this->config('mode', $mode); + })(); + + Config::set('app', [ + 'instance' => $this, + 'container' => $this->container, + ]); + } + + public function __get($name) + { + return $this->container->get($name); + } + + public function __set($name, $value) + { + $this->container->set($name, $value); + } + + public function __isset($name) + { + return $this->container->has($name); + } + + public function __unset($name) + { + $this->container->remove($name); + } + + /** + * Configure Leaf Settings + * + * This method defines application settings and acts as a setter and a getter. + * + * If only one argument is specified and that argument is a string, the value + * of the setting identified by the first argument will be returned, or NULL if + * that setting does not exist. + * + * If only one argument is specified and that argument is an associative array, + * the array will be merged into the existing application settings. + * + * If two arguments are provided, the first argument is the name of the setting + * to be created or updated, and the second argument is the setting value. + * + * @param string|array $name If a string, the name of the setting to set or retrieve. Else an associated array of setting names and values + * @param mixed $value If name is a string, the value of the setting identified by $name + * @return mixed The value of a setting if only one argument is a string + */ + public function config($name, $value = null) + { + if ($value === null && is_string($name)) { + return Config::get($name); + } + + Config::set($name, $value); + $this->setupErrorHandler(); + } + + /******************************************************************************** + * Logging + *******************************************************************************/ + + /** + * Get application log + * + * @return \Leaf\Log|null|void + */ + public function logger() + { + if (!$this->log) { + trigger_error('You need to enable logging to use this feature! Set log.enabled to true and install the logger module'); + } + + return $this->log; + } + + /******************************************************************************** + * Application Accessors + *******************************************************************************/ + + /** + * Get the Request Headers + * @return \Leaf\Http\Headers + */ + public function headers() + { + return $this->headers; + } + + /** + * Get the Request object + * @return \Leaf\Http\Request + */ + public function request() + { + return $this->request; + } + + /** + * Get the Response object + * @return \Leaf\Http\Response + */ + public function response() + { + return $this->response; + } + + + /** + * Create mode-specific code + * + * @param string $mode The mode to run code in + * @param callable $callback The code to run in selected mode. + */ + public static function script($mode, $callback) + { + static::hook('router.before', function () use ($mode, $callback) { + $appMode = Config::get('mode') ?? 'development'; + + if ($mode === $appMode) { + return $callback(); + } + }); + } + + /******************************************************************************** + * Helper Methods + *******************************************************************************/ + + /** + * Get the absolute path to this Leaf application's root directory + * + * This method returns the absolute path to the Leaf application's + * directory. If the Leaf application is installed in a public-accessible + * sub-directory, the sub-directory path will be included. This method + * will always return an absolute path WITH a trailing slash. + * + * @return string + */ + public function root() + { + return rtrim($_SERVER['DOCUMENT_ROOT'], '/') . rtrim($this->request->getRootUri(), '/') . '/'; + } + + /** + * Clean current output buffer + */ + protected function cleanBuffer() + { + if (ob_get_level() !== 0) { + ob_clean(); + } + } + + /** + * Halt + * + * Stop the application and immediately send the response with a + * specific status and body to the HTTP client. This may send any + * type of response: info, success, redirect, client error, or server error. + * + * @param int $status The HTTP response status + * @param string $message The HTTP response body + */ + public static function halt($status, $message = '') + { + if (ob_get_level() !== 0) { + ob_clean(); + } + + Http\Headers::status($status); + Http\Response::markup($message); + + exit(); + } + + /** + * Evade CORS errors + * + * Cors handler + * + * @param $options Config for cors + */ + public function cors($options = []) + { + if (class_exists('Leaf\Http\Cors')) { + Http\Cors::config($options); + } else { + trigger_error('Cors module not found! Run `composer require leafs/cors` to install the CORS module. This is required to configure CORS.'); + } + } } diff --git a/src/Config.php b/src/Config.php index 82bcad2..ee3bf91 100644 --- a/src/Config.php +++ b/src/Config.php @@ -78,9 +78,9 @@ public static function get($item = null) if ($item) { $items = explode('.', $item); - if (count($items) > 1) { - return static::$settings[$items[0]][$items[1]] ?? null; - } + if (count($items) > 1) { + return static::$settings[$items[0]][$items[1]] ?? null; + } return static::$settings[$item] ?? null; } From c9ed0e12dc8846012c5e4c55cc44ab5fd5ff3ee7 Mon Sep 17 00:00:00 2001 From: Michael Darko Date: Thu, 27 Jan 2022 21:45:36 +0000 Subject: [PATCH 10/14] :memo: updated year --- src/App.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/App.php b/src/App.php index 26c87e3..fe43189 100755 --- a/src/App.php +++ b/src/App.php @@ -10,7 +10,7 @@ * The easiest way to build simple but powerful apps and APIs quickly. * * @author Michael Darko - * @copyright 2019-2021 Michael Darko + * @copyright 2019-2022 Michael Darko * @link https://leafphp.dev * @license MIT * @package Leaf From c81a3ffe41a27c6184ace2f86405a2bc960c8189 Mon Sep 17 00:00:00 2001 From: Michael Darko Date: Thu, 27 Jan 2022 21:45:51 +0000 Subject: [PATCH 11/14] :bug: fixed broken tests on PHP 7 --- tests/app.test.php | 42 ++++++++++++------------------------------ 1 file changed, 12 insertions(+), 30 deletions(-) diff --git a/tests/app.test.php b/tests/app.test.php index 4eb68ac..c3fc7fa 100644 --- a/tests/app.test.php +++ b/tests/app.test.php @@ -7,49 +7,31 @@ }); test('app mode', function () { - app()->config('app.down', false); - - $_SERVER['REQUEST_METHOD'] = 'GET'; - $_SERVER['REQUEST_URI'] = '/'; + $_SERVER['REQUEST_METHOD'] = 'GET'; + $_SERVER['REQUEST_URI'] = '/'; - app()->config('mode', 'TEST'); - app()->get('/', function () { - }); - app()->script('TEST', function () { - app()->config('app.down', true); - }); + app()->setBasePath('/'); - app()->run(); + app()->config('test', false); + app()->config('mode', 'TEST'); - expect(app()->config('mode'))->toBe('TEST'); - expect(app()->config('app.down'))->toBe(true); -}); + app()->set404(function () {}); -test('set error handler', function () { - app()->config('app.down', false); - - $_SERVER['REQUEST_METHOD'] = 'GET'; - $_SERVER['REQUEST_URI'] = '/'; - - // create an error to trigger error handler - app()->get('/', function () { - $app; - }); - - app()->setErrorHandler(function () { - app()->config('app.down', true); + app()->script('TEST', function () { + app()->config('test', true); }); app()->run(); - expect(app()->config('app.down'))->toBe(true); + expect(app()->config('mode'))->toBe('TEST'); + expect(app()->config('test'))->toBe(true); }); test('set 404', function () { app()->config('app.down', false); - $_SERVER['REQUEST_METHOD'] = 'GET'; - $_SERVER['REQUEST_URI'] = '/'; + $_SERVER['REQUEST_METHOD'] = 'POST'; + $_SERVER['REQUEST_URI'] = '/home'; app()->set404(function () { app()->config('app.down', true); From 122f7f041251883c0bd4dac553f1dc953a34bf9f Mon Sep 17 00:00:00 2001 From: mychidarko Date: Thu, 27 Jan 2022 21:46:14 +0000 Subject: [PATCH 12/14] Fix styling --- tests/app.test.php | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tests/app.test.php b/tests/app.test.php index c3fc7fa..e88d157 100644 --- a/tests/app.test.php +++ b/tests/app.test.php @@ -7,15 +7,16 @@ }); test('app mode', function () { - $_SERVER['REQUEST_METHOD'] = 'GET'; - $_SERVER['REQUEST_URI'] = '/'; + $_SERVER['REQUEST_METHOD'] = 'GET'; + $_SERVER['REQUEST_URI'] = '/'; - app()->setBasePath('/'); + app()->setBasePath('/'); - app()->config('test', false); - app()->config('mode', 'TEST'); + app()->config('test', false); + app()->config('mode', 'TEST'); - app()->set404(function () {}); + app()->set404(function () { + }); app()->script('TEST', function () { app()->config('test', true); From 1d4ac2b2c9505b600ba14c5e72c817d1e8f50eeb Mon Sep 17 00:00:00 2001 From: Michael Darko Date: Thu, 27 Jan 2022 22:22:54 +0000 Subject: [PATCH 13/14] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20updated=20leaf=20exc?= =?UTF-8?q?eption?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 096c483..acbcc1d 100644 --- a/composer.json +++ b/composer.json @@ -34,7 +34,7 @@ "leafs/http": "^1.2", "leafs/router": "^0.1.5", "leafs/anchor": "^1.2", - "leafs/exception": "dev-master" + "leafs/exception": "^3.0" }, "require-dev": { "pestphp/pest": "^1.21", From 56cf96e709a0addc8ccbf297cd014ccf033bcc5d Mon Sep 17 00:00:00 2001 From: Michael Darko Date: Thu, 27 Jan 2022 22:31:37 +0000 Subject: [PATCH 14/14] :memo: updated readme --- README.md | 58 +++++++++++++++++++++++++++---------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 0231e44..30c4734 100644 --- a/README.md +++ b/README.md @@ -81,40 +81,40 @@ php -S localhost:8000 | Project | Status | Description | | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------ | -| [leaf] | [![Latest Stable Version](https://poser.pugx.org/leafs/leaf/v/stable)](https://packagist.org/packages/leafs/leaf) [![Total Downloads](https://poser.pugx.org/leafs/leaf/downloads)](https://packagist.org/packages/leafs/leaf) | Create websites and APIs quickly | -| [leafmvc] | [![Latest Stable Version](https://poser.pugx.org/leafs/mvc/v/stable)](https://packagist.org/packages/leafs/mvc) [![Total Downloads](https://poser.pugx.org/leafs/mvc/downloads)](https://packagist.org/packages/leafs/mvc) | An MVC wrapper for leaf (for general development) | -| [leafapi] | [![Latest Stable Version](https://poser.pugx.org/leafs/api/v/stable)](https://packagist.org/packages/leafs/api) [![Total Downloads](https://poser.pugx.org/leafs/api/downloads)](https://packagist.org/packages/leafs/api) | An MVC wrapper for leaf geared towards API development | -| [skeleton] | [![Latest Stable Version](https://poser.pugx.org/leafs/skeleton/v/stable)](https://packagist.org/packages/leafs/skeleton) [![Total Downloads](https://poser.pugx.org/leafs/skeleton/downloads)](https://packagist.org/packages/leafs/skeleton) | Leaf boilerplate for rapid development | -| [leaf-ui] | [![Latest Stable Version](https://poser.pugx.org/leafs/ui/v/stable)](https://packagist.org/packages/leafs/ui) [![Total Downloads](https://poser.pugx.org/leafs/ui/downloads)](https://packagist.org/packages/leafs/ui) | A PHP library for building user interfaces | -| [cli] | [![Latest Stable Version](https://poser.pugx.org/leafs/cli/v/stable)](https://packagist.org/packages/leafs/cli) [![Total Downloads](https://poser.pugx.org/leafs/cli/downloads)](https://packagist.org/packages/leafs/cli) | CLI for interacting with your leaf apps | +| [leaf](https://github.com/leafsphp/leaf) | [![Latest Stable Version](https://poser.pugx.org/leafs/leaf/v/stable)](https://packagist.org/packages/leafs/leaf) [![Total Downloads](https://poser.pugx.org/leafs/leaf/downloads)](https://packagist.org/packages/leafs/leaf) | Create websites and APIs quickly | +| [leafmvc](https://github.com/leafsphp/leafmvc) | [![Latest Stable Version](https://poser.pugx.org/leafs/mvc/v/stable)](https://packagist.org/packages/leafs/mvc) [![Total Downloads](https://poser.pugx.org/leafs/mvc/downloads)](https://packagist.org/packages/leafs/mvc) | An MVC wrapper for leaf (for general development) | +| [leafapi](https://github.com/leafsphp/leafapi) | [![Latest Stable Version](https://poser.pugx.org/leafs/api/v/stable)](https://packagist.org/packages/leafs/api) [![Total Downloads](https://poser.pugx.org/leafs/api/downloads)](https://packagist.org/packages/leafs/api) | An MVC wrapper for leaf geared towards API development | +| [skeleton](https://github.com/leafsphp/skeleton) | [![Latest Stable Version](https://poser.pugx.org/leafs/skeleton/v/stable)](https://packagist.org/packages/leafs/skeleton) [![Total Downloads](https://poser.pugx.org/leafs/skeleton/downloads)](https://packagist.org/packages/leafs/skeleton) | Leaf boilerplate for rapid development | +| [leaf-ui](https://github.com/leafsphp/leaf-ui) | [![Latest Stable Version](https://poser.pugx.org/leafs/ui/v/stable)](https://packagist.org/packages/leafs/ui) [![Total Downloads](https://poser.pugx.org/leafs/ui/downloads)](https://packagist.org/packages/leafs/ui) | A PHP library for building user interfaces | +| [cli](https://github.com/leafsphp/cli) | [![Latest Stable Version](https://poser.pugx.org/leafs/cli/v/stable)](https://packagist.org/packages/leafs/cli) [![Total Downloads](https://poser.pugx.org/leafs/cli/downloads)](https://packagist.org/packages/leafs/cli) | CLI for interacting with your leaf apps | ## 🧩 The Leaf Ecosystem (Modules) | Project | Status | Description | | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | -| [aloe] | [![Latest Stable Version](https://poser.pugx.org/leafs/aloe/v/stable)](https://packagist.org/packages/leafs/aloe) [![Total Downloads](https://poser.pugx.org/leafs/aloe/downloads)](https://packagist.org/packages/leafs/aloe) | Smart console helper for leaf mvc, leaf api and skeleton | -| [router] | [![Latest Stable Version](https://poser.pugx.org/leafs/router/v/stable)](https://packagist.org/packages/leafs/router) [![Total Downloads](https://poser.pugx.org/leafs/router/downloads)](https://packagist.org/packages/leafs/router) | Default router for leaf php | -| [experiments] | [![Latest Stable Version](https://poser.pugx.org/leafs/experimental/v/stable)](https://packagist.org/packages/leafs/experimental) [![Total Downloads](https://poser.pugx.org/leafs/experimental/downloads)](https://packagist.org/packages/leafs/experimental) | collection of experimental modules | -| [mail] | [![Latest Stable Version](https://poser.pugx.org/leafs/mail/v/stable)](https://packagist.org/packages/leafs/mail) [![Total Downloads](https://poser.pugx.org/leafs/mail/downloads)](https://packagist.org/packages/leafs/mail) | Mailing made easy with leaf | -| [auth] | [![Latest Stable Version](https://poser.pugx.org/leafs/auth/v/stable)](https://packagist.org/packages/leafs/auth) [![Total Downloads](https://poser.pugx.org/leafs/auth/downloads)](https://packagist.org/packages/leafs/auth) | Simple but powerful authentication system for your apps | -| [form] | [![Latest Stable Version](https://poser.pugx.org/leafs/form/v/stable)](https://packagist.org/packages/leafs/form) [![Total Downloads](https://poser.pugx.org/leafs/form/downloads)](https://packagist.org/packages/leafs/form) | Form processes and validation | -| [password] | [![Latest Stable Version](https://poser.pugx.org/leafs/password/v/stable)](https://packagist.org/packages/leafs/password) [![Total Downloads](https://poser.pugx.org/leafs/password/downloads)](https://packagist.org/packages/leafs/password) | Password encryption/validation/hashing in one box | -| [db-old] | [![Latest Stable Version](https://poser.pugx.org/leafs/db-old/v/stable)](https://packagist.org/packages/leafs/db-old) [![Total Downloads](https://poser.pugx.org/leafs/db-old/downloads)](https://packagist.org/packages/leafs/db-old) | Leaf Db from v1 (still maintained) | -| [db] | [![Latest Stable Version](https://poser.pugx.org/leafs/db/v/stable)](https://packagist.org/packages/leafs/db) [![Total Downloads](https://poser.pugx.org/leafs/db/downloads)](https://packagist.org/packages/leafs/db) | Leaf Db from v2 (actively maintained) | -| [session] | [![Latest Stable Version](https://poser.pugx.org/leafs/session/v/stable)](https://packagist.org/packages/leafs/session) [![Total Downloads](https://poser.pugx.org/leafs/session/downloads)](https://packagist.org/packages/leafs/session) | PHP sessions made simple | -| [cookie] | [![Latest Stable Version](https://poser.pugx.org/leafs/cookie/v/stable)](https://packagist.org/packages/leafs/cookie) [![Total Downloads](https://poser.pugx.org/leafs/cookie/downloads)](https://packagist.org/packages/leafs/cookie) | Cookie management without the tears | -| [logger] | [![Latest Stable Version](https://poser.pugx.org/leafs/logger/v/stable)](https://packagist.org/packages/leafs/logger) [![Total Downloads](https://poser.pugx.org/leafs/logger/downloads)](https://packagist.org/packages/leafs/logger) | leaf logger module | -| [fs] | [![Latest Stable Version](https://poser.pugx.org/leafs/fs/v/stable)](https://packagist.org/packages/leafs/fs) [![Total Downloads](https://poser.pugx.org/leafs/fs/downloads)](https://packagist.org/packages/leafs/fs) | Awesome filesystem operations + file uploads | -| [date] | [![Latest Stable Version](https://poser.pugx.org/leafs/date/v/stable)](https://packagist.org/packages/leafs/date) [![Total Downloads](https://poser.pugx.org/leafs/date/downloads)](https://packagist.org/packages/leafs/date) | PHP dates for humans | -| [bareui] | [![Latest Stable Version](https://poser.pugx.org/leafs/bareui/v/stable)](https://packagist.org/packages/leafs/bareui) [![Total Downloads](https://poser.pugx.org/leafs/bareui/downloads)](https://packagist.org/packages/leafs/bareui) | Dead simple templating engine with no compilation (blazing speed) | -| [blade] | [![Latest Stable Version](https://poser.pugx.org/leafs/blade/v/stable)](https://packagist.org/packages/leafs/blade) [![Total Downloads](https://poser.pugx.org/leafs/blade/downloads)](https://packagist.org/packages/leafs/blade) | Laravel blade templating port for leaf | -| [veins] | [![Latest Stable Version](https://poser.pugx.org/leafs/veins/v/stable)](https://packagist.org/packages/leafs/veins) [![Total Downloads](https://poser.pugx.org/leafs/veins/downloads)](https://packagist.org/packages/leafs/veins) | Leaf veins templating engine | -| [http] | [![Latest Stable Version](https://poser.pugx.org/leafs/http/v/stable)](https://packagist.org/packages/leafs/http) [![Total Downloads](https://poser.pugx.org/leafs/http/downloads)](https://packagist.org/packages/leafs/http) | Http operations made simple (request, response, ...) | -| [anchor] | [![Latest Stable Version](https://poser.pugx.org/leafs/anchor/v/stable)](https://packagist.org/packages/leafs/anchor) [![Total Downloads](https://poser.pugx.org/leafs/anchor/downloads)](https://packagist.org/packages/leafs/anchor) | Basic security tools | -| [mvc-core] | [![Latest Stable Version](https://poser.pugx.org/leafs/mvc-core/v/stable)](https://packagist.org/packages/leafs/mvc-core) [![Total Downloads](https://poser.pugx.org/leafs/mvc-core/downloads)](https://packagist.org/packages/leafs/mvc-core) | Core MVC tools powering our MVC wrappers | -| [aloe] | [![Latest Stable Version](https://poser.pugx.org/leafs/aloe/v/stable)](https://packagist.org/packages/leafs/aloe) [![Total Downloads](https://poser.pugx.org/leafs/aloe/downloads)](https://packagist.org/packages/leafs/aloe) | Overpowered cli for our MVC wrappers | -| [fetch] | [![Latest Stable Version](https://poser.pugx.org/leafs/fetch/v/stable)](https://packagist.org/packages/leafs/fetch) [![Total Downloads](https://poser.pugx.org/leafs/fetch/downloads)](https://packagist.org/packages/leafs/fetch) | HTTP requests made simple | -| [redis] | [![Latest Stable Version](https://poser.pugx.org/leafs/redis/v/stable)](https://packagist.org/packages/leafs/redis) [![Total Downloads](https://poser.pugx.org/leafs/redis/downloads)](https://packagist.org/packages/leafs/redis) | Redis module | +| [aloe](https://github.com/leafsphp/aloe) | [![Latest Stable Version](https://poser.pugx.org/leafs/aloe/v/stable)](https://packagist.org/packages/leafs/aloe) [![Total Downloads](https://poser.pugx.org/leafs/aloe/downloads)](https://packagist.org/packages/leafs/aloe) | Smart console helper for leaf mvc, leaf api and skeleton | +| [router](https://github.com/leafsphp/router) | [![Latest Stable Version](https://poser.pugx.org/leafs/router/v/stable)](https://packagist.org/packages/leafs/router) [![Total Downloads](https://poser.pugx.org/leafs/router/downloads)](https://packagist.org/packages/leafs/router) | Default router for leaf php | +| [experiments](https://github.com/leafsphp/experimental-modules) | [![Latest Stable Version](https://poser.pugx.org/leafs/experimental/v/stable)](https://packagist.org/packages/leafs/experimental) [![Total Downloads](https://poser.pugx.org/leafs/experimental/downloads)](https://packagist.org/packages/leafs/experimental) | collection of experimental modules | +| [mail](https://github.com/leafsphp/mail) | [![Latest Stable Version](https://poser.pugx.org/leafs/mail/v/stable)](https://packagist.org/packages/leafs/mail) [![Total Downloads](https://poser.pugx.org/leafs/mail/downloads)](https://packagist.org/packages/leafs/mail) | Mailing made easy with leaf | +| [auth](https://github.com/leafsphp/auth) | [![Latest Stable Version](https://poser.pugx.org/leafs/auth/v/stable)](https://packagist.org/packages/leafs/auth) [![Total Downloads](https://poser.pugx.org/leafs/auth/downloads)](https://packagist.org/packages/leafs/auth) | Simple but powerful authentication system for your apps | +| [form](https://github.com/leafsphp/form) | [![Latest Stable Version](https://poser.pugx.org/leafs/form/v/stable)](https://packagist.org/packages/leafs/form) [![Total Downloads](https://poser.pugx.org/leafs/form/downloads)](https://packagist.org/packages/leafs/form) | Form processes and validation | +| [password](https://github.com/leafsphp/password) | [![Latest Stable Version](https://poser.pugx.org/leafs/password/v/stable)](https://packagist.org/packages/leafs/password) [![Total Downloads](https://poser.pugx.org/leafs/password/downloads)](https://packagist.org/packages/leafs/password) | Password encryption/validation/hashing in one box | +| [db-old](https://github.com/leafsphp/db-old) | [![Latest Stable Version](https://poser.pugx.org/leafs/db-old/v/stable)](https://packagist.org/packages/leafs/db-old) [![Total Downloads](https://poser.pugx.org/leafs/db-old/downloads)](https://packagist.org/packages/leafs/db-old) | Leaf Db from v1 (still maintained) | +| [db](https://github.com/leafsphp/db) | [![Latest Stable Version](https://poser.pugx.org/leafs/db/v/stable)](https://packagist.org/packages/leafs/db) [![Total Downloads](https://poser.pugx.org/leafs/db/downloads)](https://packagist.org/packages/leafs/db) | Leaf Db from v2 (actively maintained) | +| [session](https://github.com/leafsphp/session) | [![Latest Stable Version](https://poser.pugx.org/leafs/session/v/stable)](https://packagist.org/packages/leafs/session) [![Total Downloads](https://poser.pugx.org/leafs/session/downloads)](https://packagist.org/packages/leafs/session) | PHP sessions made simple | +| [cookie](https://github.com/leafsphp/cookie) | [![Latest Stable Version](https://poser.pugx.org/leafs/cookie/v/stable)](https://packagist.org/packages/leafs/cookie) [![Total Downloads](https://poser.pugx.org/leafs/cookie/downloads)](https://packagist.org/packages/leafs/cookie) | Cookie management without the tears | +| [logger](https://github.com/leafsphp/logger) | [![Latest Stable Version](https://poser.pugx.org/leafs/logger/v/stable)](https://packagist.org/packages/leafs/logger) [![Total Downloads](https://poser.pugx.org/leafs/logger/downloads)](https://packagist.org/packages/leafs/logger) | leaf logger module | +| [fs](https://github.com/leafsphp/fs) | [![Latest Stable Version](https://poser.pugx.org/leafs/fs/v/stable)](https://packagist.org/packages/leafs/fs) [![Total Downloads](https://poser.pugx.org/leafs/fs/downloads)](https://packagist.org/packages/leafs/fs) | Awesome filesystem operations + file uploads | +| [date](https://github.com/leafsphp/date) | [![Latest Stable Version](https://poser.pugx.org/leafs/date/v/stable)](https://packagist.org/packages/leafs/date) [![Total Downloads](https://poser.pugx.org/leafs/date/downloads)](https://packagist.org/packages/leafs/date) | PHP dates for humans | +| [bareui](https://github.com/leafsphp/bareui) | [![Latest Stable Version](https://poser.pugx.org/leafs/bareui/v/stable)](https://packagist.org/packages/leafs/bareui) [![Total Downloads](https://poser.pugx.org/leafs/bareui/downloads)](https://packagist.org/packages/leafs/bareui) | Dead simple templating engine with no compilation (blazing speed) | +| [blade](https://github.com/leafsphp/blade) | [![Latest Stable Version](https://poser.pugx.org/leafs/blade/v/stable)](https://packagist.org/packages/leafs/blade) [![Total Downloads](https://poser.pugx.org/leafs/blade/downloads)](https://packagist.org/packages/leafs/blade) | Laravel blade templating port for leaf | +| [veins](https://github.com/leafsphp/veins) | [![Latest Stable Version](https://poser.pugx.org/leafs/veins/v/stable)](https://packagist.org/packages/leafs/veins) [![Total Downloads](https://poser.pugx.org/leafs/veins/downloads)](https://packagist.org/packages/leafs/veins) | Leaf veins templating engine | +| [http](https://github.com/leafsphp/http) | [![Latest Stable Version](https://poser.pugx.org/leafs/http/v/stable)](https://packagist.org/packages/leafs/http) [![Total Downloads](https://poser.pugx.org/leafs/http/downloads)](https://packagist.org/packages/leafs/http) | Http operations made simple (request, response, ...) | +| [anchor](https://github.com/leafsphp/anchor) | [![Latest Stable Version](https://poser.pugx.org/leafs/anchor/v/stable)](https://packagist.org/packages/leafs/anchor) [![Total Downloads](https://poser.pugx.org/leafs/anchor/downloads)](https://packagist.org/packages/leafs/anchor) | Basic security tools | +| [mvc-core](https://github.com/leafsphp/mvc-core) | [![Latest Stable Version](https://poser.pugx.org/leafs/mvc-core/v/stable)](https://packagist.org/packages/leafs/mvc-core) [![Total Downloads](https://poser.pugx.org/leafs/mvc-core/downloads)](https://packagist.org/packages/leafs/mvc-core) | Core MVC tools powering our MVC wrappers | +| [aloe](https://github.com/leafsphp/aloe) | [![Latest Stable Version](https://poser.pugx.org/leafs/aloe/v/stable)](https://packagist.org/packages/leafs/aloe) [![Total Downloads](https://poser.pugx.org/leafs/aloe/downloads)](https://packagist.org/packages/leafs/aloe) | Overpowered cli for our MVC wrappers | +| [fetch](https://github.com/leafsphp/fetch) | [![Latest Stable Version](https://poser.pugx.org/leafs/fetch/v/stable)](https://packagist.org/packages/leafs/fetch) [![Total Downloads](https://poser.pugx.org/leafs/fetch/downloads)](https://packagist.org/packages/leafs/fetch) | HTTP requests made simple | +| [redis](https://github.com/leafsphp/redis) | [![Latest Stable Version](https://poser.pugx.org/leafs/redis/v/stable)](https://packagist.org/packages/leafs/redis) [![Total Downloads](https://poser.pugx.org/leafs/redis/downloads)](https://packagist.org/packages/leafs/redis) | Redis module | ## 💬 Stay In Touch