From a6f65a777efdc44b89e9bee835a8e5b703833f24 Mon Sep 17 00:00:00 2001 From: MGatner Date: Fri, 3 Jun 2022 14:04:15 +0000 Subject: [PATCH] Release v4.2.0 --- .github/workflows/phpunit.yml | 2 +- README.md | 2 +- app/Config/App.php | 7 +- app/Config/Constants.php | 21 +- app/Config/ContentSecurityPolicy.php | 21 + app/Config/Database.php | 35 +- app/Config/Events.php | 4 +- app/Config/Feature.php | 7 +- app/Config/Filters.php | 6 +- app/Config/Format.php | 8 +- app/Config/Logger.php | 3 +- app/Config/Mimes.php | 1 + app/Config/Publisher.php | 2 +- app/Config/Routes.php | 10 +- app/Config/Security.php | 2 +- app/Config/Validation.php | 3 +- app/Config/View.php | 12 + app/Controllers/BaseController.php | 2 +- app/Views/errors/html/debug.css | 214 +++---- app/Views/errors/html/debug.js | 172 +++--- app/Views/errors/html/error_404.php | 150 ++--- app/Views/errors/html/error_exception.php | 710 +++++++++++----------- app/Views/errors/html/production.php | 20 +- app/Views/welcome_message.php | 532 ++++++++-------- app/index.html | 2 +- builds | 2 +- composer.json | 8 +- env | 7 +- public/index.php | 34 +- spark | 59 +- tests/README.md | 13 +- 31 files changed, 1091 insertions(+), 980 deletions(-) diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml index 5e37bd91..73d413ca 100644 --- a/.github/workflows/phpunit.yml +++ b/.github/workflows/phpunit.yml @@ -11,7 +11,7 @@ jobs: strategy: matrix: - php-versions: ['7.3', '7.4'] + php-versions: ['7.4', '8.0'] runs-on: ubuntu-latest diff --git a/README.md b/README.md index 363e7c89..2e17d9c9 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ Problems with it can be raised on our forum, or as issues in the main repository ## Server Requirements -PHP version 7.3 or higher is required, with the following extensions installed: +PHP version 7.4 or higher is required, with the following extensions installed: - [intl](http://php.net/manual/en/intl.requirements.php) - [libcurl](http://php.net/manual/en/curl.requirements.php) if you plan to use the HTTP\CURLRequest library diff --git a/app/Config/App.php b/app/Config/App.php index 88b295e9..c6c71682 100644 --- a/app/Config/App.php +++ b/app/Config/App.php @@ -3,6 +3,7 @@ namespace Config; use CodeIgniter\Config\BaseConfig; +use CodeIgniter\Session\Handlers\FileHandler; class App extends BaseConfig { @@ -151,7 +152,7 @@ class App extends BaseConfig * * @var string */ - public $sessionDriver = 'CodeIgniter\Session\Handlers\FileHandler'; + public $sessionDriver = FileHandler::class; /** * -------------------------------------------------------------------------- @@ -318,7 +319,7 @@ class App extends BaseConfig * (empty string) means default SameSite attribute set by browsers (`Lax`) * will be set on cookies. If set to `None`, `$cookieSecure` must also be set. * - * @var string + * @var string|null * * @deprecated use Config\Cookie::$samesite property instead. */ @@ -436,7 +437,7 @@ class App extends BaseConfig * Defaults to `Lax` as recommended in this link: * * @see https://portswigger.net/web-security/csrf/samesite-cookies - * @deprecated Use `Config\Security` $samesite property instead of using this property. + * @deprecated `Config\Cookie` $samesite property is used. * * @var string */ diff --git a/app/Config/Constants.php b/app/Config/Constants.php index 8f8498a5..0c1b5348 100644 --- a/app/Config/Constants.php +++ b/app/Config/Constants.php @@ -38,9 +38,9 @@ defined('HOUR') || define('HOUR', 3600); defined('DAY') || define('DAY', 86400); defined('WEEK') || define('WEEK', 604800); -defined('MONTH') || define('MONTH', 2592000); -defined('YEAR') || define('YEAR', 31536000); -defined('DECADE') || define('DECADE', 315360000); +defined('MONTH') || define('MONTH', 2_592_000); +defined('YEAR') || define('YEAR', 31_536_000); +defined('DECADE') || define('DECADE', 315_360_000); /* | -------------------------------------------------------------------------- @@ -77,3 +77,18 @@ defined('EXIT_DATABASE') || define('EXIT_DATABASE', 8); // database error defined('EXIT__AUTO_MIN') || define('EXIT__AUTO_MIN', 9); // lowest automatically-assigned error code defined('EXIT__AUTO_MAX') || define('EXIT__AUTO_MAX', 125); // highest automatically-assigned error code + +/** + * @deprecated Use \CodeIgniter\Events\Events::PRIORITY_LOW instead. + */ +define('EVENT_PRIORITY_LOW', 200); + +/** + * @deprecated Use \CodeIgniter\Events\Events::PRIORITY_NORMAL instead. + */ +define('EVENT_PRIORITY_NORMAL', 100); + +/** + * @deprecated Use \CodeIgniter\Events\Events::PRIORITY_HIGH instead. + */ +define('EVENT_PRIORITY_HIGH', 10); diff --git a/app/Config/ContentSecurityPolicy.php b/app/Config/ContentSecurityPolicy.php index 6fa5bd7b..aa18ba9f 100644 --- a/app/Config/ContentSecurityPolicy.php +++ b/app/Config/ContentSecurityPolicy.php @@ -164,4 +164,25 @@ class ContentSecurityPolicy extends BaseConfig * @var string|string[]|null */ public $sandbox; + + /** + * Nonce tag for style + * + * @var string + */ + public $styleNonceTag = '{csp-style-nonce}'; + + /** + * Nonce tag for script + * + * @var string + */ + public $scriptNonceTag = '{csp-script-nonce}'; + + /** + * Replace nonce tag automatically + * + * @var bool + */ + public $autoNonce = true; } diff --git a/app/Config/Database.php b/app/Config/Database.php index 1e634089..87d73b13 100644 --- a/app/Config/Database.php +++ b/app/Config/Database.php @@ -57,23 +57,24 @@ class Database extends Config * @var array */ public $tests = [ - 'DSN' => '', - 'hostname' => '127.0.0.1', - 'username' => '', - 'password' => '', - 'database' => ':memory:', - 'DBDriver' => 'SQLite3', - 'DBPrefix' => 'db_', // Needed to ensure we're working correctly with prefixes live. DO NOT REMOVE FOR CI DEVS - 'pConnect' => false, - 'DBDebug' => (ENVIRONMENT !== 'production'), - 'charset' => 'utf8', - 'DBCollat' => 'utf8_general_ci', - 'swapPre' => '', - 'encrypt' => false, - 'compress' => false, - 'strictOn' => false, - 'failover' => [], - 'port' => 3306, + 'DSN' => '', + 'hostname' => '127.0.0.1', + 'username' => '', + 'password' => '', + 'database' => ':memory:', + 'DBDriver' => 'SQLite3', + 'DBPrefix' => 'db_', // Needed to ensure we're working correctly with prefixes live. DO NOT REMOVE FOR CI DEVS + 'pConnect' => false, + 'DBDebug' => (ENVIRONMENT !== 'production'), + 'charset' => 'utf8', + 'DBCollat' => 'utf8_general_ci', + 'swapPre' => '', + 'encrypt' => false, + 'compress' => false, + 'strictOn' => false, + 'failover' => [], + 'port' => 3306, + 'foreignKeys' => true, ]; public function __construct() diff --git a/app/Config/Events.php b/app/Config/Events.php index 183280eb..5219f4ac 100644 --- a/app/Config/Events.php +++ b/app/Config/Events.php @@ -32,9 +32,7 @@ ob_end_flush(); } - ob_start(static function ($buffer) { - return $buffer; - }); + ob_start(static fn ($buffer) => $buffer); } /* diff --git a/app/Config/Feature.php b/app/Config/Feature.php index af42534a..4c5ec90c 100644 --- a/app/Config/Feature.php +++ b/app/Config/Feature.php @@ -10,7 +10,7 @@ class Feature extends BaseConfig { /** - * Enable multiple filters for a route or not + * Enable multiple filters for a route or not. * * If you enable this: * - CodeIgniter\CodeIgniter::handleRequest() uses: @@ -24,4 +24,9 @@ class Feature extends BaseConfig * @var bool */ public $multipleFilters = false; + + /** + * Use improved new auto routing instead of the default legacy version. + */ + public bool $autoRoutesImproved = false; } diff --git a/app/Config/Filters.php b/app/Config/Filters.php index 14685207..d0a97238 100644 --- a/app/Config/Filters.php +++ b/app/Config/Filters.php @@ -49,7 +49,11 @@ class Filters extends BaseConfig * particular HTTP method (GET, POST, etc.). * * Example: - * 'post' => ['csrf', 'throttle'] + * 'post' => ['foo', 'bar'] + * + * If you use this, you should disable auto-routing because auto-routing + * permits any HTTP method to access a controller. Accessing the controller + * with a method you don’t expect could bypass the filter. * * @var array */ diff --git a/app/Config/Format.php b/app/Config/Format.php index 533540e2..d89e4084 100644 --- a/app/Config/Format.php +++ b/app/Config/Format.php @@ -4,6 +4,8 @@ use CodeIgniter\Config\BaseConfig; use CodeIgniter\Format\FormatterInterface; +use CodeIgniter\Format\JSONFormatter; +use CodeIgniter\Format\XMLFormatter; class Format extends BaseConfig { @@ -40,9 +42,9 @@ class Format extends BaseConfig * @var array */ public $formatters = [ - 'application/json' => 'CodeIgniter\Format\JSONFormatter', - 'application/xml' => 'CodeIgniter\Format\XMLFormatter', - 'text/xml' => 'CodeIgniter\Format\XMLFormatter', + 'application/json' => JSONFormatter::class, + 'application/xml' => XMLFormatter::class, + 'text/xml' => XMLFormatter::class, ]; /** diff --git a/app/Config/Logger.php b/app/Config/Logger.php index a4eaeb64..406d9aac 100644 --- a/app/Config/Logger.php +++ b/app/Config/Logger.php @@ -2,6 +2,7 @@ namespace Config; +use CodeIgniter\Log\Handlers\FileHandler; use CodeIgniter\Config\BaseConfig; class Logger extends BaseConfig @@ -83,7 +84,7 @@ class Logger extends BaseConfig * File Handler * -------------------------------------------------------------------- */ - 'CodeIgniter\Log\Handlers\FileHandler' => [ + FileHandler::class => [ // The log levels that this handler will handle. 'handles' => [ diff --git a/app/Config/Mimes.php b/app/Config/Mimes.php index 786bc6a1..a1bb458a 100644 --- a/app/Config/Mimes.php +++ b/app/Config/Mimes.php @@ -260,6 +260,7 @@ class Mimes 'image/png', 'image/x-png', ], + 'webp' => 'image/webp', 'tif' => 'image/tiff', 'tiff' => 'image/tiff', 'css' => [ diff --git a/app/Config/Publisher.php b/app/Config/Publisher.php index f3768bc5..47475112 100644 --- a/app/Config/Publisher.php +++ b/app/Config/Publisher.php @@ -23,6 +23,6 @@ class Publisher extends BasePublisher */ public $restrictions = [ ROOTPATH => '*', - FCPATH => '#\.(?css|js|map|htm?|xml|json|webmanifest|tff|eot|woff?|gif|jpe?g|tiff?|png|webp|bmp|ico|svg)$#i', + FCPATH => '#\.(s?css|js|map|html?|xml|json|webmanifest|ttf|eot|woff2?|gif|jpe?g|tiff?|png|webp|bmp|ico|svg)$#i', ]; } diff --git a/app/Config/Routes.php b/app/Config/Routes.php index 0060a6f6..ff2ac645 100644 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -7,7 +7,7 @@ // Load the system's routing file first, so that the app and ENVIRONMENT // can override as needed. -if (file_exists(SYSTEMPATH . 'Config/Routes.php')) { +if (is_file(SYSTEMPATH . 'Config/Routes.php')) { require SYSTEMPATH . 'Config/Routes.php'; } @@ -21,7 +21,11 @@ $routes->setDefaultMethod('index'); $routes->setTranslateURIDashes(false); $routes->set404Override(); -$routes->setAutoRoute(true); +// The Auto Routing (Legacy) is very dangerous. It is easy to create vulnerable apps +// where controller filters or CSRF protection are bypassed. +// If you don't want to define all routes, please use the Auto Routing (Improved). +// Set `$autoRoutesImproved` to true in `app/Config/Feature.php` and set the following to true. +//$routes->setAutoRoute(false); /* * -------------------------------------------------------------------- @@ -46,6 +50,6 @@ * You will have access to the $routes object within that file without * needing to reload it. */ -if (file_exists(APPPATH . 'Config/' . ENVIRONMENT . '/Routes.php')) { +if (is_file(APPPATH . 'Config/' . ENVIRONMENT . '/Routes.php')) { require APPPATH . 'Config/' . ENVIRONMENT . '/Routes.php'; } diff --git a/app/Config/Security.php b/app/Config/Security.php index 05083f8b..107bd954 100644 --- a/app/Config/Security.php +++ b/app/Config/Security.php @@ -111,7 +111,7 @@ class Security extends BaseConfig * * @var string * - * @deprecated + * @deprecated `Config\Cookie` $samesite property is used. */ public $samesite = 'Lax'; } diff --git a/app/Config/Validation.php b/app/Config/Validation.php index 1cff0424..a254c185 100644 --- a/app/Config/Validation.php +++ b/app/Config/Validation.php @@ -2,12 +2,13 @@ namespace Config; +use CodeIgniter\Config\BaseConfig; use CodeIgniter\Validation\CreditCardRules; use CodeIgniter\Validation\FileRules; use CodeIgniter\Validation\FormatRules; use CodeIgniter\Validation\Rules; -class Validation +class Validation extends BaseConfig { //-------------------------------------------------------------------- // Setup diff --git a/app/Config/View.php b/app/Config/View.php index 024e8302..78cd547e 100644 --- a/app/Config/View.php +++ b/app/Config/View.php @@ -3,6 +3,7 @@ namespace Config; use CodeIgniter\Config\View as BaseView; +use CodeIgniter\View\ViewDecoratorInterface; class View extends BaseView { @@ -41,4 +42,15 @@ class View extends BaseView * @var array */ public $plugins = []; + + /** + * View Decorators are class methods that will be run in sequence to + * have a chance to alter the generated output just prior to caching + * the results. + * + * All classes must implement CodeIgniter\View\ViewDecoratorInterface + * + * @var class-string[] + */ + public array $decorators = []; } diff --git a/app/Controllers/BaseController.php b/app/Controllers/BaseController.php index 0328f140..122db5f9 100644 --- a/app/Controllers/BaseController.php +++ b/app/Controllers/BaseController.php @@ -19,7 +19,7 @@ * * For security be sure to declare any new methods as protected or private. */ -class BaseController extends Controller +abstract class BaseController extends Controller { /** * Instance of the main Request object. diff --git a/app/Views/errors/html/debug.css b/app/Views/errors/html/debug.css index 384d66d9..98f54dbc 100644 --- a/app/Views/errors/html/debug.css +++ b/app/Views/errors/html/debug.css @@ -1,197 +1,197 @@ :root { - --main-bg-color: #fff; - --main-text-color: #555; - --dark-text-color: #222; - --light-text-color: #c7c7c7; - --brand-primary-color: #E06E3F; - --light-bg-color: #ededee; - --dark-bg-color: #404040; + --main-bg-color: #fff; + --main-text-color: #555; + --dark-text-color: #222; + --light-text-color: #c7c7c7; + --brand-primary-color: #E06E3F; + --light-bg-color: #ededee; + --dark-bg-color: #404040; } body { - height: 100%; - background: var(--main-bg-color); - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; - color: var(--main-text-color); - font-weight: 300; - margin: 0; - padding: 0; + height: 100%; + background: var(--main-bg-color); + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; + color: var(--main-text-color); + font-weight: 300; + margin: 0; + padding: 0; } h1 { - font-weight: lighter; - letter-spacing: 0.8; - font-size: 3rem; - color: var(--dark-text-color); - margin: 0; + font-weight: lighter; + letter-spacing: 0.8; + font-size: 3rem; + color: var(--dark-text-color); + margin: 0; } h1.headline { - margin-top: 20%; - font-size: 5rem; + margin-top: 20%; + font-size: 5rem; } .text-center { - text-align: center; + text-align: center; } p.lead { - font-size: 1.6rem; + font-size: 1.6rem; } .container { - max-width: 75rem; - margin: 0 auto; - padding: 1rem; + max-width: 75rem; + margin: 0 auto; + padding: 1rem; } .header { - background: var(--light-bg-color); - color: var(--dark-text-color); + background: var(--light-bg-color); + color: var(--dark-text-color); } .header .container { - padding: 1rem 1.75rem 1.75rem 1.75rem; + padding: 1rem 1.75rem 1.75rem 1.75rem; } .header h1 { - font-size: 2.5rem; - font-weight: 500; + font-size: 2.5rem; + font-weight: 500; } .header p { - font-size: 1.2rem; - margin: 0; - line-height: 2.5; + font-size: 1.2rem; + margin: 0; + line-height: 2.5; } .header a { - color: var(--brand-primary-color); - margin-left: 2rem; - display: none; - text-decoration: none; + color: var(--brand-primary-color); + margin-left: 2rem; + display: none; + text-decoration: none; } .header:hover a { - display: inline; + display: inline; } .footer { - background: var(--dark-bg-color); - color: var(--light-text-color); + background: var(--dark-bg-color); + color: var(--light-text-color); } .footer .container { - border-top: 1px solid #e7e7e7; - margin-top: 1rem; - text-align: center; + border-top: 1px solid #e7e7e7; + margin-top: 1rem; + text-align: center; } .source { - background: #343434; - color: var(--light-text-color); - padding: 0.5em 1em; - border-radius: 5px; - font-family: Menlo, Monaco, Consolas, "Courier New", monospace; - font-size: 0.85rem; - margin: 0; - overflow-x: scroll; + background: #343434; + color: var(--light-text-color); + padding: 0.5em 1em; + border-radius: 5px; + font-family: Menlo, Monaco, Consolas, "Courier New", monospace; + font-size: 0.85rem; + margin: 0; + overflow-x: scroll; } .source span.line { - line-height: 1.4; + line-height: 1.4; } .source span.line .number { - color: #666; + color: #666; } .source .line .highlight { - display: block; - background: var(--dark-text-color); - color: var(--light-text-color); + display: block; + background: var(--dark-text-color); + color: var(--light-text-color); } .source span.highlight .number { - color: #fff; + color: #fff; } .tabs { - list-style: none; - list-style-position: inside; - margin: 0; - padding: 0; - margin-bottom: -1px; + list-style: none; + list-style-position: inside; + margin: 0; + padding: 0; + margin-bottom: -1px; } .tabs li { - display: inline; + display: inline; } .tabs a:link, .tabs a:visited { - padding: 0rem 1rem; - line-height: 2.7; - text-decoration: none; - color: var(--dark-text-color); - background: var(--light-bg-color); - border: 1px solid rgba(0,0,0,0.15); - border-bottom: 0; - border-top-left-radius: 5px; - border-top-right-radius: 5px; - display: inline-block; + padding: 0rem 1rem; + line-height: 2.7; + text-decoration: none; + color: var(--dark-text-color); + background: var(--light-bg-color); + border: 1px solid rgba(0,0,0,0.15); + border-bottom: 0; + border-top-left-radius: 5px; + border-top-right-radius: 5px; + display: inline-block; } .tabs a:hover { - background: var(--light-bg-color); - border-color: rgba(0,0,0,0.15); + background: var(--light-bg-color); + border-color: rgba(0,0,0,0.15); } .tabs a.active { - background: var(--main-bg-color); - color: var(--main-text-color); + background: var(--main-bg-color); + color: var(--main-text-color); } .tab-content { - background: var(--main-bg-color); - border: 1px solid rgba(0,0,0,0.15); + background: var(--main-bg-color); + border: 1px solid rgba(0,0,0,0.15); } .content { - padding: 1rem; + padding: 1rem; } .hide { - display: none; + display: none; } .alert { - margin-top: 2rem; - display: block; - text-align: center; - line-height: 3.0; - background: #d9edf7; - border: 1px solid #bcdff1; - border-radius: 5px; - color: #31708f; + margin-top: 2rem; + display: block; + text-align: center; + line-height: 3.0; + background: #d9edf7; + border: 1px solid #bcdff1; + border-radius: 5px; + color: #31708f; } ul, ol { - line-height: 1.8; + line-height: 1.8; } table { - width: 100%; - overflow: hidden; + width: 100%; + overflow: hidden; } th { - text-align: left; - border-bottom: 1px solid #e7e7e7; - padding-bottom: 0.5rem; + text-align: left; + border-bottom: 1px solid #e7e7e7; + padding-bottom: 0.5rem; } td { - padding: 0.2rem 0.5rem 0.2rem 0; + padding: 0.2rem 0.5rem 0.2rem 0; } tr:hover td { - background: #f1f1f1; + background: #f1f1f1; } td pre { - white-space: pre-wrap; + white-space: pre-wrap; } .trace a { - color: inherit; + color: inherit; } .trace table { - width: auto; + width: auto; } .trace tr td:first-child { - min-width: 5em; - font-weight: bold; + min-width: 5em; + font-weight: bold; } .trace td { - background: var(--light-bg-color); - padding: 0 1rem; + background: var(--light-bg-color); + padding: 0 1rem; } .trace td pre { - margin: 0; + margin: 0; } .args { - display: none; + display: none; } diff --git a/app/Views/errors/html/debug.js b/app/Views/errors/html/debug.js index 2b4d0638..99199cac 100644 --- a/app/Views/errors/html/debug.js +++ b/app/Views/errors/html/debug.js @@ -1,118 +1,116 @@ -// Tabs - var tabLinks = new Array(); var contentDivs = new Array(); function init() { - // Grab the tab links and content divs from the page - var tabListItems = document.getElementById('tabs').childNodes; - console.log(tabListItems); - for (var i = 0; i < tabListItems.length; i ++) - { - if (tabListItems[i].nodeName == "LI") - { - var tabLink = getFirstChildWithTagName(tabListItems[i], 'A'); - var id = getHash(tabLink.getAttribute('href')); - tabLinks[id] = tabLink; - contentDivs[id] = document.getElementById(id); - } - } + // Grab the tab links and content divs from the page + var tabListItems = document.getElementById('tabs').childNodes; + console.log(tabListItems); + for (var i = 0; i < tabListItems.length; i ++) + { + if (tabListItems[i].nodeName == "LI") + { + var tabLink = getFirstChildWithTagName(tabListItems[i], 'A'); + var id = getHash(tabLink.getAttribute('href')); + tabLinks[id] = tabLink; + contentDivs[id] = document.getElementById(id); + } + } - // Assign onclick events to the tab links, and - // highlight the first tab - var i = 0; + // Assign onclick events to the tab links, and + // highlight the first tab + var i = 0; - for (var id in tabLinks) - { - tabLinks[id].onclick = showTab; - tabLinks[id].onfocus = function () { - this.blur() - }; - if (i == 0) - { - tabLinks[id].className = 'active'; - } - i ++; - } + for (var id in tabLinks) + { + tabLinks[id].onclick = showTab; + tabLinks[id].onfocus = function () { + this.blur() + }; + if (i == 0) + { + tabLinks[id].className = 'active'; + } + i ++; + } - // Hide all content divs except the first - var i = 0; + // Hide all content divs except the first + var i = 0; - for (var id in contentDivs) - { - if (i != 0) - { - console.log(contentDivs[id]); - contentDivs[id].className = 'content hide'; - } - i ++; - } + for (var id in contentDivs) + { + if (i != 0) + { + console.log(contentDivs[id]); + contentDivs[id].className = 'content hide'; + } + i ++; + } } function showTab() { - var selectedId = getHash(this.getAttribute('href')); + var selectedId = getHash(this.getAttribute('href')); - // Highlight the selected tab, and dim all others. - // Also show the selected content div, and hide all others. - for (var id in contentDivs) - { - if (id == selectedId) - { - tabLinks[id].className = 'active'; - contentDivs[id].className = 'content'; - } - else - { - tabLinks[id].className = ''; - contentDivs[id].className = 'content hide'; - } - } + // Highlight the selected tab, and dim all others. + // Also show the selected content div, and hide all others. + for (var id in contentDivs) + { + if (id == selectedId) + { + tabLinks[id].className = 'active'; + contentDivs[id].className = 'content'; + } + else + { + tabLinks[id].className = ''; + contentDivs[id].className = 'content hide'; + } + } - // Stop the browser following the link - return false; + // Stop the browser following the link + return false; } function getFirstChildWithTagName(element, tagName) { - for (var i = 0; i < element.childNodes.length; i ++) - { - if (element.childNodes[i].nodeName == tagName) - { - return element.childNodes[i]; - } - } + for (var i = 0; i < element.childNodes.length; i ++) + { + if (element.childNodes[i].nodeName == tagName) + { + return element.childNodes[i]; + } + } } function getHash(url) { - var hashPos = url.lastIndexOf('#'); - return url.substring(hashPos + 1); + var hashPos = url.lastIndexOf('#'); + return url.substring(hashPos + 1); } function toggle(elem) { - elem = document.getElementById(elem); + elem = document.getElementById(elem); - if (elem.style && elem.style['display']) - { - // Only works with the "style" attr - var disp = elem.style['display']; - } - else if (elem.currentStyle) - { - // For MSIE, naturally - var disp = elem.currentStyle['display']; - } - else if (window.getComputedStyle) - { - // For most other browsers - var disp = document.defaultView.getComputedStyle(elem, null).getPropertyValue('display'); - } + if (elem.style && elem.style['display']) + { + // Only works with the "style" attr + var disp = elem.style['display']; + } + else if (elem.currentStyle) + { + // For MSIE, naturally + var disp = elem.currentStyle['display']; + } + else if (window.getComputedStyle) + { + // For most other browsers + var disp = document.defaultView.getComputedStyle(elem, null).getPropertyValue('display'); + } - // Toggle the state of the "display" style - elem.style.display = disp == 'block' ? 'none' : 'block'; + // Toggle the state of the "display" style + elem.style.display = disp == 'block' ? 'none' : 'block'; - return false; + return false; } diff --git a/app/Views/errors/html/error_404.php b/app/Views/errors/html/error_404.php index 1cca20c4..f81717fd 100644 --- a/app/Views/errors/html/error_404.php +++ b/app/Views/errors/html/error_404.php @@ -1,84 +1,84 @@ - - 404 Page Not Found + + 404 Page Not Found - + -
-

404 - File Not Found

+
+

404 - File Not Found

-

- - - - Sorry! Cannot seem to find the page you were looking for. - -

-
+

+ + + + Sorry! Cannot seem to find the page you were looking for. + +

+
diff --git a/app/Views/errors/html/error_exception.php b/app/Views/errors/html/error_exception.php index 693afed4..77e963b2 100644 --- a/app/Views/errors/html/error_exception.php +++ b/app/Views/errors/html/error_exception.php @@ -2,87 +2,87 @@ - - + + - <?= esc($title) ?> - + <?= esc($title) ?> + - + - -
-
-

getCode() ? ' #' . $exception->getCode() : '') ?>

-

- getMessage())) ?> - getMessage())) ?>" - rel="noreferrer" target="_blank">search → -

-
-
- - -
-

at line

- - -
- -
- -
- -
- - - -
- - -
- -
    - $row) : ?> - -
  1. -

    - - - +

    +
    +

    getCode() ? ' #' . $exception->getCode() : '') ?>

    +

    + getMessage())) ?> + getMessage())) ?>" + rel="noreferrer" target="_blank">search → +

    +
    +
    + + +
    +

    at line

    + + +
    + +
    + +
    + +
    + + + +
    + + +
    + +
      + $row) : ?> + +
    1. +

      + + + - - {PHP internal code} - - - - -   —   - - - ( arguments ) -

      - - - + {PHP internal code} + + + + +   —   + + + ( arguments ) +
      +
      + + $value) : ?> - - - - - - -
      name : "#{$key}") ?>
      -
      - - () - - - - -   —   () - -

      - - - -
      - -
      - -
    2. - - -
    - -
    - - -
    - - + name : "#{$key}") ?> +
    + + + + +
    + + () + + + + +   —   () + +

    + + + +
    + +
    + +
  2. + + +
+ +
+ + +
+ + -

$

- - - - - - - - - - $value) : ?> - - - - - - -
KeyValue
- - - -
- -
- - - - - - -

Constants

- - - - - - - - - - $value) : ?> - - - - - - -
KeyValue
- - - -
- -
- -
- - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PathgetUri()) ?>
HTTP MethodgetMethod(true)) ?>
IP AddressgetIPAddress()) ?>
Is AJAX Request?isAJAX() ? 'yes' : 'no' ?>
Is CLI Request?isCLI() ? 'yes' : 'no' ?>
Is Secure Request?isSecure() ? 'yes' : 'no' ?>
User AgentgetUserAgent()->getAgentString()) ?>
- - - - - $ + + + + + + + + + + $value) : ?> + + + + + + +
KeyValue
+ + + +
+ +
+ + + + + + +

Constants

+ + + + + + + + + + $value) : ?> + + + + + + +
KeyValue
+ + + +
+ +
+ +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PathgetUri()) ?>
HTTP MethodgetMethod())) ?>
IP AddressgetIPAddress()) ?>
Is AJAX Request?isAJAX() ? 'yes' : 'no' ?>
Is CLI Request?isCLI() ? 'yes' : 'no' ?>
Is Secure Request?isSecure() ? 'yes' : 'no' ?>
User AgentgetUserAgent()->getAgentString()) ?>
+ + + + + - - -

$

- - - - - - - - - - $value) : ?> - - - - - - -
KeyValue
- - - -
- -
- - - - - -
- No $_GET, $_POST, or $_COOKIE Information to show. -
- - - - getHeaders(); ?> - - -

Headers

- - - - - - - - - - - + +

$

+ +
HeaderValue
+ + + + + + + + $value) : ?> + + + + + + +
KeyValue
+ + + +
+ +
+ + + + + +
+ No $_GET, $_POST, or $_COOKIE Information to show. +
+ + + + getHeaders(); ?> + + +

Headers

+ + + + + + + + + + + - - - - - - - - -
HeaderValue
getName(), 'html') ?>getValueLine(), 'html') ?>
- - -
- - - + + getName(), 'html') ?> + getValueLine(), 'html') ?> + + + + + + + +
+ + + setStatusCode(http_response_code()); ?> -
- - - - - -
Response StatusgetStatusCode() . ' - ' . $response->getReason()) ?>
- - getHeaders(); ?> - - - -

Headers

- - - - - - - - - - $value) : ?> - - - - - - -
HeaderValue
getHeaderLine($name), 'html') ?>
- - -
- - -
- - -
    - -
  1. - -
-
- - -
- - - - - - - - - - - - - - - - -
Memory Usage
Peak Memory Usage:
Memory Limit:
- -
- -
- - - - +
+ + + + + +
Response StatusgetStatusCode() . ' - ' . $response->getReasonPhrase()) ?>
+ + getHeaders(); ?> + + + +

Headers

+ + + + + + + + + + $value) : ?> + + + + + + +
HeaderValue
getHeaderLine($name), 'html') ?>
+ + +
+ + +
+ + +
    + +
  1. + +
+
+ + +
+ + + + + + + + + + + + + + + + +
Memory Usage
Peak Memory Usage:
Memory Limit:
+ +
+ + + + + + diff --git a/app/Views/errors/html/production.php b/app/Views/errors/html/production.php index cca49c2e..9faa4a15 100644 --- a/app/Views/errors/html/production.php +++ b/app/Views/errors/html/production.php @@ -1,24 +1,24 @@ - - + + - Whoops! + Whoops! - + -
+
-

Whoops!

+

Whoops!

-

We seem to have hit a snag. Please try again later...

+

We seem to have hit a snag. Please try again later...

-
+
diff --git a/app/Views/welcome_message.php b/app/Views/welcome_message.php index 9ee2e427..c66a9615 100644 --- a/app/Views/welcome_message.php +++ b/app/Views/welcome_message.php @@ -1,229 +1,229 @@ - - Welcome to CodeIgniter 4! - - - - - - - + + Welcome to CodeIgniter 4! + + + + + + +
- - -
- -

Welcome to CodeIgniter

- -

The small framework with powerful features

- -
+ + +
+ +

Welcome to CodeIgniter

+ +

The small framework with powerful features

+ +
@@ -231,91 +231,91 @@
-

About this page

+

About this page

-

The page you are looking at is being generated dynamically by CodeIgniter.

+

The page you are looking at is being generated dynamically by CodeIgniter.

-

If you would like to edit this page you will find it located at:

+

If you would like to edit this page you will find it located at:

-
app/Views/welcome_message.php
+
app/Views/welcome_message.php
-

The corresponding controller for this page can be found at:

+

The corresponding controller for this page can be found at:

-
app/Controllers/Home.php
+
app/Controllers/Home.php
-
+
-

Go further

+

Go further

-

- - Learn -

+

+ + Learn +

-

The User Guide contains an introduction, tutorial, a number of "how to" - guides, and then reference documentation for the components that make up - the framework. Check the User Guide !

+

The User Guide contains an introduction, tutorial, a number of "how to" + guides, and then reference documentation for the components that make up + the framework. Check the User Guide !

-

- - Discuss -

+

+ + Discuss +

-

CodeIgniter is a community-developed open source project, with several - venues for the community members to gather and exchange ideas. View all - the threads on CodeIgniter's forum, or chat on Slack !

+

CodeIgniter is a community-developed open source project, with several + venues for the community members to gather and exchange ideas. View all + the threads on CodeIgniter's forum, or chat on Slack !

-

- - Contribute -

+

+ + Contribute +

-

CodeIgniter is a community driven project and accepts contributions - of code and documentation from the community. Why not - - join us ?

+

CodeIgniter is a community driven project and accepts contributions + of code and documentation from the community. Why not + + join us ?

-
+
-
+
-

Page rendered in {elapsed_time} seconds

+

Page rendered in {elapsed_time} seconds

-

Environment:

+

Environment:

-
+
-
+
-

© CodeIgniter Foundation. CodeIgniter is open source project released under the MIT - open source licence.

+

© CodeIgniter Foundation. CodeIgniter is open source project released under the MIT + open source licence.

-
+
diff --git a/app/index.html b/app/index.html index b702fbc3..69df4e1d 100644 --- a/app/index.html +++ b/app/index.html @@ -1,7 +1,7 @@ - 403 Forbidden + 403 Forbidden diff --git a/builds b/builds index 0b10a150..cc2ca085 100755 --- a/builds +++ b/builds @@ -39,7 +39,7 @@ if (is_file($file)) { if ($dev) { $array['minimum-stability'] = 'dev'; $array['prefer-stable'] = true; - $array['repositories'] = $array['repositories'] ?? []; + $array['repositories'] ??= []; $found = false; diff --git a/composer.json b/composer.json index 9f7dc492..026329ca 100644 --- a/composer.json +++ b/composer.json @@ -5,8 +5,8 @@ "homepage": "https://codeigniter.com", "license": "MIT", "require": { - "php": "^7.3 || ^8.0", - "codeigniter4/framework": "^4" + "php": "^7.4 || ^8.0", + "codeigniter4/framework": "^4.0" }, "require-dev": { "fakerphp/faker": "^1.9", @@ -17,10 +17,6 @@ "ext-fileinfo": "Improves mime type detection for files" }, "autoload": { - "psr-4": { - "App\\": "app", - "Config\\": "app/Config" - }, "exclude-from-classmap": [ "**/Database/Migrations/**" ] diff --git a/env b/env index c60b3672..67faaee5 100644 --- a/env +++ b/env @@ -21,6 +21,8 @@ #-------------------------------------------------------------------- # app.baseURL = '' +# If you have trouble with `.`, you could also use `_`. +# app_baseURL = '' # app.forceGlobalSecureRequests = false # app.sessionDriver = 'CodeIgniter\Session\Handlers\FileHandler' @@ -60,7 +62,7 @@ # contentsecuritypolicy.scriptSrc = 'self' # contentsecuritypolicy.styleSrc = 'self' # contentsecuritypolicy.imageSrc = 'self' -# contentsecuritypolicy.base_uri = null +# contentsecuritypolicy.baseURI = null # contentsecuritypolicy.childSrc = null # contentsecuritypolicy.connectSrc = 'self' # contentsecuritypolicy.fontSrc = null @@ -73,6 +75,9 @@ # contentsecuritypolicy.reportURI = null # contentsecuritypolicy.sandbox = false # contentsecuritypolicy.upgradeInsecureRequests = false +# contentsecuritypolicy.styleNonceTag = '{csp-style-nonce}' +# contentsecuritypolicy.scriptNonceTag = '{csp-script-nonce}' +# contentsecuritypolicy.autoNonce = true #-------------------------------------------------------------------- # COOKIE diff --git a/public/index.php b/public/index.php index 77373025..51f4be81 100644 --- a/public/index.php +++ b/public/index.php @@ -3,6 +3,9 @@ // Path to the front controller (this file) define('FCPATH', __DIR__ . DIRECTORY_SEPARATOR); +// Ensure the current directory is pointing to the front controller's directory +chdir(FCPATH); + /* *--------------------------------------------------------------- * BOOTSTRAP THE APPLICATION @@ -12,20 +15,34 @@ * and fires up an environment-specific bootstrapping. */ -// Ensure the current directory is pointing to the front controller's directory -chdir(__DIR__); - // Load our paths config file // This is the line that might need to be changed, depending on your folder structure. -$pathsConfig = FCPATH . '../app/Config/Paths.php'; -// ^^^ Change this if you move your application folder -require realpath($pathsConfig) ?: $pathsConfig; +require FCPATH . '../app/Config/Paths.php'; +// ^^^ Change this line if you move your application folder $paths = new Config\Paths(); // Location of the framework bootstrap file. -$bootstrap = rtrim($paths->systemDirectory, '\\/ ') . DIRECTORY_SEPARATOR . 'bootstrap.php'; -$app = require realpath($bootstrap) ?: $bootstrap; +require rtrim($paths->systemDirectory, '\\/ ') . DIRECTORY_SEPARATOR . 'bootstrap.php'; + +// Load environment settings from .env files into $_SERVER and $_ENV +require_once SYSTEMPATH . 'Config/DotEnv.php'; +(new CodeIgniter\Config\DotEnv(ROOTPATH))->load(); + +/* + * --------------------------------------------------------------- + * GRAB OUR CODEIGNITER INSTANCE + * --------------------------------------------------------------- + * + * The CodeIgniter class contains the core functionality to make + * the application run, and does all of the dirty work to get + * the pieces all working together. + */ + +$app = Config\Services::codeigniter(); +$app->initialize(); +$context = is_cli() ? 'php-cli' : 'web'; +$app->setContext($context); /* *--------------------------------------------------------------- @@ -34,4 +51,5 @@ * Now that everything is setup, it's time to actually fire * up the engines and make this app do its thang. */ + $app->run(); diff --git a/spark b/spark index 9a5a5db9..225422aa 100755 --- a/spark +++ b/spark @@ -1,6 +1,15 @@ #!/usr/bin/env php + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + /* * -------------------------------------------------------------------- * CodeIgniter command-line tools @@ -12,8 +21,28 @@ * this class mainly acts as a passthru to the framework itself. */ +// Refuse to run when called from php-cgi +if (strpos(PHP_SAPI, 'cgi') === 0) { + exit("The cli tool is not supported when running php-cgi. It needs php-cli to function!\n\n"); +} + +// We want errors to be shown when using it from the CLI. +error_reporting(-1); +ini_set('display_errors', '1'); + +/** + * @var bool + * + * @deprecated No longer in use. `CodeIgniter` has `$context` property. + */ define('SPARKED', true); +// Path to the front controller +define('FCPATH', __DIR__ . DIRECTORY_SEPARATOR . 'public' . DIRECTORY_SEPARATOR); + +// Ensure the current directory is pointing to the front controller's directory +chdir(FCPATH); + /* *--------------------------------------------------------------- * BOOTSTRAP THE APPLICATION @@ -23,34 +52,28 @@ define('SPARKED', true); * and fires up an environment-specific bootstrapping. */ -// Refuse to run when called from php-cgi -if (strpos(PHP_SAPI, 'cgi') === 0) { - exit("The cli tool is not supported when running php-cgi. It needs php-cli to function!\n\n"); -} - -// Path to the front controller -define('FCPATH', __DIR__ . DIRECTORY_SEPARATOR . 'public' . DIRECTORY_SEPARATOR); - // Load our paths config file -$pathsConfig = 'app/Config/Paths.php'; +// This is the line that might need to be changed, depending on your folder structure. +require FCPATH . '../app/Config/Paths.php'; // ^^^ Change this line if you move your application folder -require realpath($pathsConfig) ?: $pathsConfig; $paths = new Config\Paths(); -// Ensure the current directory is pointing to the front controller's directory -chdir(FCPATH); +// Location of the framework bootstrap file. +require rtrim($paths->systemDirectory, '\\/ ') . DIRECTORY_SEPARATOR . 'bootstrap.php'; + +// Load environment settings from .env files into $_SERVER and $_ENV +require_once SYSTEMPATH . 'Config/DotEnv.php'; +(new CodeIgniter\Config\DotEnv(ROOTPATH))->load(); -$bootstrap = rtrim($paths->systemDirectory, '\\/ ') . DIRECTORY_SEPARATOR . 'bootstrap.php'; -$app = require realpath($bootstrap) ?: $bootstrap; +// Grab our CodeIgniter +$app = Config\Services::codeigniter(); +$app->initialize(); +$app->setContext('spark'); // Grab our Console $console = new CodeIgniter\CLI\Console($app); -// We want errors to be shown when using it from the CLI. -error_reporting(-1); -ini_set('display_errors', '1'); - // Show basic information before we do anything else. if (is_int($suppress = array_search('--no-header', $_SERVER['argv'], true))) { unset($_SERVER['argv'][$suppress]); // @codeCoverageIgnore diff --git a/tests/README.md b/tests/README.md index 1edb1017..9d20661a 100644 --- a/tests/README.md +++ b/tests/README.md @@ -6,13 +6,14 @@ It is not intended to be a full description of the test features that you can use to test your application. Those details can be found in the documentation. ## Resources + * [CodeIgniter 4 User Guide on Testing](https://codeigniter4.github.io/userguide/testing/index.html) -* [PHPUnit docs](https://phpunit.readthedocs.io/en/8.5/index.html) +* [PHPUnit docs](https://phpunit.de/documentation.html) ## Requirements It is recommended to use the latest version of PHPUnit. At the time of this -writing we are running version 8.5.13. Support for this has been built into the +writing we are running version 9.x. Support for this has been built into the **composer.json** file that ships with CodeIgniter and can easily be installed via [Composer](https://getcomposer.org/) if you don't already have it installed globally. @@ -30,8 +31,8 @@ for code coverage to be calculated successfully. A number of the tests use a running database. In order to set up the database edit the details for the `tests` group in **app/Config/Database.php** or **phpunit.xml**. Make sure that you provide a database engine -that is currently running on your machine. More details on a test database setup are in the -*Docs>>Testing>>Testing Your Database* section of the documentation. +that is currently running on your machine. More details on a test database setup are in the +[Testing Your Database](https://codeigniter4.github.io/userguide/testing/database.html) section of the documentation. If you want to run the tests without using live database you can exclude @DatabaseLive group. Or make a copy of **phpunit.dist.xml** - @@ -44,6 +45,10 @@ The entire test suite can be run by simply typing one command-line command from > ./phpunit +If you are using Windows, use the following command. + + > vendor\bin\phpunit + You can limit tests to those within a single test directory by specifying the directory name after phpunit.