From a0d20d71a57db8786a3ad50cf4edbbfc00e4f22b Mon Sep 17 00:00:00 2001 From: kenjis Date: Fri, 29 Mar 2024 13:19:04 +0900 Subject: [PATCH 01/65] docs: add missing SECURITY to v4.4.7 --- CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f9393d2b215..36c47ff5e99a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,18 @@ ## [v4.4.7](https://github.com/codeigniter4/CodeIgniter4/tree/v4.4.7) (2024-03-29) [Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.4.6...v4.4.7) +### SECURITY + +* **Language:** *Language class DoS Vulnerability* was fixed. See the + [Security advisory](https://github.com/codeigniter4/CodeIgniter4/security/advisories/GHSA-39fp-mqmm-gxj6) + for more information. +* **URI Security:** The feature to check if URIs do not contain not permitted + strings has been added. This check is equivalent to the URI Security found in + CodeIgniter 3. This is enabled by default, but upgraded users need to add + a setting to enable it. +* **Filters:** A bug where URI paths processed by Filters were not URL-decoded + has been fixed. + ### Breaking Changes * fix: Time::difference() DST bug by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/8661 From b9179a97c07f3bce19cf6984d306b2554556d33d Mon Sep 17 00:00:00 2001 From: kenjis Date: Fri, 29 Mar 2024 13:58:46 +0900 Subject: [PATCH 02/65] docs: add changelog and upgrade for v4.4.8 --- user_guide_src/source/changelogs/index.rst | 1 + user_guide_src/source/changelogs/v4.4.8.rst | 35 ++++++++++++ .../source/installation/upgrade_448.rst | 54 +++++++++++++++++++ .../source/installation/upgrading.rst | 1 + 4 files changed, 91 insertions(+) create mode 100644 user_guide_src/source/changelogs/v4.4.8.rst create mode 100644 user_guide_src/source/installation/upgrade_448.rst diff --git a/user_guide_src/source/changelogs/index.rst b/user_guide_src/source/changelogs/index.rst index 6d5e51f49210..0f18260929b1 100644 --- a/user_guide_src/source/changelogs/index.rst +++ b/user_guide_src/source/changelogs/index.rst @@ -12,6 +12,7 @@ See all the changes. .. toctree:: :titlesonly: + v4.4.8 v4.4.7 v4.4.6 v4.4.5 diff --git a/user_guide_src/source/changelogs/v4.4.8.rst b/user_guide_src/source/changelogs/v4.4.8.rst new file mode 100644 index 000000000000..5e3eb3d16908 --- /dev/null +++ b/user_guide_src/source/changelogs/v4.4.8.rst @@ -0,0 +1,35 @@ +############# +Version 4.4.8 +############# + +Release Date: Unreleased + +**4.4.8 release of CodeIgniter4** + +.. contents:: + :local: + :depth: 3 + +******** +BREAKING +******** + +*************** +Message Changes +*************** + +******* +Changes +******* + +************ +Deprecations +************ + +********** +Bugs Fixed +********** + +See the repo's +`CHANGELOG.md `_ +for a complete list of bugs fixed. diff --git a/user_guide_src/source/installation/upgrade_448.rst b/user_guide_src/source/installation/upgrade_448.rst new file mode 100644 index 000000000000..580a1ba8edab --- /dev/null +++ b/user_guide_src/source/installation/upgrade_448.rst @@ -0,0 +1,54 @@ +############################# +Upgrading from 4.4.7 to 4.4.8 +############################# + +Please refer to the upgrade instructions corresponding to your installation method. + +- :ref:`Composer Installation App Starter Upgrading ` +- :ref:`Composer Installation Adding CodeIgniter4 to an Existing Project Upgrading ` +- :ref:`Manual Installation Upgrading ` + +.. contents:: + :local: + :depth: 2 + +********************** +Mandatory File Changes +********************** + +**************** +Breaking Changes +**************** + +********************* +Breaking Enhancements +********************* + +************* +Project Files +************* + +Some files in the **project space** (root, app, public, writable) received updates. Due to +these files being outside of the **system** scope they will not be changed without your intervention. + +There are some third-party CodeIgniter modules available to assist with merging changes to +the project space: `Explore on Packagist `_. + +Content Changes +=============== + +The following files received significant changes (including deprecations or visual adjustments) +and it is recommended that you merge the updated versions with your application: + +Config +------ + +- @TODO + +All Changes +=========== + +This is a list of all files in the **project space** that received changes; +many will be simple comments or formatting that have no effect on the runtime: + +- @TODO diff --git a/user_guide_src/source/installation/upgrading.rst b/user_guide_src/source/installation/upgrading.rst index 3b29315d4eab..1ba301af1e33 100644 --- a/user_guide_src/source/installation/upgrading.rst +++ b/user_guide_src/source/installation/upgrading.rst @@ -16,6 +16,7 @@ See also :doc:`./backward_compatibility_notes`. backward_compatibility_notes + upgrade_448 upgrade_447 upgrade_446 upgrade_445 From 5dee40ba0f5c8edcfa142ee035e073f30372298c Mon Sep 17 00:00:00 2001 From: kenjis Date: Fri, 29 Mar 2024 14:06:15 +0900 Subject: [PATCH 03/65] chore: delete release branch if exists If it exists, the git command failed, and the current branch is not changed. --- admin/prepare-release.php | 1 + 1 file changed, 1 insertion(+) diff --git a/admin/prepare-release.php b/admin/prepare-release.php index 374e37b313eb..c0dcab6866ce 100644 --- a/admin/prepare-release.php +++ b/admin/prepare-release.php @@ -26,6 +26,7 @@ function replace_file_content(string $path, string $pattern, string $replace): v // Creates a branch for release. system('git switch develop'); +system('git branch -D release-' . $version); system('git switch -c release-' . $version); // Updates version number in "CodeIgniter.php". From 1079c807d2d9bb7c1946ab9cde549fc028e06ebf Mon Sep 17 00:00:00 2001 From: kenjis Date: Fri, 29 Mar 2024 14:08:46 +0900 Subject: [PATCH 04/65] docs: small update If you replace `4.x.x` with the real version, it will be the real file path. --- admin/RELEASE.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/admin/RELEASE.md b/admin/RELEASE.md index b168f7856354..37173c4e97b0 100644 --- a/admin/RELEASE.md +++ b/admin/RELEASE.md @@ -88,7 +88,7 @@ Work off direct clones of the repos so the release branches persist for a time. > generating much new content. * [ ] Replace **CHANGELOG.md** with the new version generated above -* [ ] Update **user_guide_src/source/changelogs/{version}.rst** +* [ ] Update **user_guide_src/source/changelogs/v4.x.x.rst** * Remove the section titles that have no items * [ ] Update **user_guide_src/source/installation/upgrade_{ver}.rst** * fill in the "All Changes" section, and add it to **upgrading.rst** @@ -150,10 +150,10 @@ Work off direct clones of the repos so the release branches persist for a time. composer test && composer info codeigniter4/framework ``` * [ ] Verify that the user guide actions succeeded: - * "[Deploy Distributable Repos](https://github.com/codeigniter4/CodeIgniter4/actions/workflows/deploy-distributables.yml)", the main repo - * "[Deploy Production](https://github.com/codeigniter4/userguide/actions/workflows/deploy.yml)", UG repo - * "[pages-build-deployment](https://github.com/codeigniter4/userguide/actions/workflows/pages/pages-build-deployment)", UG repo - * Check if "CodeIgniter4.x.x.epub" is added to UG repo. "CodeIgniter.epub" was + * [ ] "[Deploy Distributable Repos](https://github.com/codeigniter4/CodeIgniter4/actions/workflows/deploy-distributables.yml)", the main repo + * [ ] "[Deploy Production](https://github.com/codeigniter4/userguide/actions/workflows/deploy.yml)", UG repo + * [ ] "[pages-build-deployment](https://github.com/codeigniter4/userguide/actions/workflows/pages/pages-build-deployment)", UG repo + * [ ] Check if "CodeIgniter4.x.x.epub" is added to UG repo. "CodeIgniter.epub" was created when v4.3.8 was released. * [ ] Fast-forward `develop` branch to catch the merge commit from `master` ```console From abdf2a881cfecafb02462ca0520be04bd68ae447 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sat, 23 Mar 2024 15:51:57 +0900 Subject: [PATCH 05/65] docs: update OWASP TOP 10 items --- user_guide_src/source/concepts/security.rst | 208 +++++++++++--------- 1 file changed, 110 insertions(+), 98 deletions(-) diff --git a/user_guide_src/source/concepts/security.rst b/user_guide_src/source/concepts/security.rst index df1bd2b34827..bb95623b70d3 100644 --- a/user_guide_src/source/concepts/security.rst +++ b/user_guide_src/source/concepts/security.rst @@ -19,57 +19,79 @@ the CodeIgniter provisions to address the problem. :local: :depth: 1 -************ -A1 Injection -************ +****************************** +A01:2021 Broken Access Control +****************************** -An injection is the inappropriate insertion of partial or complete data via -the input data from the client to the application. Attack vectors include SQL, -XML, ORM, code & buffer overflows. +Insecure Direct Object References occur when an application provides direct +access to objects based on user-supplied input. As a result of this vulnerability +attackers can bypass authorization and access resources in the system directly, +for example database records or files. + +Sensitive data must be protected when it is transmitted through the network. +Such data can include user credentials and credit cards. As a rule of thumb, +if data must be protected when it is stored, it must be protected also during +transmission. + +CSRF is an attack that forces an end user to execute unwanted actions on a web +application in which he/she is currently authenticated. OWASP recommendations ===================== -- Presentation: set correct content type, character set & locale -- Submission: validate fields and provide feedback -- Controller: sanitize input; positive input validation using correct character set -- Model: parameterized queries +- Presentation: don't expose internal data; use random reference maps +- Controller: obtain data from trusted sources or random reference maps +- Model: validate user roles before updating data + +- Presentation: ensure that non-web data is outside the web root; validate users and roles; send CSRF tokens +- Controller: validate users and roles; validate CSRF tokens +- Model: validate roles + +- Presentation: validate users and roles; send CSRF tokens +- Controller: validate users and roles; validate CSRF tokens +- Model: validate roles CodeIgniter provisions ====================== -- :ref:`urls-uri-security` -- :ref:`invalidchars` filter - :doc:`../libraries/validation` library -- :doc:`HTTP library <../incoming/incomingrequest>` provides for :ref:`input field filtering ` & content metadata +- An official authentication and authorization framework :ref:`CodeIgniter Shield ` +- Easy to add third party authentication -********************************************* -A2 Weak authentication and session management -********************************************* +- :ref:`Public ` folder, with application and system outside +- :doc:`Security ` library provides for :ref:`CSRF validation ` -Inadequate authentication or improper session management can lead to a user -getting more privileges than they are entitled to. +******************************* +A02:2021 Cryptographic Failures +******************************* + +Sensitive data must be protected when it is transmitted through the network. +Such data can include user credentials and credit cards. As a rule of thumb, +if data must be protected when it is stored, it must be protected also during +transmission. OWASP recommendations ===================== -- Presentation: validate authentication & role; send CSRF token with forms -- Design: only use built-in session management -- Controller: validate user, role, CSRF token -- Model: validate role -- Tip: consider the use of a request governor +- Presentation: use TLS1.2; use strong ciphers and hashes; do not send keys or hashes to browser +- Controller: use strong ciphers and hashes +- Model: mandate strong encrypted communications with servers CodeIgniter provisions ====================== -- :doc:`Session <../libraries/sessions>` library -- :doc:`Security ` library provides for CSRF validation -- An official authentication and authorization framework :ref:`CodeIgniter Shield ` -- Easy to add third party authentication +- The config for global secure access (``Config\App::$forceGlobalSecureRequests``) +- :php:func:`force_https()` function +- :doc:`../libraries/encryption` +- The :ref:`database config ` (``encrypt``) + +****************** +A03:2021 Injection +****************** -***************************** -A3 Cross Site Scripting (XSS) -***************************** +An injection is the inappropriate insertion of partial or complete data via +the input data from the client to the application. Attack vectors include SQL, +XML, ORM, code & buffer overflows. Insufficient input validation where one user can add content to a web site that can be malicious when viewed by other users to the web site. @@ -77,6 +99,11 @@ that can be malicious when viewed by other users to the web site. OWASP recommendations ===================== +- Presentation: set correct content type, character set & locale +- Submission: validate fields and provide feedback +- Controller: sanitize input; positive input validation using correct character set +- Model: parameterized queries + - Presentation: output encode all user data as per output context; set input constraints - Controller: positive input validation - Tips: only process trustworthy data; do not store data HTML encoded in DB @@ -84,36 +111,34 @@ OWASP recommendations CodeIgniter provisions ====================== +- :ref:`urls-uri-security` +- :ref:`invalidchars` filter +- :doc:`../libraries/validation` library +- :doc:`HTTP library <../incoming/incomingrequest>` provides for :ref:`input field filtering ` & content metadata + - :php:func:`esc()` function - :doc:`../libraries/validation` library - Support for :ref:`content-security-policy` -*********************************** -A4 Insecure Direct Object Reference -*********************************** +************************ +A04:2021 Insecure Design +************************ -Insecure Direct Object References occur when an application provides direct -access to objects based on user-supplied input. As a result of this vulnerability -attackers can bypass authorization and access resources in the system directly, -for example database records or files. +@TODO OWASP recommendations ===================== -- Presentation: don't expose internal data; use random reference maps -- Controller: obtain data from trusted sources or random reference maps -- Model: validate user roles before updating data +- @TODO CodeIgniter provisions ====================== -- :doc:`../libraries/validation` library -- An official authentication and authorization framework :ref:`CodeIgniter Shield ` -- Easy to add third party authentication +- @TODO -**************************** -A5 Security Misconfiguration -**************************** +********************************** +A05:2021 Security Misconfiguration +********************************** Improper configuration of an application architecture can lead to mistakes that might compromise the security of the whole architecture. @@ -130,104 +155,91 @@ CodeIgniter provisions - Sanity checks during bootstrap -************************** -A6 Sensitive Data Exposure -************************** +******************************************* +A06:2021 Vulnerable and Outdated Components +******************************************* -Sensitive data must be protected when it is transmitted through the network. -Such data can include user credentials and credit cards. As a rule of thumb, -if data must be protected when it is stored, it must be protected also during -transmission. +Many applications have known vulnerabilities and known attack strategies that +can be exploited in order to gain remote control or to exploit data. OWASP recommendations ===================== -- Presentation: use TLS1.2; use strong ciphers and hashes; do not send keys or hashes to browser -- Controller: use strong ciphers and hashes -- Model: mandate strong encrypted communications with servers +- Don't use any of these CodeIgniter provisions ====================== -- The config for global secure access (``Config\App::$forceGlobalSecureRequests``) -- :php:func:`force_https()` function -- :doc:`../libraries/encryption` -- The :ref:`database config ` (``encrypt``) +- Third party libraries incorporated must be vetted -**************************************** -A7 Missing Function Level Access Control -**************************************** +*************************************************** +A07:2021 Identification and Authentication Failures +*************************************************** -Sensitive data must be protected when it is transmitted through the network. -Such data can include user credentials and credit cards. As a rule of thumb, -if data must be protected when it is stored, it must be protected also during -transmission. +Inadequate authentication or improper session management can lead to a user +getting more privileges than they are entitled to. OWASP recommendations ===================== -- Presentation: ensure that non-web data is outside the web root; validate users and roles; send CSRF tokens -- Controller: validate users and roles; validate CSRF tokens -- Model: validate roles +- Presentation: validate authentication & role; send CSRF token with forms +- Design: only use built-in session management +- Controller: validate user, role, CSRF token +- Model: validate role +- Tip: consider the use of a request governor CodeIgniter provisions ====================== -- :ref:`Public ` folder, with application and system outside -- :doc:`Security ` library provides for :ref:`CSRF validation ` +- :doc:`Session <../libraries/sessions>` library +- :doc:`Security ` library provides for CSRF validation +- An official authentication and authorization framework :ref:`CodeIgniter Shield ` +- Easy to add third party authentication -************************************ -A8 Cross Site Request Forgery (CSRF) -************************************ +********************************************* +A08:2021 Software and Data Integrity Failures +********************************************* -CSRF is an attack that forces an end user to execute unwanted actions on a web -application in which he/she is currently authenticated. +@TODO OWASP recommendations ===================== -- Presentation: validate users and roles; send CSRF tokens -- Controller: validate users and roles; validate CSRF tokens -- Model: validate roles +- @TODO CodeIgniter provisions ====================== -- :doc:`Security ` library provides for :ref:`CSRF validation ` +- @TODO -********************************************** -A9 Using Components with Known Vulnerabilities -********************************************** +************************************************* +A09:2021 Security Logging and Monitoring Failures +************************************************* -Many applications have known vulnerabilities and known attack strategies that -can be exploited in order to gain remote control or to exploit data. +@TODO OWASP recommendations ===================== -- Don't use any of these +- @TODO CodeIgniter provisions ====================== -- Third party libraries incorporated must be vetted +- @TODO -************************************** -A10 Unvalidated Redirects and Forwards -************************************** +******************************************* +A10:2021 Server-Side Request Forgery (SSRF) +******************************************* -Faulty business logic or injected actionable code could redirect the user -inappropriately. +@TODO OWASP recommendations ===================== -- Presentation: don't use URL redirection; use random indirect references -- Controller: don't use URL redirection; use random indirect references -- Model: validate roles +- @TODO CodeIgniter provisions ====================== -- :doc:`HTTP library <../incoming/incomingrequest>` provides for ... -- :doc:`Session <../libraries/sessions>` library provides :ref:`sessions-flashdata` +- @TODO From 589cf2a808926d99a6dc0f5403ebfba3a1365f14 Mon Sep 17 00:00:00 2001 From: kenjis Date: Thu, 28 Mar 2024 09:48:43 +0900 Subject: [PATCH 06/65] docs: update from A01 to A03 --- user_guide_src/source/concepts/security.rst | 206 +++++++++++++++----- user_guide_src/source/database/queries.rst | 3 + 2 files changed, 159 insertions(+), 50 deletions(-) diff --git a/user_guide_src/source/concepts/security.rst b/user_guide_src/source/concepts/security.rst index bb95623b70d3..930a30cf7b41 100644 --- a/user_guide_src/source/concepts/security.rst +++ b/user_guide_src/source/concepts/security.rst @@ -23,59 +23,140 @@ the CodeIgniter provisions to address the problem. A01:2021 Broken Access Control ****************************** -Insecure Direct Object References occur when an application provides direct -access to objects based on user-supplied input. As a result of this vulnerability -attackers can bypass authorization and access resources in the system directly, -for example database records or files. - -Sensitive data must be protected when it is transmitted through the network. -Such data can include user credentials and credit cards. As a rule of thumb, -if data must be protected when it is stored, it must be protected also during -transmission. - -CSRF is an attack that forces an end user to execute unwanted actions on a web -application in which he/she is currently authenticated. +Access control enforces policy such that users cannot act outside of their intended +permissions. Failures typically lead to unauthorized information disclosure, +modification, or destruction of all data or performing a business function outside +the user's limits. + +Common access control vulnerabilities include: + +- Violation of the principle of least privilege or deny by default, where access + should only be granted for particular capabilities, roles, or users, but is + available to anyone. +- Bypassing access control checks by modifying the URL (parameter tampering or + force browsing), internal application state, or the HTML page, or by using an + attack tool modifying API requests. +- Permitting viewing or editing someone else's account, by providing its unique + identifier (insecure direct object references) +- Accessing API with missing access controls for POST, PUT and DELETE. +- Elevation of privilege. Acting as a user without being logged in or acting as + an admin when logged in as a user. +- Metadata manipulation, such as replaying or tampering with a JSON Web Token (JWT) + access control token, or a cookie or hidden field manipulated to elevate + privileges or abusing JWT invalidation. +- CORS misconfiguration allows API access from unauthorized/untrusted origins. +- Force browsing to authenticated pages as an unauthenticated user or to privileged + pages as a standard user. OWASP recommendations ===================== -- Presentation: don't expose internal data; use random reference maps -- Controller: obtain data from trusted sources or random reference maps -- Model: validate user roles before updating data - -- Presentation: ensure that non-web data is outside the web root; validate users and roles; send CSRF tokens -- Controller: validate users and roles; validate CSRF tokens -- Model: validate roles - -- Presentation: validate users and roles; send CSRF tokens -- Controller: validate users and roles; validate CSRF tokens -- Model: validate roles +Access control is only effective in trusted server-side code or server-less API, +where the attacker cannot modify the access control check or metadata. + +- Except for public resources, deny by default. +- Implement access control mechanisms once and re-use them throughout the application, + including minimizing Cross-Origin Resource Sharing (CORS) usage. +- Model access controls should enforce record ownership rather than accepting that + the user can create, read, update, or delete any record. +- Unique application business limit requirements should be enforced by domain models. +- Disable web server directory listing and ensure file metadata (e.g., .git) and + backup files are not present within web roots. +- Log access control failures, alert admins when appropriate (e.g., repeated failures). +- Rate limit API and controller access to minimize the harm from automated attack + tooling. +- Stateful session identifiers should be invalidated on the server after logout. + Stateless JWT tokens should rather be short-lived so that the window of opportunity + for an attacker is minimized. For longer lived JWTs it's highly recommended to + follow the OAuth standards to revoke access. CodeIgniter provisions ====================== +- :ref:`Public ` folder, with application and system outside - :doc:`../libraries/validation` library +- :doc:`Security ` library provides for :ref:`CSRF protection ` +- :doc:`../libraries/throttler` for rate limit +- :php:func:`log_message()` function for logging - An official authentication and authorization framework :ref:`CodeIgniter Shield ` - Easy to add third party authentication -- :ref:`Public ` folder, with application and system outside -- :doc:`Security ` library provides for :ref:`CSRF validation ` - ******************************* A02:2021 Cryptographic Failures ******************************* -Sensitive data must be protected when it is transmitted through the network. -Such data can include user credentials and credit cards. As a rule of thumb, -if data must be protected when it is stored, it must be protected also during -transmission. +The first thing is to determine the protection needs of data in transit and at +rest. For example, passwords, credit card numbers, health records, personal +information, and business secrets require extra protection, mainly if that data +falls under privacy laws, e.g., EU's General Data Protection Regulation (GDPR), +or regulations, e.g., financial data protection such as PCI Data Security Standard +(PCI DSS). For all such data: + +- Is any data transmitted in clear text? This concerns protocols such as HTTP, + SMTP, FTP also using TLS upgrades like STARTTLS. External internet traffic is + hazardous. Verify all internal traffic, e.g., between load balancers, web servers, + or back-end systems. +- Are any old or weak cryptographic algorithms or protocols used either by default + or in older code? +- Are default crypto keys in use, weak crypto keys generated or re-used, or is + proper key management or rotation missing? Are crypto keys checked into source + code repositories? +- Is encryption not enforced, e.g., are any HTTP headers (browser) security + directives or headers missing? +- Is the received server certificate and the trust chain properly validated? +- Are initialization vectors ignored, reused, or not generated sufficiently secure + for the cryptographic mode of operation? Is an insecure mode of operation such + as ECB in use? Is encryption used when authenticated encryption is more appropriate? +- Are passwords being used as cryptographic keys in absence of a password base key + derivation function? +- Is randomness used for cryptographic purposes that was not designed to meet + cryptographic requirements? Even if the correct function is chosen, does it need + to be seeded by the developer, and if not, has the developer over-written the + strong seeding functionality built into it with a seed that lacks sufficient + entropy/unpredictability? +- Are deprecated hash functions such as MD5 or SHA1 in use, or are non-cryptographic + hash functions used when cryptographic hash functions are needed? +- Are deprecated cryptographic padding methods such as PKCS number 1 v1.5 in use? +- Are cryptographic error messages or side channel information exploitable, for + example in the form of padding oracle attacks? OWASP recommendations ===================== -- Presentation: use TLS1.2; use strong ciphers and hashes; do not send keys or hashes to browser -- Controller: use strong ciphers and hashes -- Model: mandate strong encrypted communications with servers +Do the following, at a minimum, and consult the references: + +- Classify data processed, stored, or transmitted by an application. Identify which + data is sensitive according to privacy laws, regulatory requirements, or business + needs. +- Don't store sensitive data unnecessarily. Discard it as soon as possible or use + PCI DSS compliant tokenization or even truncation. Data that is not retained + cannot be stolen. +- Make sure to encrypt all sensitive data at rest. +- Ensure up-to-date and strong standard algorithms, protocols, and keys are in + place; use proper key management. +- Encrypt all data in transit with secure protocols such as TLS with forward secrecy + (FS) ciphers, cipher prioritization by the server, and secure parameters. Enforce + encryption using directives like HTTP Strict Transport Security (HSTS). +- Disable caching for response that contain sensitive data. +- Apply required security controls as per the data classification. +- Do not use legacy protocols such as FTP and SMTP for transporting sensitive data. +- Store passwords using strong adaptive and salted hashing functions with a work + factor (delay factor), such as Argon2, scrypt, bcrypt or PBKDF2. +- Initialization vectors must be chosen appropriate for the mode of operation. + For many modes, this means using a CSPRNG (cryptographically secure pseudo random + number generator). For modes that require a nonce, then the initialization vector + (IV) does not need a CSPRNG. In all cases, the IV should never be used twice for + a fixed key. +- Always use authenticated encryption instead of just encryption. +- Keys should be generated cryptographically randomly and stored in memory as byte + arrays. If a password is used, then it must be converted to a key via an + appropriate password base key derivation function. +- Ensure that cryptographic randomness is used where appropriate, and that it has + not been seeded in a predictable way or with low entropy. Most modern APIs do + not require the developer to seed the CSPRNG to get security. +- Avoid deprecated cryptographic functions and padding schemes, such as MD5, SHA1, + PKCS number 1 v1.5 . +- Verify independently the effectiveness of configuration and settings. CodeIgniter provisions ====================== @@ -84,29 +165,52 @@ CodeIgniter provisions - :php:func:`force_https()` function - :doc:`../libraries/encryption` - The :ref:`database config ` (``encrypt``) +- An official authentication and authorization framework :ref:`CodeIgniter Shield ` ****************** A03:2021 Injection ****************** -An injection is the inappropriate insertion of partial or complete data via -the input data from the client to the application. Attack vectors include SQL, -XML, ORM, code & buffer overflows. - -Insufficient input validation where one user can add content to a web site -that can be malicious when viewed by other users to the web site. +An application is vulnerable to attack when: + +- User-supplied data is not validated, filtered, or sanitized by the application. +- Dynamic queries or non-parameterized calls without context-aware escaping are + used directly in the interpreter. +- Hostile data is used within object-relational mapping (ORM) search parameters + to extract additional, sensitive records. +- Hostile data is directly used or concatenated. The SQL or command contains the + structure and malicious data in dynamic queries, commands, or stored procedures. + +Some of the more common injections are SQL, NoSQL, OS command, Object Relational +Mapping (ORM), LDAP, and Expression Language (EL) or Object Graph Navigation Library +(OGNL) injection. The concept is identical among all interpreters. Source code +review is the best method of detecting if applications are vulnerable to injections. +Automated testing of all parameters, headers, URL, cookies, JSON, SOAP, and XML +data inputs is strongly encouraged. Organizations can include static (SAST), +dynamic (DAST), and interactive (IAST) application security testing tools into +the CI/CD pipeline to identify introduced injection flaws before production deployment. OWASP recommendations ===================== -- Presentation: set correct content type, character set & locale -- Submission: validate fields and provide feedback -- Controller: sanitize input; positive input validation using correct character set -- Model: parameterized queries - -- Presentation: output encode all user data as per output context; set input constraints -- Controller: positive input validation -- Tips: only process trustworthy data; do not store data HTML encoded in DB +Preventing injection requires keeping data separate from commands and queries: + +- The preferred option is to use a safe API, which avoids using the interpreter + entirely, provides a parameterized interface, or migrates to Object Relational + Mapping Tools (ORMs). + - Note: Even when parameterized, stored procedures can still introduce SQL + injection if PL/SQL or T-SQL concatenates queries and data or executes hostile + data with EXECUTE IMMEDIATE or exec(). +- Use positive server-side input validation. This is not a complete defense as + many applications require special characters, such as text areas or APIs for + mobile applications. +- For any residual dynamic queries, escape special characters using the specific + escape syntax for that interpreter. + - Note: SQL structures such as table names, column names, and so on cannot be + escaped, and thus user-supplied structure names are dangerous. This is a + common issue in report-writing software. +- Use LIMIT and other SQL controls within queries to prevent mass disclosure of + records in case of SQL injection. CodeIgniter provisions ====================== @@ -114,11 +218,13 @@ CodeIgniter provisions - :ref:`urls-uri-security` - :ref:`invalidchars` filter - :doc:`../libraries/validation` library -- :doc:`HTTP library <../incoming/incomingrequest>` provides for :ref:`input field filtering ` & content metadata - - :php:func:`esc()` function -- :doc:`../libraries/validation` library +- :doc:`HTTP library <../incoming/incomingrequest>` provides for + :ref:`input field filtering ` & content metadata - Support for :ref:`content-security-policy` +- :doc:`../database/query_builder` +- :ref:`Database escape methods ` +- :ref:`database-queries-query-bindings` ************************ A04:2021 Insecure Design diff --git a/user_guide_src/source/database/queries.rst b/user_guide_src/source/database/queries.rst index b3904418b8a6..b0c7e6ded6a3 100644 --- a/user_guide_src/source/database/queries.rst +++ b/user_guide_src/source/database/queries.rst @@ -116,6 +116,7 @@ prefixing set ``true`` (boolean) via the second parameter: .. literalinclude:: queries/008.php +.. _database-queries-escaping: *************** Escaping Values @@ -160,6 +161,8 @@ strings are to be used in LIKE conditions so that LIKE wildcards yourself, it cannot automatically add the ``ESCAPE '!'`` condition for you, and so you'll have to manually do that. +.. _database-queries-query-bindings: + ************** Query Bindings ************** From ffce31f8411fa9bac3d24021668d9b5666c047e2 Mon Sep 17 00:00:00 2001 From: kenjis Date: Thu, 28 Mar 2024 10:23:05 +0900 Subject: [PATCH 07/65] docs: update from A04, A05 --- user_guide_src/source/concepts/security.rst | 80 ++++++++++++++++++--- 1 file changed, 71 insertions(+), 9 deletions(-) diff --git a/user_guide_src/source/concepts/security.rst b/user_guide_src/source/concepts/security.rst index 930a30cf7b41..69c84f7f4153 100644 --- a/user_guide_src/source/concepts/security.rst +++ b/user_guide_src/source/concepts/security.rst @@ -230,36 +230,98 @@ CodeIgniter provisions A04:2021 Insecure Design ************************ -@TODO +Insecure design is a broad category representing different weaknesses, expressed +as “missing or ineffective control design.” Insecure design is not the source for +all other Top 10 risk categories. There is a difference between insecure design +and insecure implementation. We differentiate between design flaws and implementation +defects for a reason, they have different root causes and remediation. + +A secure design can still have implementation defects leading to vulnerabilities +that may be exploited. An insecure design cannot be fixed by a perfect implementation +as by definition, needed security controls were never created to defend against +specific attacks. One of the factors that contribute to insecure design is the +lack of business risk profiling inherent in the software or system being developed, +and thus the failure to determine what level of security design is required. OWASP recommendations ===================== -- @TODO +- Establish and use a secure development lifecycle with AppSec professionals to + help evaluate and design security and privacy-related controls +- Establish and use a library of secure design patterns or paved road ready to + use components +- Use threat modeling for critical authentication, access control, business logic, + and key flows +- Integrate security language and controls into user stories +- Integrate plausibility checks at each tier of your application (from frontend + to backend) +- Write unit and integration tests to validate that all critical flows are resistant + to the threat model. Compile use-cases and misuse-cases for each tier of your + application. +- Segregate tier layers on the system and network layers depending on the exposure + and protection needs +- Segregate tenants robustly by design throughout all tiers +- Limit resource consumption by user or service CodeIgniter provisions ====================== -- @TODO +- :doc:`PHPUnit testing <../testing/overview>` +- :doc:`../libraries/throttler` for rate limit +- An official authentication and authorization framework :ref:`CodeIgniter Shield ` ********************************** A05:2021 Security Misconfiguration ********************************** -Improper configuration of an application architecture can lead to mistakes -that might compromise the security of the whole architecture. +The application might be vulnerable if the application is: + +- Missing appropriate security hardening across any part of the application stack + or improperly configured permissions on cloud services. +- Unnecessary features are enabled or installed (e.g., unnecessary ports, services, + pages, accounts, or privileges). +- Default accounts and their passwords are still enabled and unchanged. +- Error handling reveals stack traces or other overly informative error messages + to users. +- For upgraded systems, the latest security features are disabled or not configured + securely. +- The security settings in the application servers, application frameworks (e.g., + Struts, Spring, ASP.NET), libraries, databases, etc., are not set to secure values. +- The server does not send security headers or directives, or they are not set to + secure values. +- The software is out of date or vulnerable (see A06:2021-Vulnerable and Outdated + Components). + +Without a concerted, repeatable application security configuration process, +systems are at a higher risk. OWASP recommendations ===================== -- Presentation: harden web and application servers; use HTTP strict transport security -- Controller: harden web and application servers; protect your XML stack -- Model: harden database servers +Secure installation processes should be implemented, including: + +- A repeatable hardening process makes it fast and easy to deploy another environment + that is appropriately locked down. Development, QA, and production environments + should all be configured identically, with different credentials used in each + environment. This process should be automated to minimize the effort required + to set up a new secure environment. +- A minimal platform without any unnecessary features, components, documentation, + and samples. Remove or do not install unused features and frameworks. +- A task to review and update the configurations appropriate to all security notes, + updates, and patches as part of the patch management process (see A06:2021-Vulnerable + and Outdated Components). Review cloud storage permissions (e.g., S3 bucket permissions). +- A segmented application architecture provides effective and secure separation + between components or tenants, with segmentation, containerization, or cloud + security groups (ACLs). +- Sending security directives to clients, e.g., Security Headers. +- An automated process to verify the effectiveness of the configurations and + settings in all environments. CodeIgniter provisions ====================== -- Sanity checks during bootstrap +- :ref:`Production mode ` by default +- :ref:`secureheaders` filter ******************************************* A06:2021 Vulnerable and Outdated Components From b8025ee538094adf60096010e445a7d52d200870 Mon Sep 17 00:00:00 2001 From: kenjis Date: Thu, 28 Mar 2024 11:04:45 +0900 Subject: [PATCH 08/65] docs: update from A06 to A10 --- user_guide_src/source/concepts/security.rst | 213 +++++++++++++++++--- 1 file changed, 190 insertions(+), 23 deletions(-) diff --git a/user_guide_src/source/concepts/security.rst b/user_guide_src/source/concepts/security.rst index 69c84f7f4153..fc5efaf7f304 100644 --- a/user_guide_src/source/concepts/security.rst +++ b/user_guide_src/source/concepts/security.rst @@ -327,87 +327,254 @@ CodeIgniter provisions A06:2021 Vulnerable and Outdated Components ******************************************* -Many applications have known vulnerabilities and known attack strategies that -can be exploited in order to gain remote control or to exploit data. +You are likely vulnerable: + +- If you do not know the versions of all components you use (both client-side + and server-side). This includes components you directly use as well as nested + dependencies. +- If the software is vulnerable, unsupported, or out of date. This includes the OS, + web/application server, database management system (DBMS), applications, APIs + and all components, runtime environments, and libraries. +- If you do not scan for vulnerabilities regularly and subscribe to security + bulletins related to the components you use. +- If you do not fix or upgrade the underlying platform, frameworks, and dependencies + in a risk-based, timely fashion. This commonly happens in environments when + patching is a monthly or quarterly task under change control, leaving organizations + open to days or months of unnecessary exposure to fixed vulnerabilities. +- If software developers do not test the compatibility of updated, upgraded, or + patched libraries. +- If you do not secure the components’ configurations (see A05:2021-Security + Misconfiguration). OWASP recommendations ===================== -- Don't use any of these +There should be a patch management process in place to: + +- Remove unused dependencies, unnecessary features, components, files, and + documentation. +- Continuously inventory the versions of both client-side and server-side components + (e.g., frameworks, libraries) and their dependencies using tools like versions, + OWASP Dependency Check, retire.js, etc. Continuously monitor sources like Common + Vulnerability and Exposures (CVE) and National Vulnerability Database (NVD) for + vulnerabilities in the components. Use software composition analysis tools to + automate the process. Subscribe to email alerts for security vulnerabilities + related to components you use. +- Only obtain components from official sources over secure links. Prefer signed + packages to reduce the chance of including a modified, malicious component + (See A08:2021-Software and Data Integrity Failures). +- Monitor for libraries and components that are unmaintained or do not create + security patches for older versions. If patching is not possible, consider + deploying a virtual patch to monitor, detect, or protect against the discovered + issue. + +Every organization must ensure an ongoing plan for monitoring, triaging, and +applying updates or configuration changes for the lifetime of the application or +portfolio. CodeIgniter provisions ====================== -- Third party libraries incorporated must be vetted +- Easy :ref:`app-starter-upgrading` by Composer *************************************************** A07:2021 Identification and Authentication Failures *************************************************** -Inadequate authentication or improper session management can lead to a user -getting more privileges than they are entitled to. +Confirmation of the user's identity, authentication, and session management is +critical to protect against authentication-related attacks. There may be +authentication weaknesses if the application: + +- Permits automated attacks such as credential stuffing, where the attacker has + a list of valid usernames and passwords. +- Permits brute force or other automated attacks. +- Permits default, weak, or well-known passwords, such as "Password1" or "admin/admin". +- Uses weak or ineffective credential recovery and forgot-password processes, + such as "knowledge-based answers," which cannot be made safe. +- Uses plain text, encrypted, or weakly hashed passwords data stores + (see A02:2021-Cryptographic Failures). +- Has missing or ineffective multi-factor authentication. +- Exposes session identifier in the URL. +- Reuse session identifier after successful login. +- Does not correctly invalidate Session IDs. User sessions or authentication tokens + (mainly single sign-on (SSO) tokens) aren't properly invalidated during logout + or a period of inactivity. OWASP recommendations ===================== -- Presentation: validate authentication & role; send CSRF token with forms -- Design: only use built-in session management -- Controller: validate user, role, CSRF token -- Model: validate role -- Tip: consider the use of a request governor +- Where possible, implement multi-factor authentication to prevent automated + credential stuffing, brute force, and stolen credential reuse attacks. +- Do not ship or deploy with any default credentials, particularly for admin users. +- Implement weak password checks, such as testing new or changed passwords against + the top 10,000 worst passwords list. +- Align password length, complexity, and rotation policies with National Institute + of Standards and Technology (NIST) 800-63b's guidelines in section 5.1.1 for + Memorized Secrets or other modern, evidence-based password policies. +- Ensure registration, credential recovery, and API pathways are hardened against + account enumeration attacks by using the same messages for all outcomes. +- Limit or increasingly delay failed login attempts, but be careful not to create + a denial of service scenario. Log all failures and alert administrators when + credential stuffing, brute force, or other attacks are detected. +- Use a server-side, secure, built-in session manager that generates a new random + session ID with high entropy after login. Session identifier should not be in + the URL, be securely stored, and invalidated after logout, idle, and absolute + timeouts. CodeIgniter provisions ====================== - :doc:`Session <../libraries/sessions>` library -- :doc:`Security ` library provides for CSRF validation -- An official authentication and authorization framework :ref:`CodeIgniter Shield ` -- Easy to add third party authentication +- An official authentication and authorization framework + :ref:`CodeIgniter Shield ` ********************************************* A08:2021 Software and Data Integrity Failures ********************************************* -@TODO +Software and data integrity failures relate to code and infrastructure that does +not protect against integrity violations. An example of this is where an application +relies upon plugins, libraries, or modules from untrusted sources, repositories, +and content delivery networks (CDNs). An insecure CI/CD pipeline can introduce +the potential for unauthorized access, malicious code, or system compromise. + +Lastly, many applications now include auto-update functionality, where updates +are downloaded without sufficient integrity verification and applied to the previously +trusted application. Attackers could potentially upload their own updates to be +distributed and run on all installations. + +Another example is where objects or data are encoded or serialized into a structure +that an attacker can see and modify is vulnerable to insecure deserialization. OWASP recommendations ===================== -- @TODO +- Use digital signatures or similar mechanisms to verify the software or data is + from the expected source and has not been altered. +- Ensure libraries and dependencies, such as npm or Maven, are consuming trusted + repositories. If you have a higher risk profile, consider hosting an internal + known-good repository that's vetted. +- Ensure that a software supply chain security tool, such as OWASP Dependency + Check or OWASP CycloneDX, is used to verify that components do not contain + known vulnerabilities +- Ensure that there is a review process for code and configuration changes to + minimize the chance that malicious code or configuration could be introduced + into your software pipeline. +- Ensure that your CI/CD pipeline has proper segregation, configuration, and + access control to ensure the integrity of the code flowing through the build + and deploy processes. +- Ensure that unsigned or unencrypted serialized data is not sent to untrusted + clients without some form of integrity check or digital signature to detect + tampering or replay of the serialized data CodeIgniter provisions ====================== -- @TODO +- n/a ************************************************* A09:2021 Security Logging and Monitoring Failures ************************************************* -@TODO +This category is to help detect, escalate, and respond to active breaches. Without +logging and monitoring, breaches cannot be detected. Insufficient logging, detection, +monitoring, and active response occurs any time: + +- Auditable events, such as logins, failed logins, and high-value transactions, + are not logged. +- Warnings and errors generate no, inadequate, or unclear log messages. +- Logs of applications and APIs are not monitored for suspicious activity. +- Logs are only stored locally. +- Appropriate alerting thresholds and response escalation processes are not in + place or effective. +- Penetration testing and scans by dynamic application security testing (DAST) + tools (such as OWASP ZAP) do not trigger alerts. +- The application cannot detect, escalate, or alert for active attacks in real-time + or near real-time. + +You are vulnerable to information leakage by making logging and alerting events +visible to a user or an attacker (see A01:2021-Broken Access Control). OWASP recommendations ===================== -- @TODO +Developers should implement some or all the following controls, depending on the risk of the application: + +- Ensure all login, access control, and server-side input validation failures can + be logged with sufficient user context to identify suspicious or malicious + accounts and held for enough time to allow delayed forensic analysis. +- Ensure that logs are generated in a format that log management solutions can + easily consume. +- Ensure log data is encoded correctly to prevent injections or attacks on the + logging or monitoring systems. +- Ensure high-value transactions have an audit trail with integrity controls to + prevent tampering or deletion, such as append-only database tables or similar. +- DevSecOps teams should establish effective monitoring and alerting such that + suspicious activities are detected and responded to quickly. +- Establish or adopt an incident response and recovery plan, such as National + Institute of Standards and Technology (NIST) 800-61r2 or later. + +There are commercial and open-source application protection frameworks such as +the OWASP ModSecurity Core Rule Set, and open-source log correlation software, +such as the Elasticsearch, Logstash, Kibana (ELK) stack, that feature custom +dashboards and alerting. CodeIgniter provisions ====================== -- @TODO +- :doc:`Logging <../general/logging>` library +- An official authentication and authorization framework + :ref:`CodeIgniter Shield ` ******************************************* A10:2021 Server-Side Request Forgery (SSRF) ******************************************* -@TODO +SSRF flaws occur whenever a web application is fetching a remote resource without +validating the user-supplied URL. It allows an attacker to coerce the application +to send a crafted request to an unexpected destination, even when protected by a +firewall, VPN, or another type of network access control list (ACL). + +As modern web applications provide end-users with convenient features, fetching +a URL becomes a common scenario. As a result, the incidence of SSRF is increasing. +Also, the severity of SSRF is becoming higher due to cloud services and the +complexity of architectures. OWASP recommendations ===================== -- @TODO +Developers can prevent SSRF by implementing some or all the following defense in +depth controls: + +From Network layer: + +- Segment remote resource access functionality in separate networks to reduce the + impact of SSRF +- Enforce “deny by default” firewall policies or network access control rules to + block all but essential intranet traffic. + + - Hints: + + * Establish an ownership and a lifecycle for firewall rules based on + applications. + * Log all accepted and blocked network flows on firewalls + (see A09:2021-Security Logging and Monitoring Failures). + +From Application layer: + +- Sanitize and validate all client-supplied input data +- Enforce the URL schema, port, and destination with a positive allow list +- Do not send raw responses to clients +- Disable HTTP redirections +- Be aware of the URL consistency to avoid attacks such as DNS rebinding and + “time of check, time of use” (TOCTOU) race conditions + +Do not mitigate SSRF via the use of a deny list or regular expression. Attackers +have payload lists, tools, and skills to bypass deny lists. CodeIgniter provisions ====================== -- @TODO +- :doc:`../libraries/validation` library +- :doc:`HTTP library <../incoming/incomingrequest>` provides for + :ref:`input field filtering ` From d3ed47c1c165c0000516c31557394ac9ba7aaa42 Mon Sep 17 00:00:00 2001 From: kenjis Date: Thu, 28 Mar 2024 11:05:11 +0900 Subject: [PATCH 09/65] docs: fix RST format --- user_guide_src/source/concepts/security.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/user_guide_src/source/concepts/security.rst b/user_guide_src/source/concepts/security.rst index fc5efaf7f304..faada4d93cec 100644 --- a/user_guide_src/source/concepts/security.rst +++ b/user_guide_src/source/concepts/security.rst @@ -198,6 +198,7 @@ Preventing injection requires keeping data separate from commands and queries: - The preferred option is to use a safe API, which avoids using the interpreter entirely, provides a parameterized interface, or migrates to Object Relational Mapping Tools (ORMs). + - Note: Even when parameterized, stored procedures can still introduce SQL injection if PL/SQL or T-SQL concatenates queries and data or executes hostile data with EXECUTE IMMEDIATE or exec(). @@ -206,6 +207,7 @@ Preventing injection requires keeping data separate from commands and queries: mobile applications. - For any residual dynamic queries, escape special characters using the specific escape syntax for that interpreter. + - Note: SQL structures such as table names, column names, and so on cannot be escaped, and thus user-supplied structure names are dangerous. This is a common issue in report-writing software. From 0ea008b32a4958a42c91c4ff1470f857b099bbe9 Mon Sep 17 00:00:00 2001 From: kenjis Date: Thu, 28 Mar 2024 11:05:58 +0900 Subject: [PATCH 10/65] docs: break long lines --- user_guide_src/source/concepts/security.rst | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/user_guide_src/source/concepts/security.rst b/user_guide_src/source/concepts/security.rst index faada4d93cec..b4a522fdf293 100644 --- a/user_guide_src/source/concepts/security.rst +++ b/user_guide_src/source/concepts/security.rst @@ -73,9 +73,11 @@ where the attacker cannot modify the access control check or metadata. CodeIgniter provisions ====================== -- :ref:`Public ` folder, with application and system outside +- :ref:`Public ` folder, with application and system + outside - :doc:`../libraries/validation` library -- :doc:`Security ` library provides for :ref:`CSRF protection ` +- :doc:`Security ` library provides for + :ref:`CSRF protection ` - :doc:`../libraries/throttler` for rate limit - :php:func:`log_message()` function for logging - An official authentication and authorization framework :ref:`CodeIgniter Shield ` @@ -165,7 +167,8 @@ CodeIgniter provisions - :php:func:`force_https()` function - :doc:`../libraries/encryption` - The :ref:`database config ` (``encrypt``) -- An official authentication and authorization framework :ref:`CodeIgniter Shield ` +- An official authentication and authorization framework + :ref:`CodeIgniter Shield ` ****************** A03:2021 Injection From 9c6fec3a7b99064514d0738cde0541f0a3b04744 Mon Sep 17 00:00:00 2001 From: kenjis Date: Thu, 28 Mar 2024 11:06:46 +0900 Subject: [PATCH 11/65] docs: remove "content metadata" I don't know what it means in this context. --- user_guide_src/source/concepts/security.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/concepts/security.rst b/user_guide_src/source/concepts/security.rst index b4a522fdf293..327bb715404c 100644 --- a/user_guide_src/source/concepts/security.rst +++ b/user_guide_src/source/concepts/security.rst @@ -225,7 +225,7 @@ CodeIgniter provisions - :doc:`../libraries/validation` library - :php:func:`esc()` function - :doc:`HTTP library <../incoming/incomingrequest>` provides for - :ref:`input field filtering ` & content metadata + :ref:`input field filtering ` - Support for :ref:`content-security-policy` - :doc:`../database/query_builder` - :ref:`Database escape methods ` From c707e9a3093c506a876c5e2a4a155924dd20c8a4 Mon Sep 17 00:00:00 2001 From: kenjis Date: Thu, 28 Mar 2024 12:53:43 +0900 Subject: [PATCH 12/65] docs: update title of link --- user_guide_src/source/concepts/security.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/concepts/security.rst b/user_guide_src/source/concepts/security.rst index 327bb715404c..69f2221a644f 100644 --- a/user_guide_src/source/concepts/security.rst +++ b/user_guide_src/source/concepts/security.rst @@ -10,7 +10,7 @@ We respect the `Open Web Application Security Project (OWASP) `_, +`OWASP Top Ten `_, identifying the top vulnerabilities for web applications. For each, we provide a brief description, the OWASP recommendations, and then the CodeIgniter provisions to address the problem. From f54cdde5aaf18aaed5241ae948d1f3bead39e987 Mon Sep 17 00:00:00 2001 From: kenjis Date: Fri, 29 Mar 2024 22:22:12 +0900 Subject: [PATCH 13/65] docs: fix PHPDoc types in BaseModel --- phpstan-baseline.php | 45 -------------------------------------------- system/BaseModel.php | 9 ++++++--- 2 files changed, 6 insertions(+), 48 deletions(-) diff --git a/phpstan-baseline.php b/phpstan-baseline.php index 3380da0b8733..c990137c437c 100644 --- a/phpstan-baseline.php +++ b/phpstan-baseline.php @@ -226,11 +226,6 @@ 'count' => 1, 'path' => __DIR__ . '/system/BaseModel.php', ]; -$ignoreErrors[] = [ - 'message' => '#^Method CodeIgniter\\\\BaseModel\\:\\:setValidationRules\\(\\) has parameter \\$validationRules with no value type specified in iterable type array\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/BaseModel.php', -]; $ignoreErrors[] = [ 'message' => '#^Method CodeIgniter\\\\BaseModel\\:\\:transformDataToArray\\(\\) return type has no value type specified in iterable type array\\.$#', 'count' => 1, @@ -11391,16 +11386,6 @@ 'count' => 1, 'path' => __DIR__ . '/tests/_support/Models/UserModel.php', ]; -$ignoreErrors[] = [ - 'message' => '#^Property Tests\\\\Support\\\\Models\\\\ValidErrorsModel\\:\\:\\$validationRules \\(array\\\\|string\\) does not accept default value of type array\\\\|string\\>\\|string\\>\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/tests/_support/Models/ValidErrorsModel.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Property Tests\\\\Support\\\\Models\\\\ValidModel\\:\\:\\$validationRules \\(array\\\\|string\\) does not accept default value of type array\\\\|string\\>\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/tests/_support/Models/ValidModel.php', -]; $ignoreErrors[] = [ 'message' => '#^Property Tests\\\\Support\\\\SomeEntity\\:\\:\\$attributes type has no value type specified in iterable type array\\.$#', 'count' => 1, @@ -16301,41 +16286,11 @@ 'count' => 1, 'path' => __DIR__ . '/tests/system/Models/UpdateModelTest.php', ]; -$ignoreErrors[] = [ - 'message' => '#^Property class@anonymous/tests/system/Models/ValidationModelRuleGroupTest\\.php\\:368\\:\\:\\$validationRules \\(array\\\\|string\\) does not accept default value of type array\\\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/tests/system/Models/ValidationModelRuleGroupTest.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Property class@anonymous/tests/system/Models/ValidationModelRuleGroupTest\\.php\\:406\\:\\:\\$validationRules \\(array\\\\|string\\) does not accept default value of type array\\\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/tests/system/Models/ValidationModelRuleGroupTest.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Property class@anonymous/tests/system/Models/ValidationModelRuleGroupTest\\.php\\:446\\:\\:\\$validationRules \\(array\\\\|string\\) does not accept default value of type array\\\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/tests/system/Models/ValidationModelRuleGroupTest.php', -]; $ignoreErrors[] = [ 'message' => '#^Property class@anonymous/tests/system/Models/ValidationModelTest\\.php\\:243\\:\\:\\$grouptest has no type specified\\.$#', 'count' => 1, 'path' => __DIR__ . '/tests/system/Models/ValidationModelTest.php', ]; -$ignoreErrors[] = [ - 'message' => '#^Property class@anonymous/tests/system/Models/ValidationModelTest\\.php\\:380\\:\\:\\$validationRules \\(array\\\\|string\\) does not accept default value of type array\\\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/tests/system/Models/ValidationModelTest.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Property class@anonymous/tests/system/Models/ValidationModelTest\\.php\\:418\\:\\:\\$validationRules \\(array\\\\|string\\) does not accept default value of type array\\\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/tests/system/Models/ValidationModelTest.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Property class@anonymous/tests/system/Models/ValidationModelTest\\.php\\:458\\:\\:\\$validationRules \\(array\\\\|string\\) does not accept default value of type array\\\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/tests/system/Models/ValidationModelTest.php', -]; $ignoreErrors[] = [ 'message' => '#^Cannot access property \\$key on array\\.$#', 'count' => 7, diff --git a/system/BaseModel.php b/system/BaseModel.php index 24479ec69362..9b085efa8400 100644 --- a/system/BaseModel.php +++ b/system/BaseModel.php @@ -173,11 +173,14 @@ abstract class BaseModel protected $db; /** - * Rules used to validate data in insert, update, and save methods. + * Rules used to validate data in insert(), update(), and save() methods. + * * The array must match the format of data passed to the Validation * library. * - * @var list|string + * @see https://codeigniter4.github.io/userguide/models/model.html#setting-validation-rules + * + * @var array|string>|string>|string */ protected $validationRules = []; @@ -1448,7 +1451,7 @@ public function setValidationMessage(string $field, array $fieldMessages) * Allows to set (and reset) validation rules. * It could be used when you have to change default or override current validate rules. * - * @param array $validationRules Value + * @param array|string>|string> $validationRules Value * * @return $this */ From d272bfe1e4060be71b3da2917bb7517eb6b06fab Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Fri, 29 Mar 2024 23:37:47 +0800 Subject: [PATCH 14/65] refactor: simplify ImageMagickHandler::getVersion() --- system/Images/Handlers/ImageMagickHandler.php | 8 +++----- tests/system/Images/ImageMagickHandlerTest.php | 9 ++++----- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/system/Images/Handlers/ImageMagickHandler.php b/system/Images/Handlers/ImageMagickHandler.php index cb8ce9bae763..0291fe784740 100644 --- a/system/Images/Handlers/ImageMagickHandler.php +++ b/system/Images/Handlers/ImageMagickHandler.php @@ -167,12 +167,10 @@ protected function _flip(string $direction) */ public function getVersion(): string { - $result = $this->process('-version'); + $versionString = $this->process('-version')[0]; + preg_match('/ImageMagick\s(?P[\S]+)/', $versionString, $matches); - // The first line has the version in it... - preg_match('/(ImageMagick\s[\S]+)/', $result[0], $matches); - - return str_replace('ImageMagick ', '', $matches[0]); + return $matches['version']; } /** diff --git a/tests/system/Images/ImageMagickHandlerTest.php b/tests/system/Images/ImageMagickHandlerTest.php index b313fef96275..e02836693b44 100644 --- a/tests/system/Images/ImageMagickHandlerTest.php +++ b/tests/system/Images/ImageMagickHandlerTest.php @@ -80,11 +80,10 @@ protected function setUp(): void public function testGetVersion(): void { $version = $this->handler->getVersion(); - // make sure that the call worked - $this->assertNotFalse($version); - // we should have a numeric version, greater than 6 - $this->assertGreaterThanOrEqual(0, version_compare($version, '6.0.0')); - $this->assertLessThan(0, version_compare($version, '99.0.0')); + + $this->assertNotSame('', $version); + $this->assertTrue(version_compare($version, '6.0.0', '>')); + $this->assertTrue(version_compare($version, '99.0.0', '<')); } public function testImageProperties(): void From 2c7d445fff5168271fe9fb301c56d02219dd7e53 Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Fri, 29 Mar 2024 23:16:09 +0800 Subject: [PATCH 15/65] fix: early terminate processing of invalid library path --- phpstan-baseline.php | 2 +- system/Images/Handlers/ImageMagickHandler.php | 27 ++++++++++-------- .../system/Images/ImageMagickHandlerTest.php | 28 +++++++++++++++++++ 3 files changed, 45 insertions(+), 12 deletions(-) diff --git a/phpstan-baseline.php b/phpstan-baseline.php index 3380da0b8733..356871c8f251 100644 --- a/phpstan-baseline.php +++ b/phpstan-baseline.php @@ -7723,7 +7723,7 @@ ]; $ignoreErrors[] = [ 'message' => '#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#', - 'count' => 9, + 'count' => 8, 'path' => __DIR__ . '/system/Images/Handlers/ImageMagickHandler.php', ]; $ignoreErrors[] = [ diff --git a/system/Images/Handlers/ImageMagickHandler.php b/system/Images/Handlers/ImageMagickHandler.php index cb8ce9bae763..4b5e4fe6ad20 100644 --- a/system/Images/Handlers/ImageMagickHandler.php +++ b/system/Images/Handlers/ImageMagickHandler.php @@ -32,8 +32,6 @@ class ImageMagickHandler extends BaseHandler protected $resource; /** - * Constructor. - * * @param Images $config * * @throws ImageException @@ -45,6 +43,22 @@ public function __construct($config = null) if (! (extension_loaded('imagick') || class_exists(Imagick::class))) { throw ImageException::forMissingExtension('IMAGICK'); // @codeCoverageIgnore } + + $cmd = $this->config->libraryPath; + + if ($cmd === '') { + throw ImageException::forInvalidImageLibraryPath($cmd); + } + + if (preg_match('/convert$/i', $cmd) !== 1) { + $cmd = rtrim($cmd, '\/') . '/convert'; + + $this->config->libraryPath = $cmd; + } + + if (! is_file($cmd)) { + throw ImageException::forInvalidImageLibraryPath($cmd); + } } /** @@ -184,19 +198,10 @@ public function getVersion(): string */ protected function process(string $action, int $quality = 100): array { - // Do we have a vaild library path? - if (empty($this->config->libraryPath)) { - throw ImageException::forInvalidImageLibraryPath($this->config->libraryPath); - } - if ($action !== '-version') { $this->supportedFormatCheck(); } - if (! preg_match('/convert$/i', $this->config->libraryPath)) { - $this->config->libraryPath = rtrim($this->config->libraryPath, '/') . '/convert'; - } - $cmd = $this->config->libraryPath; $cmd .= $action === '-version' ? ' ' . $action : ' -quality ' . $quality . ' ' . $action; diff --git a/tests/system/Images/ImageMagickHandlerTest.php b/tests/system/Images/ImageMagickHandlerTest.php index b313fef96275..29ed56597175 100644 --- a/tests/system/Images/ImageMagickHandlerTest.php +++ b/tests/system/Images/ImageMagickHandlerTest.php @@ -14,6 +14,7 @@ use CodeIgniter\Config\Services; use CodeIgniter\Images\Exceptions\ImageException; use CodeIgniter\Images\Handlers\BaseHandler; +use CodeIgniter\Images\Handlers\ImageMagickHandler; use CodeIgniter\Test\CIUnitTestCase; use Config\Images; use Imagick; @@ -77,6 +78,33 @@ protected function setUp(): void $this->handler = Services::image('imagick', $config, false); } + /** + * @dataProvider provideNonexistentLibraryPathTerminatesProcessing + */ + public function testNonexistentLibraryPathTerminatesProcessing(string $path, string $invalidPath): void + { + $this->expectException(ImageException::class); + $this->expectExceptionMessage(lang('Images.libPathInvalid', [$invalidPath])); + + $config = new Images(); + + $config->libraryPath = $path; + + new ImageMagickHandler($config); + } + + /** + * @return iterable> + */ + public static function provideNonexistentLibraryPathTerminatesProcessing(): iterable + { + yield 'empty string' => ['', '']; + + yield 'invalid file' => ['/var/log/convert', '/var/log/convert']; + + yield 'nonexistent file' => ['/var/www/file', '/var/www/file/convert']; + } + public function testGetVersion(): void { $version = $this->handler->getVersion(); From 24b94ee6529196aa45a98fa68c39e853d717ca36 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sat, 30 Mar 2024 21:07:15 +0900 Subject: [PATCH 16/65] docs: add empty line --- user_guide_src/source/libraries/images.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/user_guide_src/source/libraries/images.rst b/user_guide_src/source/libraries/images.rst index 0507c5d36529..409e40707ec8 100644 --- a/user_guide_src/source/libraries/images.rst +++ b/user_guide_src/source/libraries/images.rst @@ -49,6 +49,7 @@ Regardless of the type of processing you would like to perform (resizing, cropping, rotation, or watermarking), the general process is identical. You will set some preferences corresponding to the action you intend to perform, then call one of the available processing functions. + For example, to create an image thumbnail you'll do this: .. literalinclude:: images/003.php From c691ecb4d3cc2b565ac7c42292aa6a008e9f1cdb Mon Sep 17 00:00:00 2001 From: kenjis Date: Sat, 30 Mar 2024 21:07:36 +0900 Subject: [PATCH 17/65] docs: update description --- user_guide_src/source/libraries/images.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/user_guide_src/source/libraries/images.rst b/user_guide_src/source/libraries/images.rst index 409e40707ec8..4afdd2acd049 100644 --- a/user_guide_src/source/libraries/images.rst +++ b/user_guide_src/source/libraries/images.rst @@ -55,9 +55,9 @@ For example, to create an image thumbnail you'll do this: .. literalinclude:: images/003.php The above code tells the library to look for an image -called *mypic.jpg* located in the source_image folder, then create a -new image from it that is 100 x 100pixels using the GD2 image_library, -and save it to a new file (the thumb). Since it is using the ``fit()`` method, +called **mypic.jpg** located in the **/path/to/image** folder, then create a +new image from it that is 100 x 100 pixels, +and save it to a new file **mypic_thumb.jpg**. Since it is using the ``fit()`` method, it will attempt to find the best portion of the image to crop based on the desired aspect ratio, and then crop and resize the result. From fad2cde0f30e8bd319b135bdda08d35bca2fd737 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sat, 30 Mar 2024 21:08:25 +0900 Subject: [PATCH 18/65] docs: add spaces --- user_guide_src/source/libraries/images.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/user_guide_src/source/libraries/images.rst b/user_guide_src/source/libraries/images.rst index 4afdd2acd049..0bbb167ecc9b 100644 --- a/user_guide_src/source/libraries/images.rst +++ b/user_guide_src/source/libraries/images.rst @@ -69,7 +69,7 @@ previous results: .. literalinclude:: images/004.php This example would take the same image and first fix any mobile phone orientation issues, -rotate the image by 90 degrees, and then crop the result into a 100x100 pixel image, +rotate the image by 90 degrees, and then crop the result into a 100 x 100 pixel image, starting at the top left corner. The result would be saved as the thumbnail. .. note:: In order for the image class to be allowed to do any @@ -136,7 +136,7 @@ thumbnail images that should match a certain size/aspect ratio. This is handled - ``$maintainRatio`` will, if true, adjust the final dimensions as needed to maintain the image's original aspect ratio. - ``$masterDim`` specifies which dimension should be left untouched when ``$maintainRatio`` is true. Values can be: ``'width'``, ``'height'``, or ``'auto'``. -To take a 50x50 pixel square out of the center of an image, you would need to first calculate the appropriate x and y +To take a 50 x 50 pixel square out of the center of an image, you would need to first calculate the appropriate x and y offset values: .. literalinclude:: images/008.php From b2a1a8acf78dde2c9c6341300585254b1f911abe Mon Sep 17 00:00:00 2001 From: kenjis Date: Sat, 30 Mar 2024 21:10:03 +0900 Subject: [PATCH 19/65] docs: decorate argument values --- user_guide_src/source/libraries/images.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/user_guide_src/source/libraries/images.rst b/user_guide_src/source/libraries/images.rst index 0bbb167ecc9b..e4bbfd816467 100644 --- a/user_guide_src/source/libraries/images.rst +++ b/user_guide_src/source/libraries/images.rst @@ -252,17 +252,17 @@ that allow you to specify how the text should be displayed: The possible options that are recognized are as follows: -- ``color`` Text Color (hex number), i.e., #ff0000 -- ``opacity`` A number between 0 and 1 that represents the opacity of the text. +- ``color`` Text Color (hex number), i.e., ``'#ff0000'`` +- ``opacity`` A number between ``0`` and ``1`` that represents the opacity of the text. - ``withShadow`` Boolean value whether to display a shadow or not. - ``shadowColor`` Color of the shadow (hex number) - ``shadowOffset`` How many pixels to offset the shadow. Applies to both the vertical and horizontal values. -- ``hAlign`` Horizontal alignment: left, center, right -- ``vAlign`` Vertical alignment: top, middle, bottom +- ``hAlign`` Horizontal alignment: ``'left'``, ``'center'``, ``'right'`` +- ``vAlign`` Vertical alignment: ``'top'``, ``'middle'``, ``'bottom'`` - ``hOffset`` Additional offset on the x axis, in pixels - ``vOffset`` Additional offset on the y axis, in pixels - ``fontPath`` The full server path to the TTF font you wish to use. System font will be used if none is given. -- ``fontSize`` The font size to use. When using the GD handler with the system font, valid values are between 1-5. +- ``fontSize`` The font size to use. When using the GD handler with the system font, valid values are between ``1`` to ``5``. .. note:: The ImageMagick driver does not recognize full server path for fontPath. Instead, simply provide the name of one of the installed system fonts that you wish to use, i.e., Calibri. From be473e2924fbba0761ec8583732a352eb49de69b Mon Sep 17 00:00:00 2001 From: kenjis Date: Sat, 30 Mar 2024 21:10:34 +0900 Subject: [PATCH 20/65] docs: add missing `\` --- user_guide_src/source/libraries/images.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/libraries/images.rst b/user_guide_src/source/libraries/images.rst index e4bbfd816467..30537832cadd 100644 --- a/user_guide_src/source/libraries/images.rst +++ b/user_guide_src/source/libraries/images.rst @@ -232,7 +232,7 @@ The ``rotate()`` method allows you to rotate an image in 90 degree increments:: - ``$angle`` is the number of degrees to rotate. One of ``90``, ``180``, ``270``. .. note:: While the ``$angle`` parameter accepts a float, it will convert it to an integer during the process. - If the value is any other than the three values listed above, it will throw a CodeIgniter\Images\ImageException. + If the value is any other than the three values listed above, it will throw a ``CodeIgniter\Images\ImageException``. Adding a Text Watermark ======================= From c729ffd3ae28a1fdfcf34dbc7fe4e94ffa96fce9 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sat, 30 Mar 2024 21:11:23 +0900 Subject: [PATCH 21/65] docs: replace WEBP with WebP --- user_guide_src/source/libraries/images.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/libraries/images.rst b/user_guide_src/source/libraries/images.rst index 30537832cadd..08efccfe92f9 100644 --- a/user_guide_src/source/libraries/images.rst +++ b/user_guide_src/source/libraries/images.rst @@ -86,7 +86,7 @@ Image Quality ``save()`` can take an additional parameter ``$quality`` to alter the resulting image quality. Values range from 0 to 100 with 90 being the framework default. This parameter -only applies to JPEG and WEBP images, will be ignored otherwise: +only applies to JPEG and WebP images, will be ignored otherwise: .. note:: The parameter ``$quality`` for WebP can be used since v4.4.0. From 4948a28e5f3b49268a39f53517eb0e2f1a7e317e Mon Sep 17 00:00:00 2001 From: kenjis Date: Sat, 30 Mar 2024 22:25:19 +0900 Subject: [PATCH 22/65] docs: add missing "Upgrade Image Manipulation Class" --- .../source/installation/upgrade_4xx.rst | 1 + .../source/installation/upgrade_images.rst | 42 +++++++++++++++++++ .../installation/upgrade_images/001.php | 8 ++++ .../upgrade_images/ci3sample/001.php | 12 ++++++ 4 files changed, 63 insertions(+) create mode 100644 user_guide_src/source/installation/upgrade_images.rst create mode 100644 user_guide_src/source/installation/upgrade_images/001.php create mode 100644 user_guide_src/source/installation/upgrade_images/ci3sample/001.php diff --git a/user_guide_src/source/installation/upgrade_4xx.rst b/user_guide_src/source/installation/upgrade_4xx.rst index 1253e783bb2f..97a7a567a79d 100644 --- a/user_guide_src/source/installation/upgrade_4xx.rst +++ b/user_guide_src/source/installation/upgrade_4xx.rst @@ -240,6 +240,7 @@ Upgrading Libraries upgrade_encryption upgrade_file_upload upgrade_html_tables + upgrade_images upgrade_localization upgrade_migrations upgrade_pagination diff --git a/user_guide_src/source/installation/upgrade_images.rst b/user_guide_src/source/installation/upgrade_images.rst new file mode 100644 index 000000000000..1e69bb260553 --- /dev/null +++ b/user_guide_src/source/installation/upgrade_images.rst @@ -0,0 +1,42 @@ +Upgrade Image Manipulation Class +################################ + +.. contents:: + :local: + :depth: 2 + +Documentations +============== + +- `Image Manipulation Class Documentation CodeIgniter 3.X `_ +- :doc:`Image Manipulation Class Documentation CodeIgniter 4.X <../libraries/images>` + +What has been changed +===================== +- The preferences passed to the constructor or ``initialize()`` method in CI3 + have been changed to be specified in the new methods in CI4. +- Some preferences like ``create_thumb`` are removed. +- In CI4, the ``save()`` method must be called to save the manipulated image. +- The ``display_errors()`` has been removed, and an exception will be thrown + if an error occurs. + +Upgrade Guide +============= +1. Within your class change the ``$this->load->library('image_lib');`` to + ``$image = \Config\Services::image();``. +2. Change the preferences passed to the constructor or ``initialize()`` method + to be specified in the corresponding methods. +3. Call the ``save()`` method to save the file. + +Code Example +============ + +CodeIgniter Version 3.x +------------------------ + +.. literalinclude:: upgrade_images/ci3sample/001.php + +CodeIgniter Version 4.x +----------------------- + +.. literalinclude:: upgrade_images/001.php diff --git a/user_guide_src/source/installation/upgrade_images/001.php b/user_guide_src/source/installation/upgrade_images/001.php new file mode 100644 index 000000000000..c40e9b8076a9 --- /dev/null +++ b/user_guide_src/source/installation/upgrade_images/001.php @@ -0,0 +1,8 @@ +withFile('/path/to/image/mypic.jpg') + ->resize(75, 50, true) + ->save('/path/to/image/mypic_thumb.jpg'); diff --git a/user_guide_src/source/installation/upgrade_images/ci3sample/001.php b/user_guide_src/source/installation/upgrade_images/ci3sample/001.php new file mode 100644 index 000000000000..94a8553a5e2a --- /dev/null +++ b/user_guide_src/source/installation/upgrade_images/ci3sample/001.php @@ -0,0 +1,12 @@ +load->library('image_lib', $config); + +$this->image_lib->resize(); From dd974c85583b29fabc6470d56e87e672e16c610c Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Sat, 30 Mar 2024 23:41:10 +0800 Subject: [PATCH 23/65] chore: realign contents of .gitattributes --- .gitattributes | 44 +++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/.gitattributes b/.gitattributes index 4b9a73d9d0e7..e2219ef90a4e 100644 --- a/.gitattributes +++ b/.gitattributes @@ -5,31 +5,33 @@ # git files .gitattributes export-ignore -.gitignore export-ignore +.gitignore export-ignore -# Don't give admin files -.github/ export-ignore -admin/ export-ignore -contributing/ export-ignore -.editorconfig export-ignore -.nojekyll export-ignore +# admin files +.github/ export-ignore +admin/ export-ignore +contributing/ export-ignore +.editorconfig export-ignore CODE_OF_CONDUCT.md export-ignore -CONTRIBUTING.md export-ignore +CONTRIBUTING.md export-ignore -# They don't want our test files -tests/AutoReview/ export-ignore -tests/system/ export-ignore -utils/ export-ignore -deptrac.yaml export-ignore -rector.php export-ignore -phpunit.xml.dist export-ignore -phpstan-baseline.neon.dist export-ignore -phpstan.neon.dist export-ignore -phpstan-bootstrap.php export-ignore -.php-cs-fixer.dist.php export-ignore -.php-cs-fixer.no-header.php export-ignore +# contributor/development files +tests/ export-ignore +utils/ export-ignore +.php-cs-fixer.dist.php export-ignore +.php-cs-fixer.no-header.php export-ignore .php-cs-fixer.user-guide.php export-ignore +deptrac.yaml export-ignore +phpstan-baseline.neon.dist export-ignore +phpstan-bootstrap.php export-ignore +phpstan.neon.dist export-ignore +phpunit.xml.dist export-ignore +psalm_autoload.php export-ignore +psalm-baseline.php export-ignore +psalm.xml export-ignore +rector.php export-ignore -# The source user guide, either +# source user guide user_guide_src/ export-ignore +.nojekyll export-ignore phpdoc.dist.xml export-ignore From 50eafce5b64555739207c175043d1593007f28d2 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 31 Mar 2024 09:25:32 +0900 Subject: [PATCH 24/65] docs: add sub section titles --- user_guide_src/source/general/errors.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/user_guide_src/source/general/errors.rst b/user_guide_src/source/general/errors.rst index 9e49711e7954..b84e2a299006 100644 --- a/user_guide_src/source/general/errors.rst +++ b/user_guide_src/source/general/errors.rst @@ -19,11 +19,17 @@ Using Exceptions This section is a quick overview for newer programmers, or for developers who are not experienced with using exceptions. +What is Exceptions +------------------ + Exceptions are simply events that happen when the exception is "thrown". This halts the current flow of the script, and execution is then sent to the error handler which displays the appropriate error page: .. literalinclude:: errors/001.php +Catching Exceptions +------------------- + If you are calling a method that might throw an exception, you can catch that exception using a ``try/catch`` block: .. literalinclude:: errors/002.php @@ -31,6 +37,9 @@ If you are calling a method that might throw an exception, you can catch that ex If the ``$userModel`` throws an exception, it is caught and the code within the catch block is executed. In this example, the scripts dies, echoing the error message that the ``UserModel`` defined. +Catching Specific Exceptions +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + In the example above, we catch any type of Exception. If we only want to watch for specific types of exceptions, like a ``UnknownFileException``, we can specify that in the catch parameter. Any other exceptions that are thrown and are not child classes of the caught exception will be passed on to the error handler: From 0d681900441f7ea56a044f6d996fa033cc008307 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 31 Mar 2024 09:26:22 +0900 Subject: [PATCH 25/65] docs: use real Exception class in sample code --- user_guide_src/source/general/errors.rst | 2 +- user_guide_src/source/general/errors/003.php | 4 +++- user_guide_src/source/general/errors/004.php | 4 +++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/user_guide_src/source/general/errors.rst b/user_guide_src/source/general/errors.rst index b84e2a299006..2c947ae7deb7 100644 --- a/user_guide_src/source/general/errors.rst +++ b/user_guide_src/source/general/errors.rst @@ -41,7 +41,7 @@ Catching Specific Exceptions ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ In the example above, we catch any type of Exception. If we only want to watch for specific types of exceptions, like -a ``UnknownFileException``, we can specify that in the catch parameter. Any other exceptions that are thrown and are +a ``DataException``, we can specify that in the catch parameter. Any other exceptions that are thrown and are not child classes of the caught exception will be passed on to the error handler: .. literalinclude:: errors/003.php diff --git a/user_guide_src/source/general/errors/003.php b/user_guide_src/source/general/errors/003.php index 2d230d34f29a..07418eef5504 100644 --- a/user_guide_src/source/general/errors/003.php +++ b/user_guide_src/source/general/errors/003.php @@ -1,7 +1,9 @@ find($id); -} catch (\CodeIgniter\UnknownFileException $e) { +} catch (DataException $e) { // do something here... } diff --git a/user_guide_src/source/general/errors/004.php b/user_guide_src/source/general/errors/004.php index 752b23bdba30..4b816ece1894 100644 --- a/user_guide_src/source/general/errors/004.php +++ b/user_guide_src/source/general/errors/004.php @@ -1,8 +1,10 @@ find($id); -} catch (\CodeIgniter\UnknownFileException $e) { +} catch (DataException $e) { // do something here... throw new \RuntimeException($e->getMessage(), $e->getCode(), $e); From 2f6402aa1060e7f35036ff6dad32937bcfeaaf6f Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 31 Mar 2024 09:40:45 +0900 Subject: [PATCH 26/65] docs: update sample config class --- user_guide_src/source/general/errors/005.php | 4 +++- user_guide_src/source/general/errors/006.php | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/user_guide_src/source/general/errors/005.php b/user_guide_src/source/general/errors/005.php index 54a3276fb46f..152a08137e7b 100644 --- a/user_guide_src/source/general/errors/005.php +++ b/user_guide_src/source/general/errors/005.php @@ -6,5 +6,7 @@ class Exceptions extends BaseConfig { - public $log = true; + // ... + public bool $log = true; + // ... } diff --git a/user_guide_src/source/general/errors/006.php b/user_guide_src/source/general/errors/006.php index 9d9bb636b2ff..614d61e18231 100644 --- a/user_guide_src/source/general/errors/006.php +++ b/user_guide_src/source/general/errors/006.php @@ -6,5 +6,7 @@ class Exceptions extends BaseConfig { - public $ignoredCodes = [404]; + // ... + public array $ignoreCodes = [404]; + // ... } From 042a7bd08ab7881323d68c55ae682b2dffd987d4 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 31 Mar 2024 09:41:35 +0900 Subject: [PATCH 27/65] docs: update screenshot of error page --- user_guide_src/source/images/error.png | Bin 87299 -> 123949 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/user_guide_src/source/images/error.png b/user_guide_src/source/images/error.png index 4c72bc4e43ffd45c9c1be60763c398fc62d34446..14fac852d48f872212fe0ba9ee1bc1e06d7c6e76 100644 GIT binary patch literal 123949 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

K#e^y#_=AlkO9dPwt>@aq`*RKF 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 literal 87299 zcmeEuXIN8d*RC^T!#ZL?L0~LMhZ&_vS5T0S^o~derS}l9AgBnaG^KY!Xi`H*MWqR$ z2M9%^mk?Tr5CUgy9N+Idzt44keCIm;@Pd$JKl^#sy4St#wbp*7p{8*70P}%eyLKH` zymdo!*RI_uyLSEYc>i8_#d0@eM`sMJmR9oj~O3u$%Qb|gy@}EsaZbo0S zuzvj_BVvjsH+8mZrmOug1><4$o*ZjO>P#`Eqk{fJENNxiuXgWmaC!LWxDi0A`Qzu$ z-t^nDKVN?D5vlR>HD^|i6F+}4R$$ok^C!ap18*xeVko_Dlo)jA4CAZrZu3}4pXbqn zW+@SzAw+t5jbmGq4EvH-N2Gau=jx)lY8Cs@om=-_FEL^e_E|o0Ca8mFX=!Po(!;1K zshaS4VF+bNHxbgkeY=05;pcXmN*7o%Se+5JkHqG%m(djcdVXyiuBMYC()={DwBL=Y zq)&RkcG#JytRs7vB|39`Wr{f2N;5uwNkwC%7Jc&g@#AGb`resaR3$a)z13nk$T#89)8Bn<1yTd3bon-B`ulHPAH$L+X<`&Dnvm z!VbDsWTv0@gRZ{J5ov`_bYfMIBN1_R$a|hMBF4IFS2-z)Hw7#uBRxIP&$Br)+dQTq zFRv?H#`Lzic}j2Yx)QH$Zg(_)g}D2)^QYr+db+yVsi~d1dU{TEUQ2hMEiacjP-c6Y zTZabbF}V(ee7mVO6T`B56|Qc$+3}{kQBhI24P(<9U)3j1_Lcb4_wpN+857GUvN4!j zQBgX_(8H~aSmCL>+F_>Ex9@#2XJ==1w6)XYkQC-su$uHa9gftk1tZCnp=7gTZtUR;-+L8ZEwY^QK+n%X5|P zGp>Ck9i1Vs_~6;u9*0>X&Z!Zl_PKXytf41|fO;MlcBD#>-EZ-sTGE=l`;pl>^gu1T z%6DzPf4tDj^2Pn-K0j%y5~ep1=TtYuvOFBctLHJ@ZtgMP_s=v%z@$>#kv8A&#w_LQ z9x;N>QcEbY@8Oa1)*#J#{WK`k4o29D?=SId(;XLV-=b-2q6R;%8r1ne9$SO6lZ#G} z38D+p!*w!_}W%5(J9^pO06IMQMym6UqZ)h3qtQ z+Em@$O9YI|+cQU`b}r&Q8O4_ZpFX+V|LTIm06CZ?gYo$BG{npPHka)q_DQm-H&#ol z-3&aQ6Yu@woxF6?cJ$v_6TTE*b;P3D5~@lZhkPm19rZ31>;ct`uJgw^)eae@9%2@{ zH8V3aJ6Pe?N3Zgy%QuRfSu&h#@^Rxy`H0R(Q5V+MVmFMFq|rj2^ZFTCSvhjSN5Wbz zUc89O%q&w4`-yG4Q4>m-*jU5Q4GqD5p2OAni521L##d?Q$Z&GkVZ)9X=v}}5&?3co z@mosrL@dww*RMBn-o4X+t$?@oZO^O-+pf)vooi7~k{((r&s2HKTr{s|mUq}N6N71V zVV`VEVA6~?sq$1UEG#_Na)c%NRMGt`MjinHHCR2r>*Uvd5iuv#D8;vLyWE#83#G)$ zg_E?btfIz9G2p80pF=pV({zlCvMSuB#PS2SHrMaF9@^R2zxsIfWLw|-Q(yS!yGtW& zU*k_L4>v`8hO^g}&o!;NWc$|n=8d~|?>08Ry_O~)dSLx)4azF4rMIUiIFc*$;Ds#8 z6W&trXp>6!{=zqQ#?e=!H1Y~P=E`6_qe_@32bqHCDr#+nuq3FF?%j(! z`xDUiuPH@pcz966eTwvJd?~JcyWzy}rfXl{78}<1hVpZAW;M~Tdd_<{ZZMBLKb8NK zOHk0sbtp|C{K&eBzrTOq1a)DEse}ynS2mK9mv@jm5|E>P-p91u*}T}QBSau2R>Db* zLt9o>HX_NzS955#Cns)G1$O59_wTaOUYkFbrlgUuxog*-^BFuT@M?Fljk-}do77q5 zN7ruOzAYZG<(ETED=87OOh`hn8MI#pn{EhWjWeNs|Ew%4%>;G`Fo*L#5!#jK4O?X2 zGnJ`EH)_B9AHzO##v*R>Mc059*PtS1}Xy%E8*GVt%*JR=-8 zFR${-Bs!m4E7N{;x`X+$<@6isNy%udBlY8xgR0Rsf9yTf4NGvJ?_09wENR2z zj~XbzMhICpzPxmpSqRgQ_TotaT*>|%5>wI$FhbmMvpIs({whBM1E0p_P^X-OHZ9SE zmkt*6vi&-d=4g$7{`rT8Px4PsdW?Wc?>lq5Ft_u*^WrMK$P2zZm+H+U8rJ$f0nBE0F*xzV5|^8s`;uy5VbKGJ za4HV4sJ*skmLsz3emH%D5@O4aj~8QEHjw|xY1-FJv5Y)}T}$V6^z_JAzjH}Tn;PL9 zyGk7hveIs(fl?+-**!eGyo5xl5uPhox(as}yDtp(Z>g9Luf3$kp*D=tO(~4n>S~Ic z?8LYq>vI3(WMFFG?}#-fPnhN~HrqKSgGFE~H_ZF+<5Oh6Q4Vbjoa0b$U!etS2xi|= zEV5tI9Tw-vhuNi5Rm--qF&fflk1f{qfi=vRUA}TfPFfV)ooQ`o=Xe)0z4MeX5blCI zWF2BRzfO0gnQ*7DNn(!p_zrfYHNxlMjGSob@O&G$d8S@P-27c78XlZP}g z01oBoy`+YjuHj@W(_ls(?(Bd@n`nNc;c=Vh7TyyqEG)|v?}>kLry%#R%a)};hoRK2 zEAx^3TZdE-QUV8xI0!Bm5#Tp+ua#m`|1a<`jM4}`8o*xEhQV;1WJ)?7+Y0m3m4N!~MQR+~ zi=!6+J6%G&YitI_t4lKjvDnIjH*em&#DD(uDR5rTgCeIIC(^n!$Y)qm{$J1!8ybAn z;cUqM`oAOJyurM9QHZ!Jz|gmH6{8r95GFwv;{d=DA=KJgD;paJuyrLxMHgcM=STNK zx=4?s;e_o0hnR)!wfijYZCXku;VS~|^WI;czZs33W-Z;*sgFK8TvMf~sVU^Wc;}{q zLaCWz|og z2ZI-4Qd2LFi<|t#{!K;KBPP$T8-p5j-d=E{4ted){~8 zd)2-j!zJm<*4&cWbXEicpk+S#+fXe!Ylxban`;dAR^Tz$%PZ=YbL8w*Oq{4wsV|zk z5H$nrb7E@B0VpFv=fLQgFB`1KVJbILfXF1*ps04lGRcWa#~yGq_pvXno+F|=mm0q4 zbmQMF=)u~}|CjQiU%AQup7||4=N6F87#z&YcbfY6Bm}5Dwz6`=Q)V*LxYFHVeSIB^ z!Q7*weRO)dyJr_i$#637hNyc4V`ghuv};hRRb=FdOpiutyyvz4{^{A}L-iCAc%*)i z`83cxlKfQV>y zI!G;%@5n~H`RQ1o;y|7}76uI-?>p*9wXjoHBTNF)_0*uePM%`3jX~Sbcy)J|J}8^; zWYCEcHa!|nwxf>i-nHv}$Pz5aq1TV2{_oX*ZK_2T`Jky2L~dpgzSbxY1w&c5cMP!A zyy3Q-9Y^HhnDV)7&OiKPUnvT=ZKj4#k|_XhY?^Y=u$%F? zKTx7*=C}03lRRvJx6(2rYx(=v%7?RE0Dz2&nke6bm~97s0(qNH<0hA0fhqC^K*6&` zGJq0#@QdS6<)&M`YmLK2CM4tmrA1QB9s=zw&Y|_0 zg^7e}tDv@_uStJ@KeDzXY?A2^nMu^Pc(I)7YSQ!P&ou%5BV%wTVBP%UZPx&c%Wzf+ z#7QL#i>M>ApTJ1+0edGV?(3Njdk$6U^`<&-7&CZKW`&X0fQE*I&3yYH2j`*RUZ$6; zf2JtWIhL2raikV@wZxY)n@k{L1AP+;T2a(yMKc0@^*0k13gYscC(dBSm^Y}8&_l+{ zvs;xj*bUc5_G$|UtBi*d<|^wZ4(jl;=IxWv#VlAaj^ud|{M+fR2erMXD(#h@>y%n! zd6+^Y3(D5|%LGs!0rfUmZgn9+kd2-X=hguUP)Dy%#4)hm%Iqncl-0nzIAhxK) z3MmGe+tN189X<+k0R`ha0V2`!(9j+*u}9Ko`rdz8ie()W9<7xNZ?Ir` z-j%6(&_{-c2Ya)@?(JnO`$ktl5n<2yzDvrOM~{AQm>c3jov+c2SHc`*65s-!i|}{u z^^}_pkuP5+0|F|ks~`Yf|HAS_aJPNzv0Y`m2zZF0S)?0OFxCRw|s9po|v~$Gp zXU`b8+uhu7LAg~H4Pk~=o(YF8te$H661x$GAZnr%YA^kd_rT$M2sy6)vCUhtfAXb; zV5#^0%8Xm(qSVncmKDoOWK8Y6b^AthR>1VT+P3Q#p0nLE!xq(D!nlqqTyN>o)A@m; z;G|RLP7{SOKZ=wv%~oepXu=s@8Yw-7!k_n?xDAl&^wg6(W%BEK6>Qd*P!=745+Gqf z%}rq^e=HtbUf;wqo(W1>US3Yd8_IZ=IZ?M|1|m4s3j8Pk$0{?-n)nXZfX7F zNEo5OOokS00z3#H=DvD&+^c^?EQz26l$a?)a@rBj=WwETbA81FP6a+{4-N=ch;Rz2 z1LqgLL1qHhWNc+Mp;`-GX%}M)4%`hHF%N%@^lUwIb&504y^Zn~|pDHC8m+;)u1UXa|g5S(jR$ zrKh3tWHH~#P1qXO>y!yE%3ofu0?@zV+6*|f`^}DPSJ(>XA_qke#4fsA>&9)IdHt8<2_@x%mcI{C zNJN4^zcG?MT_W|b+;8;vm1G0z&OKcU8UWZNLPR$=H(#<3sO6&1h&mcKqI$zC0RZ;0 z;vub-jXq2@RZ|Na+y7@$!3Q>B3p&QZp8m?#j$vZSd!F2Umwu;Aeo~)SXQ8loG`=^mm!he4V@?QJ2bk0*JNpZ-7 zE>{VKu0|_!PT`XXps+rYpQ*9ui3wYZ;cf8TSF^R)?m0Aa)u=W$7+W(>iIFYd_r%}- z!~X55kh%ywA$is$$31{l^78hrlSv%d`2q%P)hN5Lb(F@*;ztAnR8w8vavl25qC9Mm z7X3oWT5c4c3R#4}em1n8o6tyI*bF^ZaxFL*m-D?i{b{7lTB&kztkx6KRt-e2WKyXz z14J+2WRbDrDxW`RosG%P$S|LoxFT2*2u+S=7e{&Jj5FTG*!|^sc&U) zfDy|14SIeJuoGB@6u}fV^YifZTW|}uT(-? z*dT28?89v+u-=a+m2Yh{U+yto!`e4-Zv8$LPMpbQoc&PtIgIH04sB3ObBxmH<;uNg zb;d+ndS>X7%r-c5vozgon=8vYG91+)kb}95NUL;jomXe%vSF-ts_u<7w^9!n^_Y`0Zm{8d~ZNyzJXy&6n2hMXYF<>6qh# z5yD5>LL+RBm9*wA2`5R&Ur&;^Y2CIw{Q);(Qto^k0{BF86a?@L2w%qS;)%FGYymYJ zWP;0wwn=;-Qffe^ua+R5=TF}di`Hm~=EqTSOS_3`x$G-Bx_L_Trr_Tg$weel~KMkcbQ(RMBE7Dnk2QzRG$bK6)Mca>b~fC>{PV6 z_)P@3HtTH$ozlCx+iSIBZR+BOSI;Y9Hj3hS;udWVaP8XlK=fKww8lfkM&GKTlR`kADF*obRsXT?jeYMuDwAYD=`S1xS=qY7nWT>(cA)mV-kZY(V z+Zrou1QC{gx=i2AK{&CorgS7i_)*@{(M>>Ss8eDYn_~}hc6PpxjcJMf>H~GWuqmVx zcP>c^J>b9u6!tL^PBia{i~0Say-SM#p3@82$741*@~WIn=h!_*cSR@z=~;5E|`32Q*6BExlH1Ff-!@YYUG0Uk8Zu_?8$NIo}pFz z&?F?hUgT4Ipa}h*W&BADQh=BpqM;iUim0=cM1TZRmKzYY1~--<^O0CW`J$a7LsZ0y zNBdEJI8+I*h1ceRYR~V5$}{k#fXCkU^Q(pmiMFX}eh3qok9>Qzj@r*$<@?x}+~oKE zcjjrZA9mZf98?*sqW1OP_#8bu8@$FPI9I)MFy)St$ErPYEDKC)##c&n0|%h=XW^q^ zM(+Cb>^M?11QAORE)A7Th`{`YCAYxbjkNXNpYW6T94mWOSx9MK393#LBmI5ZdmS@Hr|ZvC9u3z$4dt39$h*j zqj+C$=eXGgW2&HNhFAM#kBpf4tQKL&Euw~&m<%kcunS+d_mJmo6t`|`K8c%HsW6j` zQk_@)4f+Na_u9q0(x?v(f2ZJk;=~CIXusc%jeVGi6t;8u#uayspF1VDLmLt?Oa7@m`X|G3=M<^slx&N()*+xW-4$*&;Lmno zn9QP%@vvhE-$g`WfWvtYj!T#9TVo4K?tWH6xsHQ6V(-bSTXF1%Mb9&xYKXXY-I&`@ zB`)Nw3JVK!+|g#aF4S*ZmVJ1HWzmzM$Uy~1%A6RWq;-Ol|E`UGH zwx>L%rwxmLDKEbYnWb%i(S%{CeH8tI%$1e?3!iha3&e?*bj66sq@)7ST6v_Ur7hX> z7?LF*eSje40ICThLc_B0iBha};XuvzN^?O8cLf3HgA`KqCvG03-grCcP!+gZJ0>k8q4^2_GbMR9TQ>6y2q)eMBm zl}HT=MVfZ10~QL^Q6lZWlo%PR&$Rn<(T z7g%)!TH3*ElB5y@?nFirIkeYTk9q`j#d~gtL1bUNTcXTB`F6dO-p!qJ2e@``yjXyj zU+3Vr5061}b9Hr<9gc(axsQDuisFZPQv9U1FOK$l=5?rZs-NN&5NI2YfrH>TtCa;E zy;vDD)>2kQ6|IJ@ZWO>r^VhG3#KQsu z17G?)`IuMFvzG;LK9-)GyhEp|h(#QFHK;nLm_xl632GdXlpF8gvvQ!d8=>rboIq~=U= z{mWTMDV^|K3&edX-aVaNT}JLIpx_-bk_FR{0#XM0*cMkxuTWJkd#_xhMB%qYvu7BwsABb?u+w3(?M=vYARm81FYQ_*$@@e<^d zruMXz!sd zN2DG(>@Ikp!2`^n=6No>;dsYU^;hMZ=~`xD=W!c*(}`@R~Lt@Ty#j;NOlO?p2ix&3Ck0_ z=lvzc4M7O5y1sIyW)`7<{6?-sw9MkNxa&9uq-UQ|BHE;&6}SjMG`FzFn@W$l%-{al zYrin)b8N)^blcpT*Kq_8GEsKQz>biA)zVxkKY#iruJrF~5F8B{6R>r)gr62Zi;SEjsOWI?zmRPELjG zb$oznpR{qc_w5mX8tEX>6vYWIGYOjBBM;eLRtP)W0j*3I6=mqF5lGQyyeYzSm^UC% z8jbDj?2NGv_}6mARaN3*G*%{CBkW>Za6X=t9#qoa%|tL)NdPqEfOQ4|g(4FbnWN*Zp## z-C3&2m@M@q9&T>MOgo+n7cPv`fxtHksfIEOE5Ww+yyj4jeEs_F_SI@Mt?VMVprAUe z;meCNhL!HwldW-CfUxlHjLgj07&Cvn4p-f>d;ND~q-C~#q|rBm4zrYaFWu!gt=5Nb z0N7#XE7mXQv6Qiq0n>6u9uAJ1c6N5>3JO5Le!Tba>v6$AARSh*nL$Tbv5=ta<+Vr=ymBk63p9 zIysp45SxsDF4W7a-ZSW}8Q1z5!0N$F`3=>U@inlf-duyU7cUM$+-a5J3D2)= zs!{Rr_ngR!qxGIk(a;hzK5of+)%lvt_GU3KRZ6Sqh%NNHfT4C5S+!hLgyLu?^j{($ z8WHe;Hdj>Kt4NJ~N7+i0OS`+Zp)+?tKrME_)5WDw{>ABYT|!Z>U#D8NM9;uR928ey znZoNSDLqeLBxrvt3Z?RQy7?}CyI!Ty+SsTeC->OZ&5cCl0>H=yze^?wdkm@-mg#6} z-VF%2ApfFWbj0s?NxY6MKN!mJ795baiOGGz2!IC2qd?P0U>34{Ngpy;5qi=Bo+#ur zEJ?d^K_LvGfEU-Ne2N_PSpUBB0Pf3U?91T>%I;_l#3A5!lGvL|>WR6~v}p&9yhi!a z#F-QrUk9JbP9}D^%JMOAHt& zF0l0M5f6crUc3;)cY=jP`J&)L{q!5fvtU(7Qo zmd~^U)f_bdjg5WmDfdle4K5^Y0l*Hl2qi}F7%z;}6{M$ML~>d%gV=v8f~1Pl%F1+K z3wbRVSKR^8C=)GV4i=Xv>66;oshOu=2zJT?Jd#&0zq=<#ucy8J&d5h0+xEX&6Qzt0 zYz8JrS!!S>;`R#Yzk4rm7(u`6b;f4qV=-`cz?%m$J~l`3a&XU=<&pXM`Qg6rcS2$g zmRM9&^r%hQ)wSr9pebMW1UN{Wou=k%03aK3olbLWoT&PlGiN-_Ei)>{Je9stMXkS{ z=0`4qK5+0LX(E@dsHWBn4U#0I_Q|hrEd;N)PixRT`@VivK?$UT2Q+kQEpPWLD=T-k zr^r!AxDhO*Y3u=;g%A-aD|nfl%*-!rK4rcX`-H15A8VsoINt>#J)rGA>hPKO5C0H` z3nJ!>h>)-_Xkl^;7_Q8Ri={zdDl{w{ZcVIv|ES1?K}TO7A_P><-X`M*%U$GP(*y+t z=Y1LeB{C&w=uay0Uo^PjoLixv%ZX7OJ@^>xMHnS93`I;K=)$vhYH$M|Wn$88LKa7K zQxsMM&;WY}_>|M%qjbmm>l^bJ7xhHRS9YA<1v~+3VM@=Z~R#F?mo{>~Gmz ztUv0>JrSAYzo7^fc3r4k&AYYLukoDkLOy!+i(jbaYOxR+dW5 zoxK{B33cic1*$>ApZdlIXa2bx2uD0yPJiFs-8~KMg5q8a@nHX@Zc}Qsnli4G-xV$? zbpkDdod<Wxpq;@E!c3IF}&qHo7dkX1&R@8yPA=J#_ldqE={meVZB3xtghj0jkrF8xZRUg|F|v zawpt9>3j#zY4Tu&MV*Yx_fN^GDH*u2(zSTQ42^G_H*^`g+uQ%_aFO`Tlfpa|=TytU zW2SOtb;i4QzwF%RAy#p1Fl5EX%3LF%daJqKyfyv@y-BTKXoIIXB6Xe{LiMk>AbXBb zAnLOPZHt8QvW#}X!5%{SEc8{Bz+jb;Z41Gy0{&b0-QM3m5?{%EABHj@XJAS)yDu7PWyH6}r zh5BgBcwLRdc;l;cEx=3qiFR|0_E76Om-gVI9fbe2r#YEQTsivs!0`N}&7L-dvx^$Y zDsDZSg+6DC@YWN*5HeJNooD=U);d2aTtS|_2bEHu&w zWwH7;IcpO#bkyBeRSl*4YHD^KAExcK>X93AuR9UST%t3b)Lb>vlyl|q%FjbbT7yTe}C+rb=++ki0) zhPWti8GXo|$h$8r%>;gnSgO=z<>%Fb_I`7*6z#?@Ryk3*xqQe{SYte-nPU(l<8B-r zHN~BBahvPImlYu?-_=`t_UsA6S&OIvVofCl=t6LXSn?S2iUTnwpvrI+DHy$~I%#Qrfo+8})o*sY49i&?rhn_t>~QOK*u?7hu19D02pw zfZg^MRI7c8RJsCMl7(HHV~=u*i5Wmb)7;k9n|%E-(y$E3aO-()AUM%A*b`7mwG9l; zl4PMuofIT->i=9vQk^~O0>ylM?}M>7(P7zeAO|X8OG@gGsPT`}MKS5W0_3f8{~t5mN#wB2L3+wQmW9>1ZHa z7*fJeOdz!FqHb_#yS31_Xk;ZNr2`WCx(e#H!19oYPZOTAyBn%7exs=7y<$Pg^XHG6 zgWgN}Z|XyCP{!F>rV=N@Kiy>iy6)mFW#!@+MP=m(!d&S{lBknuA0yAALI6}V>FMQ8 zwd?Xgt+o1mTwugu{`@DNd%rt9ggh)BHnk~&lS9$Wf89!B^e-d;mQ9@k)P;ar((>Aw zxCLD0kp6HpHCQ32s!XcmHH#|{-p3|*+4^vu3zz(5n4$i+D=&umCZZf%T#;|Oe zMy?~eWddxfVZfBQ!D?WW>H_M}bl9|h7ZadihVSk?OmAnj|Mc|O^`m=Vrf+QczMU*4 zdT<03+DPTTpV<^XnZ-Cu9SI=Bi@80TJQFAGVSgjz)W1RbWtnQ66l?fy8_1)MNxhnf z?j_YQfx=~Qbl&N*5540u+w0*7{YXBak*%K=oyb{QT53KecOYhab4muApMPXyP-bf> z{HY^@gW|yV_#ox-u`N1vYyv&kj{5MPVaY+eu(ANnWooJOY)Sjg`7v-QxSVEKDH``a3hA1j9OM%p3u9J7# z$jAv^!**2k&ZzY_t5w5j4S?<*NY6pY+7E+6#sTXsW&`vaby#bBB7XrP2Uy{hAs+H0 zfL>u3IBB4ceg9tKI-%rKV@d?*-!LYs0NO%+KpdA;iNAU~_$a$Bq~{|I+BMQwx>qWc|e{^sx~cv{4Wt@ zVirIzt)(qUfVT6bS`?CQ_%?*8Lw2NNZ$Il69%NW#3FE-a1FL?JVvD2)7R6t6dGu4y zfAF_y3nPyP(sT_i4l?u^byWdsjA_E^M7-%Nb&J@%y(}oc=^>M`y3J0b2mZKf)#|Uu zQs=E+(K4*pJMTsD$Eyvz`zFdV=hwKw^f2}@cG+{Fm{D1%MX|CL?WwseUKgGkw{J2S5-`7Ot|f zlC+%*U7b82)Ioj(-&sd;ggMfIBC8NMvToo}$!I%O2E<(QOsaHYHd_Zc`3>4Jm@J3n z8yO0K*@ zQap-1q4kg5r>5`pB%Vpd4KFjvMBQxi)qaglmDC~}Ii%gc^np3*`?gB`ddkL(Y%}}z z&~}nj%|u7vs0jsgQO7InNN*H*@APd&?4^M3fd8{Qjz7#leG`u0r(PUUN)C~^Y zO>lI-Gp`?~_<}@%kwUFC!m=PsXkBOk5m?*U7{(y^jK2AX;5cVt(=G>j*-`6m?BM0o zPYy;%2F?ro0~wqisjC~~zw_(X`sU72wiUGiNkVg2AyxrSnk2kKJgIo;#xr zjlejWx$dwVQdfm_|3-(}VLSH4VzP3}w@-V$7I%a{h=LN>1S!fiMZ zTVr2BGX0$rrrwMi0n;{tueNg(II@}BMWyHcy(QTu1W;zkcaiBhxv;!!a44WT$PvJZ zPBYvV*yyr%Vw465D$xyNf-0aSKm`+`2NM%<#TgA-zp*h_K=SzV`Exdeb`YY9%vi2E zC&QV6h$pvZzA#v^>|3*zKFN+=eFd*d`ND3^It9?;YS>|*@HKpCLG9Lt18J~g2cE#0 zdLT{rk;FH%JB;@L3oi8}8PYr)*s_rZfr@NHLs?Q^p&WFgX@b5;0k6v`FYgnG{YVOi z`-5Rh_wSz=0G0<`jE1UePr8zJHh%Fk>b^3UGJGN`F>!c)d>k5QS6BmXcfgG!MjkNJ zqd4Ws(QgIiSwqHe%_B4=o>W zij|qb?`%?lFRczH99Eaw+mJuq<~I4b0E>D>jk}=}zn|X)`fwv?$J&Tabx4>1>2D4~ z%Wc3O(B3ANezd#>(Hmuu!-d2l=uS$9-jqHvvN2U&qFNPZa6Bfh`|KQ~UYD_8mE^;pyqwzEzSQ zI8f!~)c7ur^^uaCt2{f zt{jp=u6mhBPSniW@m|EbtcX6r60bMl;fUYRlv4CstuXS>c9Cy__ZwzqMe(=x3%d)b^xR2(1;0gwOq6j#5Tmz zvbuuV{cXczkw5?@?Ib7x^UTSN_Ao+vootl5%;}uS72dEdRP&zmH$$2*$hD}q(a{=^ z5D|bKAXZ4z&r9VV^xq9)9|oc&csyk^xHa5#XM4U`QGg3TDUJa1V*y|mh&BRglpYjW zU`Sqzqq}mQJOcm_twPS$lGt=x(oSOQyvI;FdT^WM#&W7U3k;;hwYMr(CL?&*wUD|VWR()jo8*ZrBRzoKp{&C_0x9wt?j2#db(qmjjgwB)k@Ke=Wh4`Aa3r5P#qL2x zDIo|4p=uO7(+NkJSaEhB$plCl3Lp$YN8mPoxJE`n=h6@MZ8Oc5Xqj@|3XjeN-UOkF zk;a6;iwW@;hb|rq-TV6i#$&R(U&TGR_7}!DGW4&f$$!QCsdDuX`|v9x2CQFLZ6dQ> zs*~5sg{!pM=HW>;#C_sk7gd8N>Dj(IZ>7;b%khpaTi%JB3|K7owUq(Xohl%nVnNXb zC5|*wXBKVM&tg-z(k1~9Xvu-GB3rXPLb^WRJ*$L;gwheg?(ai+hNVi;R-pJOL#k@; z=%@?*_voH(AUxfjovFy7KOtChL=xtrtPJd(ob=q>ikVqhtWJ-`$bhE9eD-WNObYc3 z4A=`wRQtFs4-cEN%J{>ui51dbgxdV}ExF8TB)_QWGKxLB-HdKjY#l1e77i-Rd`|2a zmf-qPU~Ivw;}Pm`4@w}Cr1P~DH9(W|2JYnCM$}#@%gmc}v-3M>qH&dKsi7!5;G|VW!_@kb|B9}&BGG9Wt2DT1~&1Jv<_LP<)u7F6mq4GrJjVlbhc=o}svfPTH6o{W@~bBB)}b+~iq z8r)CNF+Oj|Ebhvf!LT$)UKZQ&z?p&f_T!lby6jVVUxL~LbC+VsN4&lJ{8er_ITg_T zE=<`tJGVbpfui8m&xecmi|IpoQdcDscX$Wjt_##CRAiS|q zR1MR6?}V*#`+j*Rt$FbFytOi>s;5yZ#X9vBN_J>|QYz(7G4|@)8QbD|SNJo29=g}> z9NmW^COijFVy)W1tjD%WKS0Kg@?kxnby3FdW6s3R4ZX4Z)dTaV%dclPJ-zxXOS*CP z*VCKM|EGQC*std}J$U-7k>=W`&+a=<6@m+ZkbMK4`ZIxBu=)o&dv$c0!1AzIp4sDv zcdqFy7}C!OMQJ(dBz09))nAvreJX+@vVK$pJO!Sk08d%?b)i44{xOhkO_DJS4Gjf~ zoAqz|&YR!v<_E@Ju?h2&PJ&u_0hITJMMQpT>5Vh)^5~EMuHq(qyr;K!AT}-zCPSh9 z&JYy4|Ndt^+0aj}k~?dAF#X?d%J0{#_J4cv*P!!X-~W#`F7{FEvNpd^S82P7o~gBL zcf;et3eB_j4!6QLt~y4+E+{G@gRg1Q(XYVmp~S6YX2$M`g2q%d3IM0OfdN_WoAtXz zf$(4$U3iMcn@5ixCDHx%iJY8S`5?Y$GgCw+ilsCHp6B!%K-$Pw|I|uT5ue!EFihR6 zOnaA>vw3LA%T~%>Hqo^kPo~Ne>x=jdq7lEUq4XL7VujE9@tNk1j+)avykLOW2(Jh9 z5t_GETl(M$H91#o+NL4-E8^pw*SE3B0WLCh64uysKh8ho|lY3t4V_w)E# zI$??z9<*=>nlG^F>EmUISF}~srmo77iwnajYipW6AJ36_(OQ`+d}=D{7_Y=-566It zAQ~^F)9s0C#vqsX7v2<}$xAs-CJ#kCChSLoWceN6TCf|HUR8D?@pl1)`TEGcdd)ay zb^1ewMO>w;u=6`(o3$vrmgZ{31`z{>QmB5w>eSI_G$Mi_!;@-ZNELl;&9i26xdqI% z>!4tDG7OgoO5q`^$_-@^g@=H+DXm1tyiKkrhENt#4)CLG!{`zublEj^sc5SaCk3zC zQ>Eq5tg0C?Mv-@VHY_8ep53&hq#AY<)wa;aXX2e9TOch%FkRD+Pf91{tmv3V=E8S* zSesQaW37qdib^jCnfb=ZHwIK|u;|K$ic*gk@+q5`m_Rci{9nYU3ZWfg1foh-PfsJa`1`Bo>F$gK?%c5)j?a_wTRutpvlPkKnmjrO*uV zpzJ;|6S{ErPDu%>t4mQLfdrOXwVa|*DE;`iP|dG{5rNJ~{0kR`d?9v7Yv&|~p zo!wsj?yr@USkUuXE^1@!iD6G<)<8o8PfF}$)hY=^*_c$S8TK#K4q(Q1%}G2*PS6Ho z(8|7+o;rU0zQE@%ct)uI2hyj56mKC}Kt}RK90zSfI!;xCOf_mcPx)q{eG|x9t$}O$Rjoy=7-$W;Jl(itj{_gJLM~->z3j4^_Oh`*u^0!wkqF;BAdqq?miA0PIs@FV zX>4hFzQ5Q!V+4YVl|ghEn-unseMcIL_$r1Rk$x0!Z+)0M>Op#RfPlh*!W035i$#1j z^fe;VGXqRP`R#`f*I{ZF{nh0xq>3=Usu>Va2XPt3=Q-e60?_+|ni+m}oDuiAeS*LZ%ff?uaVOXC_0)DOb^wcrqZpn+@DU$-dflg!!BwG%) zwzrJR?!9$Ci-5v|J^*>7tN;zRkb3|{h0}&+5Xn&sW9}mWT55ILA`9p*JSqO@B11Fd z<-R=)?E%`5()4Dnz>W-bM@e9JomnX8)~j2@)Qh@|x)sCS+rmvhe*73%RY~1fP7V$L zI?_-Tp_U9PJ519GR0Sdm5)A8O9zOgH=_-h};)a%nXsa0z#*}W|f;DIJwP-3RGz5qNyg@cN#xGq0BdWF3;u|EKJLDkY@^%S%f8qC zDlqBiAt4G;0?}4> zJy@ONVPlqNGjH^SpZMeUu*IN>jlbLGN~IidV{GuNO^{mmWQd}5Emtq6TEexTuSB?S z-KnT8cehv{YUSv@;U;serKsdSQRHo=o!R=hXBEkn6L?3^F;iJ!pG>CS9}?@}X)Oa( z33yZpW}QSvo)S~(Jo@pCp6rvhxmM*3M9P6jg>(p{$l}eQF7G+Av=(?EN=7us5)J}7 z%g1WHga|5>^Z3@0^BVpByuqX0G(Nc56+6+#qYiu;?Wj?pXe(bEmQ zDS)z;2GP9*zy^@^i^j1f#Q0&t4z>`Q;fs_pFwy}S1{mKw>fCoJ4&UBcG5B>i%3!c{ zkNPx~;fE?KYvI?$Z;EO-x!N-HK5 zdKPJggVmhcU?mWbAMH6<0?z_^M^Ay6Ac9qKsT%Ty0+h$n_nQbp!lPM0D;D?PEC-8{ zZ(RbzyF8hwk#e5f{bV$xEdZzxIxS)}APs`&xd^J}=vvfx z$=Bk)#1=r^c`6arCeSRu)ixHolgIu^1ImMp+r*p$$qdXA%95!6#ol`cRk<$RqAVjK z3Zh^lECYyuU;-s8ASiJnIg5x0f*?_{fnWrcC+G_2>0&bHJD<=!dUW^bni6Mg6BKi5r|eYiH9qGU!2DVw$WQqpj4Mn_kl-Y~J3t1kc*C1F zw{as;N70a^6^poRQ^~fxn!eU0OW_*ddCh|GGxf3 zJua`OZyRc?#Hd{AuV5$~jkSA0m|Acg!)bsc7-DC2=puvF3dWKfbE}k zD!Pzzm#VZ<_UlH`BR{BSpPT0r zeHOcRedmC+soDc+7moXLp+|i6*QH#YtAAjkQ(SXtnYG|M^F{ipk;NLTxARwX&AGxh z&&UjF&cPGPS5gRBaq!3z>hQonvtpRuA45nd2B{ z`>t*93p_>{TrzOI^(ddj#Rt@^|MrcX4F)FkJ$!h5V`GbV3$oeYuKtAj*j7Q$@Fzcy zl-GbyK}`F)2ep=an3)>@SsIdcIN#@_ovQy#$B`jab@Z;W{%yFlKTYa{xOJ z)fjNRls9-+nWA%eYrfm=(vY5l=qKriPSms78~}&BoLG`u%f@5-DY!Fj>YB71^Ze8U zS?go~n=pfjX+mMbd40QjuVKVbR>(e96ic7BjB8ISpt&)bCOTbs;z#aoBaxMlpAHQ? z629^`ilbJfRrF8szZi`XT`$Nteoz#9vXAG)Ss9j~NFX&o-(4(|qpKg)I&?Y9t?cvX zr^Z1bi17y|=tWKE*zxTXiyOBPS2jR5p|0SkFFEYs%5U7x*TFI&R2!Rhicd;X(bNnA zCx>E{!NbB?nIdvHMjr8TZ7i#A@AIgX4mob(PKgervCdw=70Bd`l3MIq{rQK7fn4E; zUkM6-VZjNd<)6DP`)Z`}FYGxK+HTP9=ct!3{^04P59WGW@`O zw}5M^7y&z?E=KAmM_2DtO`Jc~<`rEGYS)w+FpK&W#Gt>_`NPBM?+BEq= zZ0er?o*R#fAHRq>99DG*95bvGD$eEm(s;Y$@U>I|VkG`!0mO(9BQ*5J$QEAuO!~KU z@rdZk9zE~2mRvr#`t7kzd2VyLUj{jq-k1Y~1A0H2yS4iG!?wPp?cX_8`+;uvo&6`c z|2G=re>k|W)AK`H(yXwW zr)18bf7&=u4>K1j(zFlPp?Ti)j!BtP=5*5<9UiQmybyL#a2@eDlOepx|=d@o=ct*(RzhfvLA4sc%Bx^CfL1}$p63C2rLI3pT_pCx@fVl{Y^%jD+uq0T164Py9V zi}zl0Mm$t}U~nS7v1e5helg#|#9ThxH6@KIezM&1KrRC*H z3JTtE&~@$Y%(fLL<n}BRTgJu4O1ZncV?V%a0Euf}i}4n0zjLp9?oqeCF*C7}mpdLO~%Lo!dP6Y@}auw5Z0+qm-#PF1SKqAY~3jx zHHt2Vym6aRQZ(>vQC>!>0oHejRpr@&i;)JQf4LTyn75*wV?yxJkSx~KYygw6^krY%i0FS&!bhT*tKBcWH7*Sc(dO zeqpQW`-iQE{YV1=-2fa+4lc<$pLc!wP(BI*sDvw=SWumO^;3O*XAbZ-<^YRoR)@v` z)Fkilq&66%>z}9DGdQ|9Ec*m^?1cp*xFVAv7N?{`3|HC9hZr;env;s{@$@5Ic<{Dk z5}u*IXioy3pthXvU${`>uqb^`*CqLA z|A^hn?W8{7Awxz*G{9YVufmeyqWoq*Qn;Z>_~jqby;M?uq|HLfsdW9|IR*`!2dD_( zJ(dxsB!(!?YbZ$bFRS#{Fg}4|`@RtU$K2sA_Cf`20_iEOBDSFR3S;XeIs;7=$OxdW zG2du^Isbd=va?mROU+H$s|9rZpe3iOfaR)@M1jUj%55s9{9HF8BGEbSJ~2JrGQH(U zhoEKGedl19Ivk+p5-T>Qfy_rX4xL6XwN{>g|2~lHWo6R5yu9a}@0QSUgo_zgM=(q) zxaM-pgd|xJ53DN!`fxMUV%h^2`d{o>w4a$ub|avGnmI>Sj0v8k_)Gn-H!=IM%+D7N zI?wAmt8cOaqvhOD%j4J7T_w>;NzW7RoG!UXL(E+|;p=HgpINv@i#Vm5{P`+~q^7Ds zmg=+CK@;s>r)B>8DK~+eKi#}oM3Vtu;XQ-(*G=ESAx$lhL&-*S)iM$F7?yuFP31ZelL4-0QP(A!ezg)WmNmYfFjv`dZ>_^yLz# zQ~R)2>XXu9*d*sFkCEx^I2bpU%I!h^An%oWs%tB~GXCC~FEhEg@swAR?n~aj5n7IL z$tBi8tRvyBi3lcJkE?#Zq^HTfDn(xTdR5+DHLehsxC-67r zyTh`&-=d%=RTe_{#bTfOsqrn0T-lrBAACCYLM1`9N$n7Lh0>#U4Qnc8dZ5){-BYm} z#|>SV(8551(ij|{L${wAL4rUO)$@Hgi0qasI}n{j`yN6De?tx6Q$PJ(K(}9UV;1*> z7c-pJy{Y;mO?V{^55$cBheRexd4tkX|L2ai+G} zxjD;1A}(*O7}X>CxG8Q6KB42Fk{sT&eu1%)mpi0IFB#sbOZw(77!CrFOqsD4biZDq zr*5B*bbGia_C(I^S=k)1Rao*}{?#9RN#OZaSol8?9&nTa@eqj_7kjUVmOc?8D?Sn} z_xGDtEIV>gK>tA~^{qSPAmqA>yh1K78jMfW8N5*ZGw0*X!4Fq9@dEt9{1I*I=w<~9 zM~lycxhvj)9|XZDQ$;W7%td@-3W{et0HsjMf;s>cMRi*X5Ugl$@ZQN%=5jqQbVIMa z>c0IdUj75^fw_=z>EA9K+EJjAv}E>Ck!5uS&K&%R$sxNu(8bq1J_{J`8xXK_@&R)x zZuh<5M=)Le!xditz~cRf0Wn)!wGlJGGXe*E?j5hp>AuxVqY4rVPPy#7ouu<>c5L-Q z4Gbc|G!2FY2L&l3D}{$duvdpW>o7T+tH`o>H zF5?YOg+=#-!&hHvir|OnFheeIHMW8`M%R;c-62OVB3O~rIdMJi@0HiSu_j*oq1&?Z z+ZcFl{t%C3J>##l_qyW`EBJAK+%|b^b;BjZWCKWc_vCbPiSq?WlHbT6@vkp&W)rXY z>5yuc6+(&POw|8U6zQI?D8b ze7GmrQHbQBYz`{X0{uQrN8I@zG-q)roXfo+kdu3!h_HiLo2EwpD>b+(~9(&+QrKj{7OaxnDg?|P_!s%pH7Qvx7?iOUbQY0#YbA;)*6uWYc3LSt?4ES z3kxHsclMi^xA0UTa#1+11oW=0jVvpxR%1inn8=F}l*lOPDs+xYfcj{9ZD?X_oVH8= z>{X^e;M9&M{GsumoUr^&<)bynFV_9Gcz{gdSq+c);LUfJp@|#uPQ3jrk)q!EfTY_ZhXSg zjZ;^aPu70aP*>Wh5uWkbH?`OKnuec1-ay?q-91^d-2A*}w`_h?KX)rSNU%&<+mD`a zsi&muZV5A|lI_{%sLrF*;!FwGGfjH$9&;^03<-o9JME=46!reNQ7XD5>)+~AI)##iN1JqSxP<2dc!rH!CYXYJaxmZuqy zKfY9Y8ihIVheUx3I#>b>HTs8#w>oOWQ~?&Cge?hjW5^0YPxBfg0`zzmfSo3iT_Mnc z0c+T1*HI6e&l(x&xtjRw&DqZ`*3#9ddH&jF(>;x)L4qQXXx{1dmh+>nV+ADUGkbxOyJ>sl50ew-GQ?lmgUCp)!vSRsPN zZOXsBQbjHyS>Xr1j~pI;7S0bHX^KipKCla5f{3lA4$C~pE8Nv+wvq&6m)|!PxGG3UoN;B>x-49Qf_fu1ZWCbAmiWUoy4B;rg#vl$ zSKg&H*3}`MnVFr9U`?2)0AgnLFYkUmVueO>MOI}bDm^?s{m@U1bN44H1`vdV83a)g z#l7Y#=T6k-4oEH#Q9$sXA3dv+9;*^Pevh>j3lKj_Z_;g>V&dekSrn&;9UDq;#@Zaj!ls(=i_UZ+3<+tM5cq< zBNK)laspmyLUYzn2qUZZQ+0Zn!NzPdem3p6aN&ES0P`eK9pn1cRCVG2-DKq`w*Hbc ze>GG1n8Nj`3=f?jJ6@ZLTRi5rv$WXCcdCh%*ezn?=$_vld0St2#AKK~*n&|>*fpGU z2{eDW4M1370!Kr-V}_js{2(@R9a%4j}%CQd5S5S6HObEnr#e>HN>7PVWp0< z942l;kLuMj*epMu88F#$x!3<|v7N()+mhl$3xhZ5G7jE5CD@_;7!i5K;~ltz?mCq_IKtWXmMC{!Wdqj9 zjBxJvUcnEod0;vvk)zIwopz(57mq9bAycuMpx$a0!Wmne`b4Pxhn1&rva42_hzPG9 zO2U>AJz@?d4f z+edhHH~IaPR-PVKJ<{@xz47H0!>$U|^5@d5v25hL+%uc~l|SRWf`qSm_tj71hzti| zH!d9B1jiE(S5jEgpwv>JncJ4ontKELd;u2)7woOeV)VdXW#wmaDuO#(1A{kxI#xe! zhYF#h+~Z3kmk3mOyN^JX$O|I6z#mxdhYvq3$rw#PeDE+F^wQF)_`&E~BP_v(#$&qe z6hxlKRc#m;j5u6Aq>QSS!qOS@_MjAleCa{XNH&Kgy%0>Rvsph?6|2Wlo2IjUWE1^s z=qG6Sv(}!JVj373$qs1s^II=-#Phwrx#kTGf7IZIqoxwJoM7kRv0*WgFGY#%dOuXU zpjK^hDrD9&`gF|6EDz6nGCz8*Y)px=cPLqNq=lBt9DdW$So2zw`hc8k8aGl@Zc8RQ z*CNM*z1{sIg$p9)K8+9E*pmO8{X`XO$4|?7j(C-P2J$Dh)5E9fd_p+lD;R4d6&kNK zeF~E%w=>ZD8Z9V`tq)kM%1i$(*gvJIHr}tL5T?1zDe8kRLpI=Y!=Q1!lX>cI&5W2Y zA`E+^ex)qcXMV{HAry!;}jhuhB^&>vSiVjNNWCoE<*K#|1G0HL|}XObF3LWQq} z+a_Gpeo*%Lg>$*LjJzCaxOGCYyqxa&%d#WVH2Dtwy^kZK1hm=;j~E2?TlFwRm;YcC zmg2U14cs7QmV9HE$MJFPvWmUVnHra0D<$*)=;Z{$ORx3u4e}@ZHa*}-E_*#)98`At zQw@n%3$%?Y97ImB<-6&X?Aj#<1g`5_lW{CcZ2f*Ue-t1JiJgU}U%zf1IvMMRa zTY-#6)Bn4$+|Fzhx-?b;*xQ1S{}F5ZZ>vp-G#y%#a&vPPMfnd1|9-R9VWL{99(C7) zgM;Kht%aB3hfC=Qg^szf7cre_q@7crK9mA`}!cKSMdgbsdLz=lS70in*YNcdmxlft0Lu3ZnVi z-1E754t=aBFYY^4JX`s=b$dM8YETwW#BTY-JlBPRh%K*-xl3OAzk4(B2kQZ&xMCsRYki$mr{T^9?0 z{oquQ5F~rApj3)W9K*SECLBH&ks>9c=CGG(2U%JBpiF~k06LEM{~)^YsB4`xl%3MGk9viD z{fAaZ9{%m$n#rpCS>jx6)!}fWOMqrA*Pl_df%3kj146UsWAKhJ3H~YENBR$~_SOE) zKoWcMUHaxK6(rE4wr6ttx|Qdm-OL^?`3+H8hjtw5;dW2@tez-Z&dU*Q1a#^)=;Tk0 zJMUWXCiKrR;M%_ABjKov0VVa$E!iRH*S9O78U*94Y+~tDr?5ilQJhK4xh_LKPv(}Z zUM+pLv|)cc|(}mC76Q{@U?Ofz&wmf6nB6R$tPon*x!dt6%rF`oAV^{l7`@ z{ux@le>j;toM@CqYhZJ8b85>t`fC2ETa;p2JtA*HY#RMZPvgom++|gX znssDVHRNMY^!xm&oKJa({L<^!7x63H0i6F)P#>R>;k#ya^6n*%`(Vi+<^a+W?ng1~ z(&}e_IKK(^ZilUwj!p);0FE5NMhOLfNrI0N~8KJ&Fe`FstKl+e|HZe1M zGWVxq;`>x)Ru(aDwpN8nPVe6(Epg@TqpL*bwOy8ZY=)Y7d#pXeHhy_q@z%&Q_eI~0ZlM*qiKi{)W~g?5-5Ca1 z4eQ6AvIc}WTtD8BY5E~~ps{%6eHSNBDK67lWY5eEn0HMtTedD6e0eFkEM&3uDw~L4 zMWyViCi}lv9y~j&g*zPGw=pa(Gx`Fb#P&b^db~7k47x@G=~W`$bFMQTW*BZ^MSTB? zK5Ai_m-k*gs@iIGqE^nDc}3*n^H6V=3>gY2mh^ShU!Vp-{N2lb5aPfKVUuA$&`wc< z`P_lEyB(NMZ#=OxjH%~pOz^LNzD`w!FX_rvqX;^V=H<7p*+IX`FK3k4@_>SSIF(~@ zz4!6>r`C#tf9}Dv&8&68w9;h3X;ydsCrkF<87oYkYy*Q|c&(~&9JCB~%iAcn$)>j` z>S%1WDDkvUR-RUIRjpQn+m|$bhCO_^HDE{2$vI*t(?^M&MAZr*;PLdcJ6%6B&{4?z z^LKw%jBjPdq*iY~T`oziZ7n{dltvv z@CP={3?4mo0%P6@DV6hGcsv?6y8bWD7xd*LU5Pc>yRs&ct1=-EwDEX%@F}@GhbxRH zs@C>iy4sokzO};Im();h{rR6-fR*k0->@LlS2hu?bvn70BMVYJAspdFbN$x~mzOrq z&2)B1F3Lz`6h-|uJhr-4=jPML=Q~`9j)i_=VtR{=)yuBXbyz@7D11D0}x{Dq)nvX`2doh)96ls zOW@I_;rtPip$^x-`}Np{Cjxx?f@&dgBWfyQR2dYFhIax3D=xR0W4i5#?7+_c^Zq0> z;z0%ojmQp|-eef`WNvXp^vRrdd(!ZPQSa3bCo3JOClT|vi17lrDJp0hK*lvfgvZY0 z27oAAK~=3n3h{z=XFK-(@nf}L)~<-(`ju$Bg#>T_pu%7uRj#F5Fj%1CZ*?U3UI%5*XIGnWwD!wilbzE z13pTzI4j306i3#^c2uLxx63kj2e2|0S8{QBIT!84nNG7l+k|OqZMI^9TsTx~n|i-L zIpu}v*@U1FA!%4N^OHw0tSY}cWM(iUTQnXzn~rWun3JpJyfn+p#6OmYe)qjP#V#X$ z#4sO#0_O1-Y7OJiw{A~FP!h^QK#B(iPMNKq+*|y(cH9UFW)a%V54t=x=y1XQ;!wT^(W|; zxI)Bna$*A0<|R?-&_6g>=epF3iO!n}4?p7yU;6DSN(_O=BfldXz*l(c%LovIgEFyg z8(Ex4#E#lbE~G9WSZ=kd>iglzQr%zQ=J5T_M(gFp>FZD(0VLf!*=^96tecD2trs_k zkaoZ>mQ__XzP*?7t%NuZAZQ4U7nJrn*Q*{ZijtUnG_hTQt2R+1Jw_0(v3e{q$VZ4L)CY}m2mF4uS#(RPf4UPvwycBITgvex3^mKjO zRMVzh#IN{vdP`%`$$Bh0VshPfuEV)okb)9*-bxi?_F^z3Fc_(HVqL;qUCio6LD55- z#fu}HTd!z8Vh9{x1sb8>w-HNB^u@YTIxbI54HF8cHqi3ugO*3F|B))fa|TrP66v#g zI%3Yy)k1l3!&rCuPz%wVfb&ntnu3iZBQa|UU8VnyvboMU;47cL?PfGhA71?MMEpC;qdpCuF|F_a-39|ApJO=w@# z7=<~UH$=j>pV(Iq(|vrX3VKzJDR`6c0V#Rn4Z=)5J(I;lSeCjVzb&t1bb3 zRiev}p`mnXVP+FFE$#a2t+f1{q`umBI!6k@~wE8V*h~y`f2K^uFHQdjJQ^w?c>@Y=mx#v zzXU?Iq6^dm4;v|!(Dm!Nup`HBuvceeYxQv&$IKBDKT(X7)QA4bY3pA{AAi!=#cz2K z=9nI9fB~(vyLUghf1eU+O$@<&ewUzvLp%nq(+_Lg?={io#J^@nw?j_*E==KN*xj!K zla@~iTu_#ofZkF?-J&fxFmCd(;#TA5PKPkf{P0Zw+GqXw^XD^WLz^&OgIQl+|MmEY zLjE4Fd9UB!(oC?X{2?{GZezXqDfnD`1!5cIDsOVBr&8cwPo8+`EAmAYwcVS6^aZ|3 zc!5(ehg+UyHwSk3uy`+?WU1qRg|X$$X^e((^YlC*Ag~9UA!TiS5?cfbPRhue3@E&K zkdZ+n%&p$llnR5nCFC3Eqzs5MRP#@DN8 z_xyM2INGvnC-)yYvIWfnmM3eVY(5IX{MkHcJ)CCTTHo=oT8e_Cql06LMzr;_3&?Lg zd-?J)%Kj;M#M!r<0lN8t0@wIy>oBiFV(g>LaA12dr<%;Hm1D8bly5J^ccAFo5$xz& zN$=+cwJ^rJ|1%U2e5xSWNUfSc4?X$m$O)(IgS7p1aT^{p3u}7ioxgD5Ed)nZQlOO@ zE4A^*mk_=1#(`&klZRh^38j^et^EN@8ehzG>INgBK{o5UEr?OrVMp2^n zA zhvLi2S;CaYDx7-P=al=my~|R7WnLU*I84cXR6Thg-_36sMg0NC_UVh+2mR@=qp3|l zRlK94o!QGrzlOiZ@?JADQz?vz?lzj8ZTD(Jm&SWYM0fovoS$a7gbh$nzbZ!>xU&-6 zulw?pDmtyTeh*CQ@lb6L8;_fk>vY*7^MB?YI=Bj zdTl3fyBIJhTm1lF$x{q8?WnqT^4+zQWwb<({?R>8M6w~SsIydsBSkLUVs5fsGmz3N9i}7*9mbY#?WJ$hc zyf;5Pd)W&DSG%*kVK^ZF?th@u!=dRuAHQ+U!HVF3c#~6@Wj{_CuDw>>4qxKRd}ZaL z*rW!VkA?^jeCaZ$IH6@}B5!9SAc)HkPxo{D+ah=oeQ5ew2+ik2-CZ8giMDkt`)5?yIp4q}5bAJv#O{-)%%h^C-`>cM%+1~XFyik+he|qmmpn*u zadG#bJoyL}CGX^9Zi+Vu1g}s+K4s%MMP((bHVl9zw0^71wz^&NAT_2eukq}3tn7uo z@(p>syfj_>`u%q~^L@U**kf?&Ql39D>p;(Bym(x<#g=ew!5UcQ+w!qWZSG&gjxz~6 zWL(YBKmI~kjD7lCY5X21TW#7Avm?giDi;Ftj zyyH&w>CG5lXdxgdB*va*QJ6A4q(GTzq~LB+7EjW)CMPG4+7B{BZsE}@x@$jvQ8?!O zhRA<;=vi8#L1awc8p3^dBG}{eKOxSGW8e{mykE(j^qHzXPyjYt^GY|c0EX;g#3hs* z#5Zz2yh{|BEnjyhVO$L9$Wd-=pzppqXl{kGQxW|m4wdk*U3`zKb!nJ|NQE(RaTjdV zikIjrXr*Oq>1qYETt5kW-ie33Ll~Imyi7(owG{zxh1hY!+ZPqn z)F$UxTut8regCjtIw8ic7z$WqDG(rPxQ8X|HhgA+q%o+DEsu8{6Z6;`;e>;vGI9b} zZMWF0(icpxIDq4l?65S^C#m9T3nBZndcmB5`+V6qHvVmf!G^FJKQOO36`Kn=vIn?w z;faZ|nf^ZNv3-b)?$*v)>Kip?<~O&vN=ZnpyQs9hHa2}NdS94r4mWUu0+Kx)vgD3A zbR`yp>Y4qshUE{tbapNptGzetxLL18*eyjmwa~q-_iQ2hzfIo2PLqkI|7_oqTK_}Hs4D-;=v%Vmo zwEfN(h&gWCyY~?$`+|=74#9Z!OcVMY=gvZ3pTdT6Jp};;+JtE+fU_H^B+`FfCP7C>+I982tLH%*-R~leB~RorZ?a zR3Y+*8U)em7Fw;TPfzV6Ca?B6Crs;co$!UU#{lI3<3NDg&p|-WE9n=nTXf}GY+(aKZnsG`%DXe z3&Z|4M6md0#S50BH(F2222W*m44LE%2`0}`g)htw14u~Vu*!#+ydhrp{jE6h4nhrE zCSTty%zabBV&ov3ZcP&|k^jhB;mwN^qTlgOU3E1CVU*QTd^g9;Ew=d`=r3tA$%;Uj+}ZZPKXIT$XC(mu@Xg$UY7)m+CyfRYFoSzO2Vb;kEL< zI>11#5estdWUFSIjX+whbXvZ?(vVc;*ednqmW3#ve1njix`POwE z-e>68SRza^W*(050wR(!ubc0yLtXC`%1I#c^D^_K%;GPB$HX)BW^0o@T8a)N!V$oU z?{pd&%FoVNNYtF>f&*bey5hFm-{G6Wi~N?C`(nqj!Isswu(_1o0>aa8?%cE7a%`J? zezGwi0dPjoh4!b*vYkM8W~>9(fdgwn5jc^TYg_rltqbbex_)k(Zx8I@olgSa<&S5AiU&H`_<-riXQ` z?d%+#a((1u3gV(`kM97VIuRD?a`_F@T`%E=@pO!;=}$-^Qgt)U^s_3 zvEDm%&QwvNe0%~M7S`NLaac_g-8o?@&i+$0yhyM;;X7%BYbqw2)+C3&PK>-1&l}Pg z)^E{ar_Oh81jgXwXlLdwQI|DrazQ&@|MTnbY5VDK@2h(w*edu4D1K5s#qji;!lv*+ z+k6v;cG0FEMI))EC^s~!Dl>jxS-DeO^`HBqEGdbR)h-MRas`KT z1H=;fn(uE{-dXvVz+tZ1w<#MN-hTC;-&%f^Tu!^9&wlXMQ8$P6mniA;*w#s$a zzt4RPJG#1xt3v|LdhYwTxsRI!{;c4NJ3s$bpYopxB1m1`-MYGgs~Y-r*8l#lPhY)X z-Mp0)m)klfkAErlQ|>$X>;HV@|F;+Ro`j>LASM;LgS%h1pzFH4cnSyaE=tv?-}n~> zK}F>+!1a5DyD4s?i!n`EW3MGy3@8qXQNkT z$9I(k0)MYX%<`+he+@-lL&HxL?Cp9wT?7f~&r_vp&rod-v~Bve>M$i9WtZ6G9UvEQ zHSe#ToSGL#)`jZcTZ(gvQHaug^r7U`}eQ`($4BQ2* zwwuBNQ-z$NK_Js8Fw(!{oJ3&<_`4uKN7>4Jc=2Z=$dgpNmf)pK~et_3PIe zC<%oL?f^bQd^*$!MMXUR8O0q!n6P%HeFRek@p5cmwo`>x$do&b_f!Hi^@g=59-CXt zbv~=O<0ND+y3)0cZ`=TZaSJNxpGCFsc<}kxE!@Dhw27(mOzN1EY)%R(6 zY3X;sNKQo_B?ikgm-@fiE$c3&b^^!zny-gnHdt75_!yPqzeF28M?v zacV%cAmKMaph+~Ljn0yBp^s;^1hxWRopl;v`I6Losi zV}vo7M6(Mx@OI_ZdlBpw0wFU2@fLPnVnmDsxpF08ZUKLSUd9QawdqSZ z@#>?+z(fuNd>`VC;+e-)W1aW$^XKYKzM`8y_thl)Nn-Lvt?Z=?n!cu;WeiKEzI5xJ+Jc1#m?3g{-jifpM<;>A<`-tA# zlG&f2P-tEqdX#>no{0;o6^Z%0t}q&{x{{VkL`K zaWYBari_BZ^l97s+n#BbOifH!fB5j>70Pw##fC5#h}o#Tyxh}?l9=Yf{_rjos6KV_ z->km;cTC_GKU(QWcPPUNQwWha{Iqy!PG+1}h%UApANMYPU zSW!K%V{7RN)m|2m&wmwrbBY&+7XC2H-?-lmsq(vsNx+4yabOXn9NmB`L5_=i7E+F6 z=0v^~Y8X5*MZ&v8P(a`}M*Ng>N`m%u$FHEs>MsgZHhUtu0pXg3U?eDEP{6{z?_5L! zKX2?iY6(i{Nm3(uf<7kiUW^l=j;y{P7#OH4j*C?`g-j&daz}ObUodoPYM!{d4u!`> zzV53-O~Vr`O1%LFJg_3Vcfnw_$`KTWXfvYIX;K~0d!i~Qv(PPqp)3E<&+zD6{exWF zp*^spmrm{hDV7R^ospGQ)|)9HjQf86{COl{&dbV9oSq%%?|+Af?>?V{to9%}7bo|` z);>T+k5Mvm8?FxKExl&SwRJ$227w^eIYnA<5QN&NPso=q^0*rC4j?zz6; z=wn0>_vVSjk+iFodvbNa9a=WWU8f{QlFn*eP2Y_NiBEWl>ebKX z4t{__xs&b|ogycG#Og2;PBXMfw<1eFnRCsYIKa5h%KOlovW0Q$Mv6;UU@MqJ1c8H#I5By2Zz+xQoMNaf=ni#K}mmNKC(SO zao|26@6~N?jl$%Tk(3n6oyDD4&_I+|^xxY0rnji6p}_+;T4My#mN|~o$yJsZ{v)NT z>R;7}+A~*u%6h3ah>B->_QDLHx&afD>w2%BGHX7V5)~U0<6v$3;pseT^G}{SH9tAK zcAFTghL? zVDI!#?&-F}9U_?>qEP2z@a9#MQ2z!Phw8wxx#Xmzj~I3G*TlNVaO(Kqw_;aOlrhkv zg(RV#Ln=4d&H;4}pukhHsMz1jXxS$1IG4$Gch});n*mj5gy@g{{1{J)N=Vp@l|1@F zmN+KJq7gkJ_&k+ZDeVVUm6acnMY$pfE|&LpsO=$n3lvo;s;ICEQUhpAvCp^nPr#4` zO=7`nq56EmiX_tC#liE|LT@c7!zG?}(QNgEqxzeZb2CyC?6_bd z#?_mQ&CNZluenuTeGjagicvE-p2Tu(RDArJm{vm=ReTrkZ5NW6KEQgyMC?CyY#aE` zcSt7cXKLb4caZkwV!q(Y&s9}Y=u30zfCnv|R@t#0@^?6N-0!@JI42)J;WS4n3Pi}yM93}&{J1$#mJMqK&hrGZP^PA)DZL9Q6^Q4MFmm_H#!(QiBy&gQ`cKn6x=d!XfQli1%Vnkm6`@@JmOx1OD z>&_{{()XxQs%U6zLXt@>1HJUA7C*#h7oSK*_K1ZRx|48jPMkoYTQ!*~JXFYjf40`& z^4DDFhfYxl+X-9g-Fp+ZeOpo=%k1nd0g)yhU&YMCbT1!EQ}xG|9wK3r~jt;^+L6u zS$;}2w$SF*y5aZbi$``g`dGV`6=CADfkPQ#1j%b+Yd4)6)xN~X!=q4NiYy5a`1r`6 zi=53v=slu1lgwm2(k|>h!b6*KlZpRpTiX-hyIi@SA6lJ*Q83oE$MR=aeW6b=DpO$$ zG4Wh!d^}9Z{w_v6t3$!7R}@#%HRN;meA(ywjpl>gUWWs0Y@ZA= zqECz5U&QCr;HNg)NE`s=JY|xo9&VFb1Ij%?G9|~B7Vz#8&ae$vd!w+MWn1sLY{CQbfG@a1`JsUW;-cvRGfUZ&re6B^F`u(5a3zJs)gil3bkV}FaUL3o$l zM>V(@X4m$X!8_nJRkI_Q4EGM@7GRzCt&Jn*yeB(WYf&2V1wRW89tb~Oy?vkW>6`T8 zx9^0qNT;>?sZ#odgggN+-UW5xk&w_(?(FoWq{>Kfg02U6^ER7225IVDIzM0wPR7>a zQ0UnL#O_6k4MSX$P1JtMD@W9J?h$J; zuPPn>ve|j{;9}@?f9=RSz-A(`M~o2+W3!i0KnJ^|Y` zHb18{{L`-0*}(}99Y5SQEq9DgTDH%&f(5l<$3T{J_4Gub z5V?Lgt(pv0t!n_o_PV@ZbFSrQQgBnci~T6&AHOX4L6akSz+`k_ts?~o z1)g4(Tlc0|V94CFq~i~CX=!fmqBlEdZy&(7P0W72m52c8#Q@7wy4k0~QSqlcLJ98~ zl`HZ{{c~<+X8l0hC?uSa5?%Ybdd;2u1Qzt0H zTc-ygG%Mmxx*V0_|8x4CTE6#_|BJo%fT}9%xw!9#F=LCRpL8Y!)JCj45n6kMj*I) zw|v}kdNpz=Yc|f3yK3IpT$et*jZSTs=Sy_>aP$3ic2{;?6XQM&GRwigtQ&%w$y=ds z(GmG|qtfBvYJ`={a#?`|8Pf~!P>v8j)VB*AJ}eo-v<)O4p>IQwJ^bUxk6GHt`(ZTK z^lyuS0)F%;GOH$&=lao1oqN2bhtBu8FL3soQZ<170T|LS$>z*5{);31k52hGe4r7( zH2sGG%@Jo>w22%NwHeRBa=_z@RW*nATMv6ooj@9tSZ8#^w!PyzmpmWRyzB@2l%69q zKlz?r|K*G4b^0k9k})!&GEVt0NzX<4m5~)l?ll{l^l-h|k)-K)Ll66?6u~Ky*+(y4 zylxFd1ba0$HX`Df`D7ttH^Mxf`Lg5Lp3%mBF-!y@KhttrjeY?-1zELBG<+M2)%;`X zM7GSe@Sq?sFzEK6z}|I2V6YnjKl_Iz^U@P>RK)DFWF()arBokQ7*7toykbnocyz%@kBGHyhp1!_{XU{$&-vVlKZNu3wIZ1oB-0%I9FOiA)GH>Ksx=0jb{;;zx zR?iXGx9^_SEZWC(KE?!ShC=Lro*hMs0(> zuz32YS4OrlUUv8``3Jb*%h5O5?=nQTANMCbme;u1{KaHH!OOE?nqSi#TztVnbm~HU z0GprCC@usw6{HJgp$l=GiH}tC*l*2mh7x1gnWXvv_a2R7{#M}UfO5$4X{Vi}hs#Bb z@&1#dq)mHBZ_hVV==<(UR8&BCg=CLZYxHHY(96@aR`aU`9(hFXOCAXcRupu`y9O`p zkn=pte);@^{KfH8{{3cvVmx0{zum)5pp<*uzx&DQKk9_6!hdrYN4Y>oB0SktMC0h8JG;@p&>5bMKN{jN^l;0oq5$#3*&v)^#zSJp1Jq z`46djwG!s;e3I%gjSuJ1J0Av8Vg)?*uUwv50#o|**)yBxK!9NRf%rGDGdq7{lJgXv zOEb|s*!rH2A%!bJoJ%R5rU;1X`aAc>EdB>y^z%qchYA@LBiJ$8IKn@{$Ax4bi>NbnK4g z?sZZHEyDv+p2^CZ9-B{5XYv$LHQhP*FumW`&yOC$ex=Sa;zl*#0JPO;$X}QH#{90&#zsLU8XfMZG8U1pEHAnT-kL=5Yr1S^x(q=> z$(MUGT`P!eUd!=0`3d7X7x!8@$@9%POrRuitXZIR$JKlj;|qZGn?4H3QS~#>M$UU= zF#dwL1)0v2Sd=bi$BrK##u*2ij4Xw~N03+uuasS(+9iGnu>>^#fZqjC#c*JxC~``^ z^9mI@5RuRj*M?6#ZWsn!{Of!-&7>4*)2(46t=mlM&o#GQ=Zs^#i53a*5=9<<)nQV0 zM^;3Hh1D9LWTtAII6;lDz>nz2yH_LXKnZheR1}YxR6jxum=mNOa^~hjpTcGQ;cH>F zZ34w+as-L#xcDiNM-f612KkWBp7$Y7-Hhk?;nk_YAMEE9VtXN;Gix<8FyJK$Gm-d; z?NAKK^zyR+RzzTWM}&o;p)1x;i9qI)TF+~eG;C{k1AFx8S!n+ug7pRFBGys%zBm=} zBp~S!DZ7h)j(v%L**1aU_z4g>k%@_P`!BwE7;R1Qee3jhb4A_nICC}ggQ+!lP<;u& zfEfW8Qj&tjahgAdHh)7k1X95)-FS`76~-db>FHaEpP(y$!Y_@SRw!;(l6vUH<0HeR$+PaEgoAq8^@}&iE##sRNpJ0r=AZ1=n$%_Nn(M z5+@r>j#v*?U{77!4sPXLX0bNXZ%}G#7W@4K^bg`A^bu}uMnhd9<9np)oPNd>78_0s zY}=*Id$QgzrU1pCh$a(tHV0Un7|Y%tD7AHk#@Xn`jY0(UJx`B}U11`U?R;$YUve~p zLt3|%1e{rFiR!?#jEv%McRrepGRv)zS`!e(hp!J%RRDb3wQ`Z9y39t{+BJEBIZzia z3V9*E%(}{D8IJ@Uo-bRW~RP-cR?l!fzHEQ7MY-Jel+a7n_#44 zUUO#mlb?)tolEu4YBh{ah2;-6Q1i zx*nZ7E-B##QG--WRPAK%{{r^wM|p(C$Chw2LGVjcwYK{g2o$b{=^Bg*#76{0xLslI z$(prk`rpnsaSfw)M|{mYjRWznS%CsN!%WiNSCVu51NI#}_$=J;@C#K<%>ye+nnRZ1 zN?%+3O3H7^%e!cD0#D|WkZ9{Y5hp_yZW=dhTKc5@b_+AJP3Yrq`CQYgg!>?FaH$l( zC?nGqtyx)7abe&oA&Tw47q5L@ODj=4ruWAWBFANJw6L_)hh2+bav=Wh4zSxW2IDdd z@i71)04;Zb!yeGd&xKVTNGmFKXQ&(^q=NUuUVn#KQ+q}RANa%Zb;GdMiBqSFK~~yG zF3Zf@N?I!3lMaXc%-Fvi{9FKc*ik>P+U~cvY6$+*ESQdR9ReN#Lv6^ujXi!48IxUT zHf7ulyJE08lB)YeEQKu|F!G`F#dk`U}Rhk@M(~UwPy`m5oN-5=K z)pc|nJkIDn(*fH6f=DuO>f$oM==ihQ@U58+3+elyJJ)#f@Zo(TzUY+T{84%1vXEnx}{!1=dqZ#?f^GwlG#mlokt^ke;W9;GEBz0 zi>(qqk+ReSD{6$Pre}n>^7~i$n$bDlK?Z`Y)yXh#6hGlGKl%D+vV%@0gN{`%4p3eg zqYSX%TjWpAAYWRrFw479zdQU^_iQDyDdDEm<~`_j5~MzEw||=z_76bv>B~wOKEuKN zU@KDe@q}EhF7>arrPD}frDM5gi&7np#CUC9S!R^Up`UeM?X2QrN9*Phvc* z3bjenPPk8wfjtkG32fQlV#~)5j34E)-deMksVvWVlwLb^>eO8zO#}j^0ibzHsrgb^ zH3{^y3z%Nb4ImP@J>ip)0z^C<#?t`*>n>9p-7Y*{XcW(?-dZ3(fFJ#6C&fp0pNUHvf|auT3}nS{jX!3 zgHP{v#xVLD0Fdpdj%#O4W1} zI0{ONitCTdZkBtnaaEf0gZV397g{Sw7c_DcRr>siJQZH^40931c)q-r9MyK(|5EMz zzi5*#c)U(bR0L=|X)8tOr&dK&{SSr3#I^$tTNmJYh_sO#&SA$SrNZjZh`(C5!Hr6A zWl_n;F+w{l)935<*DLqFN|Qk#FaFh-#C^ddxoya$Qhfzund;T{qwmyJe-Rd_bJd?%18cvse9>4juG!cnG>n{ z`;GD2l=>04Y$lfuRpPUMe|w#vDl~K+g(iG|e`oJ2XBIx%NVyVS(JlYHwC)(~7B^LH z{bbgS|Ge}D7v)CWsbDjm>p!=7gmIlK?vQ2B?Y}PXe`xh;yEZxwBxW2!)gs!q6NEC% z?0D7-AwwQ%X~Ir;`1_O5L4ROuhNu}~0!5DylHS(fpU2QYD!lp#moJV=jO4lyuSAuV zX~B}nprr)SyuWYC76p;aU`0z=O>>>mjDQ;-gF>|IUn9L$FV?}n0e1g)Z|NL2h+P-;;o%u= zy_KYgS+@vbQLsuI`OgO4k-WP1DbAHcWNrN{CeWaZe%AIxhsLXfWAJX)8W$WcoOHZH~^okAZ7Z@df=Zm zcaL5D)X9toi5K@Hh950sHT`pCD#=MUX@okiqyWJ!173+YwaMKO+kN)$b96*?^?3A| zt|yn36`5otY@vBbE-l3;S4w=fVeNm4``>#$hW6h)UR~k)DF$TyA`-PrC*$8YaBjXv zUZ6+lpCGhmSzs?Zn3iDjm{K;f;baL{Y59z3Kr5-^m zfQZ0LT=-88s)!Vz0>bVi>b+6i<}W>O^f}`o%k(R{&(0Qj65#kp=YsIS;-+8Ypu9vn z0ZEKY{?gJInZ0gI8qY^`#&>S8E~0!a13 zZ+E_ae8dZN4mrZ~$svAzdXUH{iyCU`ihYIY(vfTM|7g>)esVs{&lviC4A@-nUhE78 zSPy0Hirv=xRvC-#(x)V1one zhKkPCKj_|~K0}ZgEGvk*EwP}aw#>V{BF4>JD=Xloi@x3A0%g$GW&~38(Idf#kt~xA zaa=LoN4rqIl|TGHm436#W3H`Wb(n9<==3Qmj?qXH{iM`wvd(8@dSv$Z(b3tlY9yTY z&%zQF5^I9zftD$(z(@#F2R98{;{`kR-B+~p1Arc40U<47x6R|gdO0`TEVUcS+q2YE zuq@n1XL(2~*IE$bml&0B>Atq_CS2TT`7yzXI&i>Pua~94y<@$I;vocAkhmSvE25ZN zgfbx6s5VO4lpJiDzt<)BTMQU^EC%{%4323kD3m-jW_519!WvOHfxhOh_(`G5hP(5V zH$W)@_%O=;cs8d_>7&2$r+m=cjA)-N6ncq$?e1nN210yp_dGmXPIeLpzEj zGpJ1jybFYA-RK>!C@U+QB->CjmSoW0OjX@`z3RC$s__ZaOeihx-c6zPn7<-`T}23! zu-edL3KW>5AZ<|_)e%`YC6Kwe;}v2DE0DT^ZFbBM?XurYsI>wPUU>+4hpG)#{dclK zaiFOIIevR{?mh9R+ojs(I@4-rW4_)F>Ad!#&{t^eQQpj<@++*^{j=y2_6R!*bJ6Y5 z*;FIV=ut=z-!BPK(QT;rC+-P5$`2yCs$fwDEFw|u{^8r3o7M(J}&%t zqLK_A0f6IlHv79bb#+8P2CP4%WD3JM`RUO~^EYGj7tzb?>z$)0T5~6O5=r7TeaBGt z50L-j`zWpHj}5PwW?^Q2H6M&5j~Bryun6$R!bBzvKVLo_iWlRJ2}-;53QDic%*=>S zei{%^F=|uwTo8@Ml2S0Jx#8}y=TJWomx&5XIq+ObZDSZSU4FZ1M=A5i<-@R7#4$CT zsUFXv?+g~%fpg2f%hLYX?fa_%K>m?cKZJ+@I|!DlvR2vFp~i^sxA$*QvlLu!it)27 zGRqCpYy3St9-eQ|#0UWK-KU@5$9xBSOeBWw+JH7kcSgU;_DxBJijTDd6G4B4udp@m z?AhrzK=bn@tW8B&_F7uipH*lJB_0>6YU0siNS!}g81T;8rO+m>vL z1hUmKS|Kpu%wkBwHyG$UN7>>JhvUX^r^$9RCSsxIeRiN=x(IpyT?nlS#V_bejV!Zk z*egj*21DEGNBcC{T9q%K&APFhBh*{IF?VG{p0C}QvsW>#`*IufGe2=ji9=W)C+vN)Tm} zIg&t4aKCq8v!K3YL6gk4)jhW*U|vzcN-P9~JP?U|#T%P_iOU%3^UrU6EdW#CjfV*9 z8G_+QQ|&Ms$bbCtrpB#l)DSTpCS8WTO*d{?i(@KiwogVzC!hfZrK&nQ0+lE2n;t*j z6msh0HPesp5PQO`B0L9zqt63&N`HEuy%(ija!<~*P4HU97JHyRaY;!t zRAn3Voi2@+UzHT!=AO|xDe#$nDXEEAvcr#8bP5K-SGQ+89g)89eD)Gyp+zJxsu9|!JtAB;nJzGp`M7i>SN9{LmPmN$`!j4fX3^XK2Hnh znA0zKis=PyF?3wfjUD`T5pYt%DG3^s!Q9DO^1ykzV*|H%5_5uSBk4J%eb6P{j&zD= z5t=xf$@#!<=SEs8}`8mY>?f% zDPdTf-(B-=r`PHERxMhV>*kl564w>^k*q+43G=r1LVQb0d3Qg*K7G3)<{~TOR5Lab z(hs?$CeUe{V4?$hriU`RDw?w0R1pyq;TE=IkCeZdAz&gSQ*7Y7G0bRO$AadDrLptUv_yrtcC!<48cq&y0-ui4A9P=w+z zUAQ+Oou6WcqM+8%~?0buvkz9Jm{X=K%D6@1Flke$C0Gkw4qV7gf_BM(Lc~kvRvVf~>`?C)y}iAa z$!OF=v*qDRp`djN2Ac@5fGeE_*>Zvy!+N6p-M zfzArUI#SbEL+p9Us)gAeo~g@2U&7x33!RZ_jbd4|OS?Xh@fR2Yjv;BZQ=5$;aGkUBaZ9KV^`A~|ds(=;#!H)%%~r@f0P66jGNu}2jfy}x{|8M?LZF>RQw zG6Z^P2}Sf?dOgd^bn*0y!|Tz1>oNBQ^rX8P9jMe=)|=5#rVLgtQCepzZYZ4d$%q$7P!AQRjOdD z$O}UX-JWF2R5OE~A8VfXae3$T9z+)%O3TaP4Nc>W-hAFux1>+mooANopOUKgHd~gU z7FAa0E@TjN>`K>8pd&m4fZJzDOtK!gzV=?e&O<`W0DlNZng*^>HFS-m^+7=WA7o!v zBK}9TH`nfmGzWGeu4sRxOxw4!GpWnIr^rX` ze4NQ2=_;^d^zOwYj5pzsIl{S>ByeVJ>y)G&Ga=QOVROZW4gzami&A)O9=F#P>& zJALFuDq+S2Zz)?gY}JVcejCeMHPX+*N=j!}Gv5srtKioBl|qPa(M69yBwl{!w|cVs z%;Oc5wMY^u!=e=D?e>!+=H6$mc`O*?Z}YC1z@-dpqx6s^Rj@7k`g zv$BfPr&RaDCb@__sUv&R?tUoSc}zbdGUW!_X=5(RwPgbC6y0SM#+2T&`WCx?wM%P_ zWT|CMgX?~)c*Lo2VmTr;@R9H#pX3uR6$0@sf+u|=;$FR{D0KF zaCTk;7QcANvT{^+W<#@){WjxS#~!&a3{$p*h}08<=PA~%fw=IaY5`xxvK*g3nK#g| z)mUV9$r1WJlkDLOiuktfamf(|i*(d*eWX2O~DvK=I0OqeXH(VJ)ewl0J1e3O*a4HJrO z?G_*3H!$FW8m{7R$G8|j6cw#SgAHe-E4es$r$B4XD2sAZw52q@sTCElyRS| zMKgWl%)Qn#D+8HL1FkVQM(6AiLph#$97U4W7|f?D*eft8YdEufI2-CCJPQgJS**R7 z!LjfZ#)E@|-bG+qjwG_!Ak&PQhHu`y(Y%INiF7@NkHYdUE z6^+bPzDl0oVCi6r3F5oA`{4>dz6x*`Lw~=ovc20EXU{M%&p+D`o)%uXa>>fzYvcAG zS2adue#+~dEl3?ImB~*ia`_}=*Qt{$7$J~YpClnEd7y0t?RvNwsXjh5w6WQa7BD+p&!!fcv>7eQt)qz?@$Y7`_)@E7^Y<-tnTp*kXHKb#*kFcBHFSg5xjd8u* z5tEF+4moT}((ya>CSS5@uK8Agd$CF9%;DT6KW8I)yI}@f>41O$h{m^0j7{HkFV*)H zbMyZtd>ti+jwNBIj^FiTupOS7;2e=W9?YE?-s3|Sla8g%c%5i;IiS#ctuzO*5pZjHY3!NDu9q z1%b-3B`+E)>5pPxh62g*r_`;|^FFsvzm(y`ibRQq*6LF|i#R!bjNA6fQ8vH0%AvlY z?lEiK3?a6N?$S%D04NlW7q70{F1rY{tXTcpu9wClw=3?klqycy>X*->;9`bf2UVxJ8<%l&>A zg{!20uJHG}@Ek7s|93ej{mf#Sr9@L}e_Q6{ zRM0xLYa?v)7D2&!g;P2qWuXja56#9E>cSa_yRNH4>cy6iFPj1;S51B=)Uv3+MR*D@2lg=H8IQdyjhh38YkLJClSum_2*P{ z4cokJtMA6O1xh5&>J|Hj-WJGTxQ)tO=klv^Dk=;jqN3cS4}*gT>M+)^)6v?R1*M_m zy%>HgN97DlVc;|z6a#GX+n*!m78cBC&h)2k0mm2Fd1I)E4)EnVtw=9AI?1)Rz;aFJ zTWM-5Z#BS2a*DLydG?aMFFkAf*2V6eeTy~;n+P9dA1U0AzSsD=$F};o8}uYHQ30SF zyBRqh3adUJp~K(M20&k`vS8yrR!ghkHh3qagU-|NL5u;M07N-@y{v~*v#LG5Ney@ zyu3WvF7djBs*fM z*{o&vy@c~yr1sb??aO98a+ygl?@=wUScXro4xg8%s*N@Il;y8DP--g&=o-Xd;D1qB zo&43?S>#L`)zg8spOlKDa3{X`%BT#ki;721o zw=?K@i_$wDw1t8~ijtD@kdzcr8}c$He`D=|tu*j%qtHRN=U6d-^X^Dx$GDF40!Tul zmiPl{u=iMY?C?73h?+}UdU}0(dp7757U)x2Sq}@gKXdQ5b7h&c2n*4gp-Eyt&lz6Y z*!${vzPg9G;koJDq`dhzQBi9NFNto+T%=>@a)^?&9`m=jr<{E3ca)t#xC>kBhUQnv z+x5pj9Z!>nb^Pz)4g*%jmD4)Dqw_!lW`$Iv1zF`~d&nD4Slm{#)t2#T`tGk+phmpe{Jewt3 zsU=@JgVV7xeIU(#B-S)xw4urd5#3qq-Rt{ft7a=l_Kg+0v91{8v&lGi7j9b@`6^JD zS!8k6cLTy7m(sI)mRas}moI%znLK}eF>v%N*))E1sQ$3MusO%Tii#&~Bw;}eKKMG> zU37!3;NL>ldGI4SI0g5&XULGp4?+?uZZ6V?uU~bWQ-Ir|n2L*$ifFM(#E0BpfV2C! zx~`|9u`)H z5iIcgKsC7AGOj>PhYkWD1XJF>ckS=L@jFN;SLMAZfn~(r!dpU%qS2O+kN_k0{W}i` z{Ed)zR316w!nhWJC&I{LRG8(<#U<9rcmL`tI1p%Xpk7aDeyP%5kyAOb9o?(N%jQ0pT|K#1<$Mi1)p7}tH^dgUTk_wHc< z6hcIIN|U5&Rkn{q&}ASuyrn|Jrk0>XOh7RognrO zh5{q@MAP5h(>Hj&J@2szqsFl=Xv$U`OD^4J)ZU-;_dlCt@$QR4|W+J zwaeBvF4}ZB(OnfAwKTqLR-hLcgnWgO%<06;N$S(9AI-U(tUC_^nQgUB_vu2l=aYYD}3p30IGyBh*(hP z;&1oyr&Qm$vgZM#+%Pk`$YI|#B&c_%%NKM(`RKuT)7EweLVlbg6l-IN$Hh>_b&SgS z=g*%GHTSd6S?oLTN3LoJB=+hj`nqb4dq3G`F@sp6Q-h z7(b!elEYKB$b=qEu34!0?vhQ516`bdBO#CbJmsnRYb#x+w!y8+Bd~tT!L`?m z$8@1b-ms)99_RDixh?qRvB^R)o8S)ar?a_RRt!7XYel3;^9@t#3u;RU?qNH-rDJWY znS{Tzo?*}+F(O>O7T>bMAa8xkoxGuA0yX&8Z zp-JCTMOUzcH(_(>xb4E2ZFs1Z-BvlO)ih3g12xh5o$c=1@1g#A^9NePRAK%PYn*%f zc_Ex}?e9De(1z|IkIqhQv}h5#uKVJNULm4)&M(&nZI+jF)wZ{Owig#LfKUm!>bF5SqjY zLxE-+}{HE);;+h+dY_WIj0y{hK!BD)JL`|oreJ+ZprF@MLZXdy$F)Z`5>exj>uFN#~2 z7a()RN56Ox$yL}r77-Ie326tAp!j&V>dA<1=7Xenm(Hwus}Y@m=-1On!rBE&KBcRh zD0A#5)dh|vDsSoT2s&?M`mty-PxW)!QrqFh*e=(%td{_etR;NZc;sfU@qLq~c=RZrVJ@;q)ZC+X#>dZ3|Jt?nU|q(o(-toXgV^jslUn388CDZXcG`o5b{RjnI5ywu=Im0_11uv@=7k_8;b9+vPqj61 zY`-kIFv$kz@_|4eN-NZbR0w!tq#$ReE_yiTvs<-Pb&Fo0sE9}fT~t$JjvB8qCsHlOt`T5S?VN=Ic zjRP#4oPp%Y1*AQ%wGYh1eY zhzx}3+QZG>EpK!!qBN$RMmC6#c`-@;x&5M!a};| zujy}+W;;Qs?G5fQ%+1>{?HZBfPO8BCO9fcpK< z`TMV;G24lPn*wEBZZBSJMNoHF1t8s4Iig3v}D@*LR4gJf8=3K2jqY{eIgx?FYRVG_oIrY+2Nc#3-ICt`n|}FT*#l z?$+Pnp19X1kr$^`)tDjwVIrd>D&oct6h*C=IYMC&7hkapeXS!SxdD8Gr|3ox7A7)E zct4}A0JT3<+=q@Fp?FeGLQVWVOUv(=v*MF^1}6|rD?l?HLX$dAPiiqsqT>rXw4OeF z8Y50l>1CkZR-kP0*RNds+O*XiCy;+5$AX7LhbJ$f1vY41@c?g6boWIAaT*$hsQ0`m zWdPr)bV`Dd+y<9c&ctvQimCD z&Z_N_UR3b53*YrDt8aGlu-QiCp*fB0r%(2bBEj#f3UJkO%>V3F{d&`v3!_UR#NORh z^BcfcG{pE}V1;6BC(g>HMq;v+!KZtu#>Oe-+qduJyzZ;$-Z5(=V2o>l*65MZs19lh zxS{)?3-oW?IN_#B$OX4<-O5FJSmO9Y>n3VJ!T-s;S>8bhC$f0*?#}`U=zRpHN%^d-FpeXzXEBd9Wi8xSl zROl67h{{t;^$lYH#N8OauzejG$)#T^7)Gt`-gONcLpb7yh7MEYp_G%vC>R*U;TrPb zJaIa__M7wgH7vfP8Y&LdeIM#`blN-knh|F<#VN135B`_TM1=LHgInH2d>g;<@>LmU z2I1Ef$c3xtWqbeC*!rwqGx3}b`ZM)K0XzFSo2M8eCZ|DAJ`y^OmZ<9$&;A(PwnSvC z^9u_-HS=S@hZJBw!R=ut%mD!bvawFu(*4P6SF^H`u0KE*Pd1=WciRBr{p{@}T2+fx z#(<3)o-4oid~6`Ng-LwJOy^T6Z3+D$>R0ZnIr5J>GQ2@lb(w6VrTjK!JBAsJ`g(e6 zL=vd6k?wVT^lSNqD&Uqq^MkMR%8unS-n2@N&B%j^{Kdj2AP{J9=py5m+qBS{$Wbw+ zrws#DGgDH3V~I5;2xJ#oJo*Ft{4jHa%{qqaGG^ZzzwSbpd7C%4k0}~h&DM$AWizvf zYr8Fsf1B>8C{HE;A%hyUu52EJUo{=)UCN6Y&-W7o>5}~c`hr~XPLe+&@e9qQ8Op7&ku$-xoAs~ zY#l~;wcaqE3}@OxOL_L(IUadF%;^EZ{e7&XSx*Tm@S>v(p4qj6VVDG0RcMKQ=j_p? zZ;K%1r0UtO;+rzyahDqF>v;w2Ci@=Ib`uvMXZH|xp>Sk|bHJ>sqiXBsR(Jky+e9mQ z{Cb#OMfM6vhVK=~X}Q9lvVBh0h@Ta4I8)bSLlMrAiJgRF^(y^shtpj;Ohm!PV*uvM zCwt^iNu%#VPk{G~*f^u?V0k#+ur|94~ajM}UZQCvTR#9D#Y_BC&P%l$VGuEk_c~2MJnffN&wk=EF(!uPxeOl zvw8w1XBn=*e3sSdKaKM@F_X-$siuadFX~I(il+jMcr|t%Z%Y8;aq?tEzM*Pua6(E7 z({H>MT`lF+)dNFbU=-*Bbk?e*OXY<2n}BZHel0BYEim*Wuhc?b`J=35|Ni}YGfnO7 zKGoG{Y{GL25^K4<6jd2R#{xocZ4FVNAi7uls#)pe?3**14WuRdI2#xkm>JB3 zg>i@p-AWQunKMfpA zG**+Lmpbx*4glut#Km34jV|tNrY`6esX4s<)s4#~V?;cLjW?>Ybx&U6hN*w}bdSZY z(m2JF36hIb`&CM$&#rnLPu+ir@lHy>))12duJO(K;@jzn3Jl`kIqjM6CWmNm)Tz99 z|l z5Y%)*CT0}mhQHzcQ0Nb}EXrF5z66mDU`RwqkhvllMmT}ijn47$;89L+Ca*pr;K(Q$ee;j^!5t>uX-29oLW<)C|L-U*>mMBR$RuP=MC)&Si3yw<9zp>PFx(!+oN$}{zMepbEscW-lN zm8GJIHa!N`16Kh|6~5xqr5Cwcva)MHax6iKkSWIrA}U<;e*`XqjeSZ+ zETek<{5tF#1T15+?wuTvix5ueVmQ;(>`1`@LaMdWZ}rW8%}l=Z^VT=J?HAH$31_oc zgF3h&t@k$0B);m1ZpOIZ68|o1BI~%L&hc*rF5R6fXjKkN!bC*@6+-k&nIsML4N1w( zrRAnTgaXtxbW&v3DHU^(D*g14;CK!Sq0JEwxjeQVRaQ6?LjDa3SgUy1&dT>ugNeKnwXK>0W+hX9RK%e#> zWB-9ASwwL4za#qrF9BY7V&0qxexov;teU2Ok1Exv!bq#B_2@JLLV|&VJmZP9Zoko# z<3pE6I=y;7nr!}*Va{h00Ar2q491#9{X}bi;GU(JT^q32Uu;zr*(R(d`*5dVmqAYc zcz*Iffk(j@0-ctYhK?z$Aa2p{ss!ONShUYAE!2>(0IEXFN^*Vm-zq(3jE`58Zzj|a zLtO!iX%0q4RL;l#Ks21`wBlGYhL&0F1x}oZ4k0U`2@YX7{I?b$_!7&0(!0xv=*1&z zXt)PK9sAauL=PgsE1(vRLt}GbYh}J4`-N4c3iBU607okJ>p;^Vcm$N*f%C>N%V#4r z5K00A1#^TBBT9H3qEvFXEM5lm^v=q%114}le*<|D-$^4+eF$21@7Yt^-p1;vNxZ(RFHP=&t;a~&<8ssA7KnM@Dzem#u z1FMO>5UW^#ftc0q^^gL4?223UhsXcQW&XyLo>wOoxxuNTJgo?kIVzBgx5kpCjD*TE4uzPeB^FF!mYf_}ilrO@ge zf`ka!?bWMSKVDPJfxJQXtX%iS0@T2+LBAFxmJf=*Jq%n}ay)SG;BN#92I}94E6~|~ z=Qcc>1j212+gD+{s3`=Bhs7#D3%JkR|x%0h*8Wdhen=h6eQHB2=JAn14PDwEdli)=r z-@9EGW)3?c8&jOy>!=FNBhELaNvP*h2>&S{u6bz5X?~Oq+nQWe<>VALzl}7J0HR5Z){}iqzq?WT**{^-P zp@a9tqbXH$T81o*f_BUN05FFFix1sngdG6a7eYN1;KfXI8)o=*_`BQTq|{4i2oErz zG`anJxc#_i2s>=)rt}o6H=y_#I&xg7p)io=bLf9oe)Vx-5w<1vPR|thASgdplWfU+ zyRA+u+53jgzsN^)oQlHttwyw~+7xIakyc&pd&&2WXmM7&O~;;qQ$<7&B=EXCwI=&~ zmgnLog}euw?hdH9Vt_zU!RCYwbkx{bQN41i)IP*QY8og-HwHJrvm{A)4cerY3 z?w~fND?kc5*fwzw!m}S?s-`K?w2|NhbR5+^$yPOGlEZh7j(&6JM4#PtU<}5`0Z$#* z+C+eJGoE!L$HVVpR1BCe#9Zpao$sthIR*CdfDKy9nbD&ll`g4GYViCXM+Wn; zc_IaUv}mg*rApvcc&AC{wyY1T+&y!zt%l`ud!%nU!?^3(Rj;t&x4;__4diG@YtTBg z4%M-de3AE0uFRmsAHMytkbLE_$x331@uf@Urer(se(nn^?paKn7a2Co-9G(HsiP_j zt`GbUfEd8v+rgLZp1E=E=_q4)?*vZ&4zG7sR1K(D1PFy;gca8=L3V@j6?^$HWdkI` z?9#GS+u7kX2qwKZus^D#MxO{m@iB9vuHc07*)7{`r)Fk|Zh~MJ^&*FG5}>B}!J&bz zkeA0UM@979!=pAE;X$VUpY$22y@F`wZryW)2nF48#-|%}h&l&E6McPrU>>!!FldQl zi=yXt;&2M&&#onUVwIZIRpU$W;8N>A0ig?SNlF)Sy%U1GGl(O z$?$sFO*|#fCclo}pXyEWcKaGGl!Ph9^KE3a6neYz{oaLZpT5Ke7F;pk*h`am*8iDA zw^MWnJ{X4$z)u_3UioiBC=4YhhVxc7pPpYS9 zZjyf~2re&Ms3~wYp~teQn9g6onv}smr7@>WN*o~hwP^#_C-HbXA9}()EjRsgb(m_xz#0&al&c(5VzTKbd!(t& zo$AHK#b!3auu2sb8$qUwf`X$33w`tEM!fNO+qxx388Be0h`XibR;yfs6{5_edsB69C}fG9T824cxlqF7?+$0 zStI!gs3AUIV9nohp-Vxs@%BN4kJCr%J1`2&HaUQs$jrm*$0& zrBE-CU~#J)z7ww0<6nwPWQaj#@$nqxqQ!Y~YB7RCBu9h88K7l5*mJE9-y(h4exi-D~ z=PCfOc}5)gHp>1V%3#_&?7~a`HR96q15oaL&`;Spc=x<=*s;8yA0Tko3k?5s(uG%O z=pzT&Qk*$9wNCeN=%4dh(zJlC;n=wM@4wZR0X|<5y%p_cBu${_1+htOmm7T*&1G=l zCM^}7gopbhav;gP1tS+zLQsv1o~>+;i@|An^_jmTXO`uxZe;7D`oBL z?=NFC&3YgW=g*r*tQ^{0S_i*^x-lwl5X|?9ir8*MC5&=kF){82@9b7&aso9DZkpC)q4oDwB8fN3PHQg3o1tOZS;pj8UL2tS12OBhFI5WED=9}fH zP>;rgT=bU(WNSo61*iOfEu5KJ`gvyCZQ4b&zZj>0jm10k-`o8|u7JbredZtA|=MP zNIP&_<4b&)yo-v?e3d?I2#k8`sliWh@#9k_Pz@X!X4I7|~*nF2Y z6a;TyAC;ZtlEdzc5||RTft#Wgz3#iGr>68Ej(`1nOr{-qAZRQ8!KJfjy%_-Bj{~0-v<&eSHOA;fLNj|moIDP>ONou7 zrC3WOA_z84a9SNd*vc}SRSsH7gQ~$+m`HBlNbx&Y?+`POahTn7c_c5$!SLTIfSOYk zI}5J3_^J4=?78C9fvI*FOMc}yiUnE;%g%T3U@7V~4!FETqjO;8Ad|qk0?z$i=Q=qv zgXH!9>g`LQsoeXvJE=tzc6QeLwGQEtphr{$eShlg&C1hFv+ zDD;Gc?e1B%`(7jM0k1zpVS`TqV$vvO301UwY|e93cktHz0E}f50u_lBg^#ZpWEkY3 zv7towEv3bzjdqh$TF!mxj#WS0O8R)Hw23aQ%5=?)nS>StN}N^mfq%qaMBPPGpMc=V zuQ~BO)AF45gAM1aESYLu?l93Jf@485FS*bO%ZhW^UW#{O z$<2a*43Ws-PcU%w_1mEm7|lkfi((qh$q)9k7M`9tTql9E5c!9{gO)(VMQ-aNm4iUD z0Kx$lK-sKiU_haVtx6&kPmrT>(dO8JSW;1u*7Z5G5C!Ewm|obud%|v9Sdjk}jyK`` z3{q*pU_?wASX=mkTx?H+E{LgW2)Jfwg>;*DMK6WX1~*_nVr5$Ijc@m{8THH~bh@5i zUeI|3K)U3Co`MIU4Ds18=8uF88&-P_*w^SLcV==EVnSGw0FGt%P zh1sz8NRi;;$Ud>Ez9Es|7n8J}H*fxgOsi})F1$jp=J2lT{d^{>(NVl^8r-xZ$%+Uz z0P<9@!p*^9^HhCb8V}(T08Ej9LDZ*z@K5Vp22#-uyM)vao+l*q^q~NU(^hR*$nlKjtfeW6X9TjL{e-?Agk+& z9iB~A9x9vOMwNtKBFJUGMEiWyEDQR_bGxMd z3c3lp_$-7u?X|W!4GLZylw1rM`Sx0)kO?ojTVwrhtxq?Q#~<9tu00g|NokL@?!s&H z)yR9weAzRGA`9IzcFU<1JnWR{ddBuOSe8C2A-JgQ^kA+Cc0a_EJN2}WWnEhxJX_g{ z{DX$ZD5+r6N0$ZR2nDlWh(u7heU1az#!rHtukAuEfpC8z68`>Trxk{(d5%dRyAq%- zkqFaYxtFzS=k^HpX%zit)0LEPz|&wIQ<+H(dh+(|P9SKYMFM^XNM-Nncnr8SPDYTg zB0v3MRZ~^9M&t7$F-*f{B7IlZ$O?cfl6n91WZJ(Jvx%#p=oQ^K-tgt~XBJ~@^vp7_ z@qP>F=mxP(j>S}}GQ=qKIk4mj#l4@b?l2cSW+XJ z@C=aS%F`rI-CVb0^=jJTXj8#HpP7%$mgWcQMQ1yE+L=}yBzXbHAkFX-X}S+Dk~m(P zrfH@Lm^_vgx~I6=Y=f--j&d{Ey`;^n%TwNNp1Uw^{vV_rMyKls+9sS*j^~^n9O$0b zn{14V%FpB5v_(Eg>gOx@GouOD%}l})_FRv4dN1`ZMPYr}Q`yeK?NY@bew-uoVTcdK z3$DJ)?&g##xp3<6E@u9`-tEuX)_;=wQFoE1^N4oJDWbimInr-%$)KBD1oh`JWMEx8 z9@$(t;&0KN^ZE#*?eVz#24(gOY6X7IN)zK$&Bvkg&4uCT99?+_!YjWeHBK8j6xSKJ z9XnYUa4&jk3T?|0hpjK+9J1?VW( zMbtb6tbE3As@53^6m4wMHlnxPRxc_#lhKY7k;sC?A8p*_(*Vpi?EZZ;R9qa3J#vDI zV&6=fdh9k06{H-h1*aUS+5((UpbEa?g;mtquCF4~ksrTTj^HLN3^c@I#a~*CuR6hT z9=}Hbhy^RX@+_w*zu?>#u<|hqU(7&Iy`BZ0@+x&KT;IQ}!eA|MN(sken2ELin*P!E z`OB9!+PLH&9z;wxD13#ADUO0ZG@g~)R*M=F!xDPsJc+y- zPqd`?o*<41UEDR}CAyBhXfn^i$tm(JsnyBqQHSlL4jtq33l7|}u;Jk63!-uxMGP08 zw@7FqTyE;vamrNgPCj7wzQZ=VjJ~;E68gG{B(SiFOea4}*M6S!s^1GLQQYm;*S{5w zfHBiRLr)EY!RPlZ%Ytyy#S2nYbswICE}!CVE-EaHQSep!XakKI0T9`@!4d6!hv%aU z3u}U`J`C-B_#$@eGuhno{`&kUG*4bCTVk?p>em3--g_YxYU%7j8o!u}pZv@tVSDTh zRNzi7wj=&?F4HTA+MHsxGWQ2>f^z;ayMZ=S&0?ljQg4BY{IN7?n}!GDdj{CWXE)B1 za1kCI!#SXPgOEpvoj}_`S(j}t2W~mMMbT3{OibLfzyMb6ElKNR%Rj|2<4icLr`8uR z1X~QH_StdApc)`C#aXCQZm#idV?jb}qF7EZUqvBD`757=?kxyG#B+1Dv08bN8#T+2P)1k5A7)2{lxZ`?pNRusN2E+%fB0kF+LZ&G z8s{H$o1V^GPAW>MZg>MLlJcz^Y&Nx{A3@{sao|GS)29nzS;F!w`SM&YA$s-AzqkM< za4i6XzrESIXZ#?Tqj*)g&zpylzQ+~Q=$NJ2cG!oM?Mb~X=UjRvcB)3s%-nn{os9{> z?9PzeL1wqT&riKHJF{nrucE2Q{zp&s{E3PkOYtZGYji7{8!Ky`f1BKyE@qMt<^kOa z@g^OvznR&#aU-9rcsBKwe_x+;u*RDT=B8(ppm{>UDgnE`DTBe+mQJd|z@`qGg24vTE(x8^F$01L%rcANyu`b%S81} zjELixCnW}BRJnqbu)F1m_DE{iV*|}9oqIhEc~tjA={Ab!;{Zm5ShCqvK=MImpmb>n zr7yw4^Y3CFL*qDL4PhNWH7^bd*9a-NR{iyRpga;#dDJv8q8fMn=)~>jFgm z!X_2RM11YT2R0i%gZ{SsyQgqA23-)igW7SEWYH<-tZa#-b#rzZSf-=n4%`uJqozJH zX55%_o)gLve?EroL4UtZp=)6g;}qGa+dbwdaL~l0L~N?3r{^|mJ|xV*c9w%udbt^IXLbX8{sNk8BIIOo_sSNrK&xJSG5@~5?EwIt2Qk!2eXP&LDYmlIja{xsX ziV11SnLU?++eICS>Vt4L1gTd{GXZH#z(Ta5R%N3}}lU zBZ3TKyg#9?0i7V9LA5LF?9?3pL%VK@Z;xLdN-$hCFsN(CRM}8~c?B$kF3@OXX**jd zNg70k;~YeEqVhp8mX@BrMzFW0W?{vp0JItqw-6Cm;OBgZ(F{W6wGtIXfK@^FBFmOF zW*NC5<>x(=o^5|C#V6`kqwVwf;e*T*^ggSakDneCTxR5{xecx)OwWpnv|$K1od*Ug zI79JVFUNbN{M!t`FvXCPu*ksypBf6C)|wgK9aMofOvZ2l!4c|Rk2mAs0DwjH*c0dk zaX}KbAnLcZsiA_7Dq~%#lH~oSO9}CLQW6n+ED;GF=?}MGzsfWsqxTL?wuQ!firT}w z&&B1%{WEbvbN%0jC@Wli;DCwX8%|t6f&5d3LmAEa zuq1ZwEL{qc)k7c`DVJ^{iaSn+0!C2GT!cf8^6fcJ5zYG#6yXnT|cK>EnTYp<_Wfeh8qDIGkR~I^WV#yLwRnxxIyT>h7Uc_l*Z~`I(q1m^Ynqarw5fX^5~sH8+o#WM`Nb zJbbuA?i2%hCXa84R{FlSwhOT;M#VXBZYwD$1b|VepjTHeSeD=7MClztQ@#050n$-l z-{$9fG3W)z8sY1trGZnP@U|62)N3=e|2?h=pdZwljvkIr|i8lzc1O4dLT9cy< zSj{Cf*;Lwf(3hjjPIJTv6p60vqrd{;MT=HF&rBxRMw%R0>Z@MJLt{vwTPgC7E904D zGyXli2i}Poj1Mg23Ldll2D|d*!opkj8C0=d0>No{Xh(2+^b7u}hKE6Q{;A1{iEVQQ z39JW$6L>PvEU7bg11flS_(L}ebiwGr<~0=XJCqWAzXe8FILN|c_C6YNz&T+((vMS9 zXxEYem8f0HzTJIo_f_xPWvhL2x*V&eo)SdO1HBp(@6mH2%C67_LX54W`n&Y%MxcZw zb4Oe%bD0B22G#vchFNF2f*9TS`+6CfA1n4HX-cbwSsLVa8~2SB^YHMLb~tBuP=Q7f z0HLTe9tWvsMzZm&+WuUv-a`3~No^y6mDJV3c+TeC9G1>1-+bKU1dgLk&Kd3YyT`E3 zS=%q()o0ZCRcEq%RbiEjkDhcB-qndiGd`&qmA2W+&vG-~Zxk```-=X}*=KoEfh`z0 zBbh7l+J*~GUn*l>ZZERmL=HA8iaQ78BG~Z>as&GWk}qDo7-rY|TGsBtg&4|;D`U%j zJk3-mkHY|?YwSC*7mhwT=_uHTT{L|8ncXEC@ zlz!Id%_*e}TIek6H&u!Cd~oD6=SWh~L8eR!wMa15#~Y&&tW-VuW;g|5j0^a^JJ zUw6MuKHVUWL(}KyHAg}1D}zeD8$cAUrr)!BVo_ZmA6s{@BJ)#-ub1NJY#<94JWRW{}ucr_8bhgzdU05wS;Bh$-1;+CyAJ50c z#^C>bwrts6$MiM~&^c)N)%4z#>cZIvpBu-wxJVP}W-I>yxnkMm@Q^8Awp?-2n`2P7 z#LcDCbrn*gJUuu+=)|USy7W4`R?1y*b@FW4-kiy%A(Z;QMcqBGdi+H0YtLiT4ZpH@ z6Nd{<-4~1h7L@B`XSRef<-nFNlyo_J@PKNvCBKZBza1(0`}uJX98KZmT~HBv>fAs>C-+U{-|B(bgMVlyD82q1Xb7e_GCD7N=}6YYiJtK3nP5Pb+;NFl z9llvRj~x>y5@--x0qJ!I9NZOEtC8Fa$ga)#J;J2TFwVjFD9*DVQH_?_6tEyOomy7i zaLB5&Pe@Fx53Bjs&;oBOIOjjH)ERQ>8-GEy#WzKc%0t`wyVM;CL*Iy~DvR6;0sW%0 zL;OuWY%yBB@fhdB#H;qT$GOH#mIx#2ILCNkji>MwqCBQ3%qiY!+p`u>RxL9g+f5ri(gqxo&mmvV^aFcX1C<9Lk~l)#P7OcuY2m{lP7 z_(TGVFaUiZ9Hn6!!K(2cZcQ@ZS(Lxm@#BMQi-HV*Q$>Z5X6tKQaWGjFiZTY3UThv5 z#*?3?s$jqh9JlDl7El3rzyWRwtD*KLrDE{PCiXsuEdmV&3u0d}au#WXe5B>~)qTvG znVYFSP*IQk_{a~IJSdi?qPurpR5>j=WxZ?Xxf8K4puT8rE%NBSs>inwHd>B5QlM%mUNvybt>?+4@%*dB)-JQ zkLQEd29i1vev2p^E-3~GZrC!>ZQCozhKY%HSLmh_DJRu|`hwM(j}8SGVoR04)&q16 zvCq1@YioV)BIp3cDTaN=8ZsS>CjED}JVqQg$k}A?g8ck^g&;#!&Wc{JV&%&Bjg9wXL=+wB$qa_C_wZYG`GH&=;>ar7gqQZoo|RzTLjS2W zRT<4#V*uCb(&1?4bTo(>Z`@jZP#d{oX~w-q7?umQ{N1~YZTXlsEzmpnO-H#OZlWka zFab5Mk;R)jXyp@t^#nEn-tID>6$tkr_^d4S9-I)u;PEV)2_jn$Sx2BJg7Z%pH$tw0 z$Y~&hFoT9(7fbEOm;~s@D}d`l&_Rd>FxF3qmrTA%P=dSh=tnOAYES14;H%B|$yI$N zNFqj07#O|5C}>NGAAhxh15sldrZc#X5*Wn%UWPJ!ieQU(^`*|Up5RNC35 zi_jQ#TK^WJExpoE%!$sQ znSgP_QCRW+z4H**vIx8QMN5irlcAW&qnR{ivghcJg_shH`$kqb7_ep8VyA+C@${)~ z^DRU=>zI9@9U|W6n9m!&UT*m@|~8%x?5uqD3N^R1`cpI zw+9G(iFkp_95V#)Qi%v2_$b=fT|^rS7t@tTGVR|ISx~ocOB)OVYz+@<6@Bk%C_ z#%s*1V+^z~zC7nh&sVPkBNcM&kuD9U5OY+Xc@cJFg|5gQPOq?6yr=5VOe&zR_wd5s zQ+uN$BdXC$H7YD_0~nm3gQLW(B!>*IM`DhQ*3?<>2g_Z+c# zs7<-b48S;%cywUzzI_oeti1bdFiE)10PIf4Bd9VH=^x?J8T+*gBNj#wYn;wJoK=@5slOR(MKA7zP^@$0#s## zb?er_IVw!7>f1MzQA^4p+5x{sh{R$b5f|FS$wjA88xg{<-I!osJ);54QxF#eQI=rS zuXDm#2=C;qwzMQcZ=MW3H$Wu-^(Eu`2byw~5(myHKfoVWhqx6@*U8(fR>~1;4`HD$ zn|hu9upoeyuLp4Pljkw(4|sB9K!8!$hqNuTL0+G#o3YY62ZU_(60Qx#d(Oezc=c2L zc{pq9dn#KVW%5i~Y;G{%UlJ8$UNNCf)Q25CwGM{qqi-jPFstjKG=lQ;&An*9RfXNj-4^ zQR+Uy7*9M*PZvP;lqPt8?`L4pF*m+aXiZgYwSQnx&{+oEgWz%)?ZRO*>g%)O2#O@I zKb|`@WF}}P0O?2k)5mot(LNJGp&}}Q?d``f8q(zO|N8ao<%USGNMYj~)^;NWw>!ZK z_;Tuoak6+uNWS8CL};aC}s2bFdckRxz zsr$G%f(U(r2ww>%vuVgzaO^d(&i8q7m3>L5o*^M5rGL#=D@k@6ngc^`72L#Ot7>>Oid+*wy!kJ)SLU?vw5WPm54K zAoIwwVhA%^Ael(qz$hK({1ya&iN`1RRB~AzuxUVPeox8iUEeq;o;-jZ6#Bv2{`?!cM=j%CmnV(cLrxRci0oP<{xDU4maHeEsWd(6ZyHb@kJ&(p| z$eZWlx_F2QrJQ|%a(TH1MPIbd74#p%n#S6CCnHOJ!(q`n1r?R`HF4jvUI9y#Q`JEF zh0bud^`UIOGZM6?&DE$B)6<_l<<+9f_4FAo6uY=ftw-W3nGa@j;aNcqqIfHWG10Vo zTmIftG+Q95IAbrpOMRoxHxO}v*ezxnMAvoaFn@5j=?g5jus9XVTNzPdc%=Emi=5l+ zn_o`{7>_6hHw@h3v zv~u75IT@?Ym*l`Db$4ig`V2P8%5BbbP|x7lFe+)uLbswXDuquFy!fxakLv4*L`Y}M z9Whh;;B%zC6J;i0mxn{Vq%{$MoBqeElbM8u-$#@vsYAp-VK=hDNssi?tF#M5vF&5^ z_wTTJ+Pp>oRojW|9$WRNePJ&VY?85v2~x1&DN zL`w|0I7;qD71_TYL)7f_WY^YA>;QLFX{ge(PM$ukS-Ewqf&!+UJCN~l_lX#&+k+yv z{_5ckW*dEp6%9SE?*=X)HWWB)Oax{P$T`EfO*4=P&_OE+06xU)G)t0(zON>d_4GD3 zl%wauUk2;$uqTgrSj^5Y!FrxSxs1ZfgO zAePle8Y%|#GelK*fm4LCT9jN z$q>lwr|nX{1IgNF_Ut!F_2?pP0&`_*Mv1$&^0`QG`%oduGCmRz88D6t&k-FQYKzmS zH$gL+m?(qKe(wvQ89>Th6oQF@=n*oX^EQ%`pXy=RMZOt^AvaG7GJg6>KD%>EL5;b4 zZ)W3Uh{Q>|MRSNs`ts%2S+ovgc}z6{L=h3tzP{>MWH3E$+k{F7hQ;`GG!twqWQE`v z5!tqYX3h>h8#`6J-o_WAq@e_1wgv(#zH#H?b=PaEM1laV9 zjE`NQ?}*KVFBK{O6dK5Ghq6yzCpD#;Xes;X9}WijdH*yw4%=FFk8<TC0lMQnf3!7{;U=N|bOGrnHL%ZJI zN=yXG46g@vo|QXr>p|ETE4|INn=dCfx1y=(8WDgp86rT+z}T}s!w$5ct1GwPM*1wl z0|MSSMgLVs7?`8No4!wsn2-jNn=6crJQ6??Fj@)TG^7E9XBQ&kkn$00y(Y4<=RHWs zcsyLG@kuU^J{^^-pQxDhOOGpK{q&csOIdiUgVu4C2l`;h9+G9)D#Muu6Jyx^VKx}Z zR|?xdR65Z8<5aZP3FOmGGJV<3_)`QwCfCIB!o@?8op=I5^lrmJPOEc2%oqEv@6}u& zc$ZpU@Crmw}I*qF~TL;C#5`Z~glNKd|T)c)Jx49zA;W=pmnsg2Ga{$h|3? zOSgFjms~CIG8Yj*2eRm^Zw^0xa)E+-!7Gy_6Blo9xoX;4?6#iN9EmY28ZT79m_s99 zri2D;4eDU@**GA+RYuE4MBTcT`2@{Hy%G%MK#72?5SohmdKt|G_(JS362J-su$Ey4 zjSr$ZWMt?Keki~+IWhp965IfQim+J4^MmC{_PH(|z%F5k9fr5NdtP%`M6mh&%r-xRw7}Ol4dg{I}?v@BgKiz3MfDHrxgYEOrCyP~~ zV86KQz(TR|iV87J>6u<{EM7hm0Ui--{=0Xtr@mAsM&Ll90PNfhd3^fB2>?Oj-FYm( zd%wYS?fmOjdOZh-Ap;gxn{R6XGmBt0wnw=D+jLE8Q>($Be{@B3|6pdok^(363ygfzMR0@{Iw)ZOc6rokB^ z=Jfz?9!fJRO#CZH@Pr}MLqEg<|6;I8i3JGPs)v)DRes{4eso1D=OS}uhFp9H>$@ zfaCnn%YKWjjL!$QP2l7~5|4r0J~?uj-KKSWT*XYV)%WbV8ux6Bujh?F8`ufqsl-wO z+JM9GEf^T+{BXQ3)=WUthn1)q#M6e_0GO4d<6wZ833_64R~S?B0Im&O&s-Pm=h>n0 zrKVk~BSv14FF1d#N$Aa%bFBU0x&3eM3Qp*|PlU)T+7_dEgcSKzlVi$4ZQ-8{UN>Dv z&YrG2;6V#tChW`98`6SJR{(gI05U-w35mc9^CT_4~XHh74 zqY#REw8umUVOiIWgQHM!Z;^FpAn8L`z3>5N5tdC%inm=AZL~jLWh5xNsyNS&Wxrl= z@6+snDefF>xK>n$i+e78C6m>rl&?&8`FbUaP37XU_EqQ_A^iQA*)3LK7R8?}O*O;m zf<0E~;E?;Wl~(Ha_-%}6vIDh$`nU)FDr?jVm_M$VyfEu;@cS|+3USYyorgAL*EkiY zE;}=lzkbVL13&MP%Im|$PQt@q4$U{q$Ov+*-p85mb!dam)8vz8YgkLjXzM@qzmLWt z*+p#9v#(^`KmUEUv&}PE(-&-XI5(mF?`S@J z<(M^bZF*;kr!+Vty?u7vMyPKs? zRS|QMlrSrQ!Q~QYI95Mh5o2Bz;aF60Ksc&drtsbF?a%Hk!%wGrWb-asaN4&0fipv= zKX%#c?R_`%hf03lLH}f1-P*gi)(rLac?;@&7aC=H@eaNz&ZU<%p{QiP_p&wR;~RYG z`-7b}iC=e&!`S%#ttO6MZJE4WTp(u0;()@5u(>f3qdJM>x8*m68buLWPE>yP`XgS5TW_7x{XoJVp~m2$D?;Q3#Cm#7CFf z`oS*(4{eo)D-Z6exug^foDO{4hducg#MyEvx~&%u8+y2#CQ+yn3}uVmtIzv(?lZ@Nxhg zpgcpV0nsWJp&85)p7e74IJ99inHo#8KxF|&duUa}8riE69a*ufpiL}3^OkOPv(0ax ziFomuouS_^RtfkmU!uoxK5dJEy5dH%+46PZ7S`r|3Pw-j^6ZwNBNIG3B1{nO>JY|$ zY%nz~LiLGR?5vW*dTaQnPBUYU?-dnr&cPxuWBrOrcKY^lx zwcjy_^Bw}owt^%LuQ#!I#6SsMo~rj0pQ))SW;!si2g(^(7UN`qily-Kmf7cVZuWWn zOH3dR*egqIIuBN(==CrGwXV4%=DBoctWQR036i9AMAeqNkXMd8Y7uqROZYBU#k{>S z&=AXk%Tr#BTyOh}T>+bHGgrvZE$9Y5RcK)@HaY=<;`5!AK=C2o6}LHYlR-y0ipao^ z#XRuUfb29e5t|Mv%_THUvx#QPNC3=v6c24cVc%m7RRDJ%&12D|t!7Wx&Mkj2=YH2+ zCzJnU-319*APs(KpE?)4a^7B%)EjSgu&w@SYwK8b9Z87muxzk^I}?@TJhdUevg+2K zry1i_{Xh0U%N{!P43k*iv$IhNL3N%EG38og|#=cfl8arBS z-i3ksrDK+?8*S|8-#-d!Ej8z z<^=Ar>%R*)z@O#aMc8QI(LY4CI}ohkcB}RJ9RB~F^%^k@wyyMY5fOL>3J|(}WfLp! z5$*SUGDzwLW(`H(sujY*P&YZlzr9fUOSHz*4>)l;#YcbtaI-9#S+XOFVMAkn)zS`a zF%w5e$3~U)6=tAfg3i?=p)x{qqH`W)o{3pDIXC6QPb;a!efpKT)RxqW_U2G4FT0WW z0^2un>v_DrQs-uUrRwYo;Eb8rO6G|f>+xvoZ^$C*dI*HzBQFGkI+wpO8Xgv1vbPTq z@TU3ANLN}tq=JU_^-SyAODUxTw*gqO?R9;0O7J9)$5A_N>&YA;E3p>sBNtu2`nsa(R*()1a{=+4;1q@iJDQ z(LMfeNJo~LIF;xvPQ9DllC};ukXVmbxNNqJWbv;^7Sq+)PHe1-EN><2D*m5WK(lq- z;{OMr_J0*DyYoF$g)^RK=>6NDvUrX~K3=n(q;%$A-N80=b^teVFXLa>3+$v|GEYK< z)_)=QbuzfL;F&G}!W-fh^th(xX`#vpK{F!?;PI1Qjae+ewab@}&DQ0p-+;%Xup_!! zsK5cG;@86Chz!ELi(fk(@a-Tt4geem>u;8jV{-hY3d1&Eaj=1vh@+*IHrs(I#-^E? z27r%+B)``iaBSeX1#%!wUbIq3JKYH7iu;9J+5h5CJ=&B(||fOM-RBs7~3SXf-c&9_EU za(;jzs9F%F0PO+__7~6Lq8n4 J6w=X){|DY{%{Tx6 From 3ccc5b0717fc253f3caf5d57442edeee02cfffcd Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 31 Mar 2024 09:42:12 +0900 Subject: [PATCH 28/65] docs: add link to Logging page --- user_guide_src/source/general/errors.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/user_guide_src/source/general/errors.rst b/user_guide_src/source/general/errors.rst index 2c947ae7deb7..4b86f21f56dc 100644 --- a/user_guide_src/source/general/errors.rst +++ b/user_guide_src/source/general/errors.rst @@ -83,8 +83,9 @@ To ignore logging on other status codes, you can set the status code to ignore i .. literalinclude:: errors/006.php -.. note:: It is possible that logging still will not happen for exceptions if your current Log settings - are not set up to log **critical** errors, which all exceptions are logged as. +.. note:: It is possible that logging still will not happen for exceptions if your current + :ref:`Log settings ` + are not set up to log ``critical`` errors, which all exceptions are logged as. Framework Exceptions ==================== From 55f26cdcd09a3c9c277c57432f6113d43480cf94 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 31 Mar 2024 09:53:21 +0900 Subject: [PATCH 29/65] docs: improve sample code --- user_guide_src/source/general/errors/007.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/user_guide_src/source/general/errors/007.php b/user_guide_src/source/general/errors/007.php index a06e5000737a..585f4aa6b68a 100644 --- a/user_guide_src/source/general/errors/007.php +++ b/user_guide_src/source/general/errors/007.php @@ -1,5 +1,9 @@ find($id)) { - throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(); +use CodeIgniter\Exceptions\PageNotFoundException; + +$page = $pageModel->find($id); + +if ($page === null) { + throw PageNotFoundException::forPageNotFound(); } From 83ea16fd4c606ec7f75cf976308bb4368a6d3c3b Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 31 Mar 2024 09:55:47 +0900 Subject: [PATCH 30/65] docs: add "" for readability --- user_guide_src/source/general/errors.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/general/errors.rst b/user_guide_src/source/general/errors.rst index 4b86f21f56dc..b84defe7b432 100644 --- a/user_guide_src/source/general/errors.rst +++ b/user_guide_src/source/general/errors.rst @@ -74,7 +74,7 @@ See :ref:`setting-environment`. Logging Exceptions ------------------ -By default, all Exceptions other than 404 - Page Not Found exceptions are logged. This can be turned on and off +By default, all Exceptions other than "404 - Page Not Found" exceptions are logged. This can be turned on and off by setting the ``$log`` value of **app/Config/Exceptions.php**: .. literalinclude:: errors/005.php From 0d493d180e42dd497595ced637bb53506653dec8 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 31 Mar 2024 09:56:22 +0900 Subject: [PATCH 31/65] docs: add link and change paragraph order for readability --- user_guide_src/source/general/errors.rst | 11 +++++++---- user_guide_src/source/incoming/routing.rst | 2 ++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/user_guide_src/source/general/errors.rst b/user_guide_src/source/general/errors.rst index b84defe7b432..ef6f30abe4ed 100644 --- a/user_guide_src/source/general/errors.rst +++ b/user_guide_src/source/general/errors.rst @@ -95,15 +95,18 @@ The following framework exceptions are available: PageNotFoundException --------------------- -This is used to signal a 404, Page Not Found error. When thrown, the system will show the view found at -**app/Views/errors/html/error_404.php**. You should customize all of the error views for your site. -If, in **app/Config/Routes.php**, you have specified a 404 Override, that will be called instead of the standard -404 page: +This is used to signal a 404, Page Not Found error: .. literalinclude:: errors/007.php You can pass a message into the exception that will be displayed in place of the default message on the 404 page. +When thrown, the system will show the view found at +**app/Views/errors/html/error_404.php**. You should customize all of the error views for your site. + +If, in **app/Config/Routing.php** or **app/Config/Routes.php**, you have specified +a :ref:`404-override`, that will be called instead of the standard 404 page. + ConfigException --------------- diff --git a/user_guide_src/source/incoming/routing.rst b/user_guide_src/source/incoming/routing.rst index a9aa28602822..f05ac3298467 100644 --- a/user_guide_src/source/incoming/routing.rst +++ b/user_guide_src/source/incoming/routing.rst @@ -664,6 +664,8 @@ to only those defined by you, by setting the ``$autoRoute`` property to false: .. warning:: If you use the :doc:`CSRF protection `, it does not protect **GET** requests. If the URI is accessible by the GET method, the CSRF protection will not work. +.. _404-override: + 404 Override ============ From 37931d07d000d606ff3f77b7a9ad364d30f1088b Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 31 Mar 2024 10:42:53 +0900 Subject: [PATCH 32/65] docs: add missing "HTTP Status Code and Error Views" --- user_guide_src/source/general/errors.rst | 28 ++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/user_guide_src/source/general/errors.rst b/user_guide_src/source/general/errors.rst index ef6f30abe4ed..0f6ebd178145 100644 --- a/user_guide_src/source/general/errors.rst +++ b/user_guide_src/source/general/errors.rst @@ -101,8 +101,7 @@ This is used to signal a 404, Page Not Found error: You can pass a message into the exception that will be displayed in place of the default message on the 404 page. -When thrown, the system will show the view found at -**app/Views/errors/html/error_404.php**. You should customize all of the error views for your site. +For the default 404 view file location, see :ref:`http-status-code-and-error-views`. If, in **app/Config/Routing.php** or **app/Config/Routes.php**, you have specified a :ref:`404-override`, that will be called instead of the standard 404 page. @@ -161,6 +160,31 @@ Since v4.3.0, you can specify the HTTP status code for your Exception class to i When an exception implementing ``HTTPExceptionInterface`` is caught by CodeIgniter's exception handler, the Exception code will become the HTTP status code. +.. _http-status-code-and-error-views: + +HTTP Status Code and Error Views +================================ + +The exception handler displays the error view corresponding to the HTTP status +code, if it exists. + +For example, ``PageNotFoundException`` implements the ``HTTPExceptionInterface``, +so its exception code ``404`` will be the HTTP status code. Therefore if it is +thrown, the system will show the **error_404.php** in the **app/Views/errors/html** +folder when it is a web request. If it is invoked via CLI, the system will show +the **error_404.php** in the **app/Views/errors/cli** folder. + +You should customize all of the error views in the **app/Views/error** folder for +your site. + +You can also create error views for specific HTTP status code. For example, if +you want to create an error view for "400 Bad Request", add **error_400.php**. + +.. warning:: If an error view file with the corresponding HTTP status code exists, + the exception handler will display that file regardless of the environment. + The view file must be implemented in such a way that it does not display + detailed error messages in production environment by yourself. + .. _error-specify-exit-code: Specify Exit Code in Your Exception From e6bdf906570aeec0e3d03c13dfb3eb21ab86b945 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 31 Mar 2024 11:13:04 +0900 Subject: [PATCH 33/65] fix: bug that the error view is determined by Exception code It should be determined by HTTP status code. Otherwise, if by chance an exception with code 404 is thrown, a 404 Not Found page will be displayed. --- system/Debug/ExceptionHandler.php | 19 +++++++++++-------- tests/system/Debug/ExceptionHandlerTest.php | 2 +- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/system/Debug/ExceptionHandler.php b/system/Debug/ExceptionHandler.php index 63449888ffb5..09c6de0d5b7e 100644 --- a/system/Debug/ExceptionHandler.php +++ b/system/Debug/ExceptionHandler.php @@ -97,8 +97,8 @@ public function handle( . DIRECTORY_SEPARATOR . 'errors' . DIRECTORY_SEPARATOR . $addPath; // Determine the views - $view = $this->determineView($exception, $path); - $altView = $this->determineView($exception, $altPath); + $view = $this->determineView($exception, $path, $statusCode); + $altView = $this->determineView($exception, $altPath, $statusCode); // Check if the view exists $viewFile = null; @@ -119,13 +119,16 @@ public function handle( } /** - * Determines the view to display based on the exception thrown, - * whether an HTTP or CLI request, etc. + * Determines the view to display based on the exception thrown, HTTP status + * code, whether an HTTP or CLI request, etc. * * @return string The filename of the view file to use */ - protected function determineView(Throwable $exception, string $templatePath): string - { + protected function determineView( + Throwable $exception, + string $templatePath, + int $statusCode = 500 + ): string { // Production environments should have a custom exception file. $view = 'production.php'; @@ -147,8 +150,8 @@ protected function determineView(Throwable $exception, string $templatePath): st $templatePath = rtrim($templatePath, '\\/ ') . DIRECTORY_SEPARATOR; // Allow for custom views based upon the status code - if (is_file($templatePath . 'error_' . $exception->getCode() . '.php')) { - return 'error_' . $exception->getCode() . '.php'; + if (is_file($templatePath . 'error_' . $statusCode . '.php')) { + return 'error_' . $statusCode . '.php'; } return $view; diff --git a/tests/system/Debug/ExceptionHandlerTest.php b/tests/system/Debug/ExceptionHandlerTest.php index 8dcad8e6c338..a55227ab7a34 100644 --- a/tests/system/Debug/ExceptionHandlerTest.php +++ b/tests/system/Debug/ExceptionHandlerTest.php @@ -69,7 +69,7 @@ public function testDetermineViewsRuntimeExceptionCode404(): void $templatePath = APPPATH . 'Views/errors/html'; $viewFile = $determineView($exception, $templatePath); - $this->assertSame('error_404.php', $viewFile); + $this->assertSame('error_exception.php', $viewFile); } public function testDetermineViewsDisplayErrorsOffRuntimeException(): void From c65e05d8bf41eee5326c322257c6422cae58e177 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 31 Mar 2024 11:23:54 +0900 Subject: [PATCH 34/65] docs: move "Logging Deprecation Warnings" in section "Configuration" --- user_guide_src/source/general/errors.rst | 62 ++++++++++++------------ 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/user_guide_src/source/general/errors.rst b/user_guide_src/source/general/errors.rst index 0f6ebd178145..d551773fb87a 100644 --- a/user_guide_src/source/general/errors.rst +++ b/user_guide_src/source/general/errors.rst @@ -87,6 +87,37 @@ To ignore logging on other status codes, you can set the status code to ignore i :ref:`Log settings ` are not set up to log ``critical`` errors, which all exceptions are logged as. +.. _logging_deprecation_warnings: + +Logging Deprecation Warnings +---------------------------- + +.. versionadded:: 4.3.0 + +By default, all errors reported by ``error_reporting()`` will be thrown as an ``ErrorException`` object. These +include both ``E_DEPRECATED`` and ``E_USER_DEPRECATED`` errors. With the surge in use of PHP 8.1+, many users +may see exceptions thrown for `passing null to non-nullable arguments of internal functions `_. +To ease the migration to PHP 8.1, you can instruct CodeIgniter to log the deprecations instead of throwing them. + +First, make sure your copy of ``Config\Exceptions`` is updated with the two new properties and set as follows: + +.. literalinclude:: errors/012.php + +Next, depending on the log level you set in ``Config\Exceptions::$deprecationLogLevel``, check whether the +logger threshold defined in ``Config\Logger::$threshold`` covers the deprecation log level. If not, adjust +it accordingly. + +.. literalinclude:: errors/013.php + +After that, subsequent deprecations will be logged instead of thrown. + +This feature also works with user deprecations: + +.. literalinclude:: errors/014.php + +For testing your application you may want to always throw on deprecations. You may configure this by +setting the environment variable ``CODEIGNITER_SCREAM_DEPRECATIONS`` to a truthy value. + Framework Exceptions ==================== @@ -197,37 +228,6 @@ Since v4.3.0, you can specify the exit code for your Exception class to implemen When an exception implementing ``HasExitCodeInterface`` is caught by CodeIgniter's exception handler, the code returned from the ``getExitCode()`` method will become the exit code. -.. _logging_deprecation_warnings: - -Logging Deprecation Warnings -============================ - -.. versionadded:: 4.3.0 - -By default, all errors reported by ``error_reporting()`` will be thrown as an ``ErrorException`` object. These -include both ``E_DEPRECATED`` and ``E_USER_DEPRECATED`` errors. With the surge in use of PHP 8.1+, many users -may see exceptions thrown for `passing null to non-nullable arguments of internal functions `_. -To ease the migration to PHP 8.1, you can instruct CodeIgniter to log the deprecations instead of throwing them. - -First, make sure your copy of ``Config\Exceptions`` is updated with the two new properties and set as follows: - -.. literalinclude:: errors/012.php - -Next, depending on the log level you set in ``Config\Exceptions::$deprecationLogLevel``, check whether the -logger threshold defined in ``Config\Logger::$threshold`` covers the deprecation log level. If not, adjust -it accordingly. - -.. literalinclude:: errors/013.php - -After that, subsequent deprecations will be logged instead of thrown. - -This feature also works with user deprecations: - -.. literalinclude:: errors/014.php - -For testing your application you may want to always throw on deprecations. You may configure this by -setting the environment variable ``CODEIGNITER_SCREAM_DEPRECATIONS`` to a truthy value. - .. _custom-exception-handlers: Custom Exception Handlers From 8047699d7d9d12457b5dfb2dd9419be250b674e9 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 31 Mar 2024 11:34:38 +0900 Subject: [PATCH 35/65] docs: add about default error view files --- user_guide_src/source/general/errors.rst | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/user_guide_src/source/general/errors.rst b/user_guide_src/source/general/errors.rst index d551773fb87a..31055982da6c 100644 --- a/user_guide_src/source/general/errors.rst +++ b/user_guide_src/source/general/errors.rst @@ -197,7 +197,7 @@ HTTP Status Code and Error Views ================================ The exception handler displays the error view corresponding to the HTTP status -code, if it exists. +code, if one exists. For example, ``PageNotFoundException`` implements the ``HTTPExceptionInterface``, so its exception code ``404`` will be the HTTP status code. Therefore if it is @@ -205,8 +205,14 @@ thrown, the system will show the **error_404.php** in the **app/Views/errors/htm folder when it is a web request. If it is invoked via CLI, the system will show the **error_404.php** in the **app/Views/errors/cli** folder. -You should customize all of the error views in the **app/Views/error** folder for -your site. +If there is no view file corresponding to the HTTP status code, **production.php** +or **error_exception.php** will be displayed. + +.. note:: If ``display_errors`` is on in the PHP INI configuration, + **error_exception.php** is selected and a detailed error report is displayed. + +You should customize all of the error views in the **app/Views/errors/html** folder +for your site. You can also create error views for specific HTTP status code. For example, if you want to create an error view for "400 Bad Request", add **error_400.php**. From b651344387be99e84101a851f4ee78904c9d1d34 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 31 Mar 2024 13:32:42 +0900 Subject: [PATCH 36/65] docs: add changelog --- user_guide_src/source/changelogs/v4.4.8.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/user_guide_src/source/changelogs/v4.4.8.rst b/user_guide_src/source/changelogs/v4.4.8.rst index 5e3eb3d16908..4ff846be528a 100644 --- a/user_guide_src/source/changelogs/v4.4.8.rst +++ b/user_guide_src/source/changelogs/v4.4.8.rst @@ -14,6 +14,11 @@ Release Date: Unreleased BREAKING ******** +- A bug that caused the :doc:`Exception handler <../general/errors>` to display + incorrect error view file corresponding to the exception code has been fixed. + The third parameter ``int $statusCode = 500`` has been added to + ``CodeIgniter\Debug\ExceptionHandler::determineView()`` for this purpose. + *************** Message Changes *************** From b4855dd67c50b4856a88cb520f2d0175e70b4c8b Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 31 Mar 2024 13:45:44 +0900 Subject: [PATCH 37/65] docs: show full classnames --- user_guide_src/source/general/errors.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/user_guide_src/source/general/errors.rst b/user_guide_src/source/general/errors.rst index 31055982da6c..2b6eafce8c33 100644 --- a/user_guide_src/source/general/errors.rst +++ b/user_guide_src/source/general/errors.rst @@ -187,7 +187,7 @@ Specify HTTP Status Code in Your Exception .. versionadded:: 4.3.0 Since v4.3.0, you can specify the HTTP status code for your Exception class to implement -``HTTPExceptionInterface``. +``CodeIgniter\Exceptions\HTTPExceptionInterface``. When an exception implementing ``HTTPExceptionInterface`` is caught by CodeIgniter's exception handler, the Exception code will become the HTTP status code. @@ -230,7 +230,7 @@ Specify Exit Code in Your Exception .. versionadded:: 4.3.0 Since v4.3.0, you can specify the exit code for your Exception class to implement -``HasExitCodeInterface``. +``CodeIgniter\Exceptions\HasExitCodeInterface``. When an exception implementing ``HasExitCodeInterface`` is caught by CodeIgniter's exception handler, the code returned from the ``getExitCode()`` method will become the exit code. From 17763f32d364f5148c3a71ed9a4edae3afa293fb Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 31 Mar 2024 13:50:32 +0900 Subject: [PATCH 38/65] docs: update outdated description --- user_guide_src/source/database/transactions.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/user_guide_src/source/database/transactions.rst b/user_guide_src/source/database/transactions.rst index bb58040ac4ce..4fe44c8929e5 100644 --- a/user_guide_src/source/database/transactions.rst +++ b/user_guide_src/source/database/transactions.rst @@ -67,11 +67,11 @@ Strict Mode can be disabled as follows: Managing Errors =============== -When you have ``DBDebug`` true in your **app/Config/Database.php** file, -if a query error occurs, all the queries will be rolled backed, and an exception -will be thrown. So you'll see a standard error page. +.. note:: + Since v4.3.0, during transactions, exceptions are not thrown by default + even if ``DBDebug`` is true. -If the ``DBDebug`` is false, you can manage your own errors like this: +You can manage your own errors like this: .. literalinclude:: transactions/003.php From dc7e86c6396b5ede0dfe8645cf724faaace89a36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=BCller=20Nato?= Date: Sun, 31 Mar 2024 04:40:48 -0300 Subject: [PATCH 39/65] Update upgrade_447.rst added to changed files - app/Config/App.php --- user_guide_src/source/installation/upgrade_447.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/user_guide_src/source/installation/upgrade_447.rst b/user_guide_src/source/installation/upgrade_447.rst index 562118bbbb78..d31496d601d3 100644 --- a/user_guide_src/source/installation/upgrade_447.rst +++ b/user_guide_src/source/installation/upgrade_447.rst @@ -118,6 +118,7 @@ All Changes This is a list of all files in the **project space** that received changes; many will be simple comments or formatting that have no effect on the runtime: +- app/Config/App.php - app/Config/Cache.php - app/Config/ContentSecurityPolicy.php - app/Config/Database.php From b559dbd9f8f471f52b55c35f2c0a3bdc980cf5b1 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 31 Mar 2024 17:10:06 +0900 Subject: [PATCH 40/65] docs: fix sample .htaccess The settings may redirect to URI with `/public/`. E.g., http://localhost/ci4/users/ -> http://localhost/ci4/public/users --- .../source/installation/running.rst | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/user_guide_src/source/installation/running.rst b/user_guide_src/source/installation/running.rst index 69f89cfbdbd0..4bd823911b75 100644 --- a/user_guide_src/source/installation/running.rst +++ b/user_guide_src/source/installation/running.rst @@ -295,6 +295,30 @@ And edit **.htaccess** as follows: Satisfy All +And remove the redirect settings in **public/.htaccess**: + +.. code-block:: diff + + --- a/public/.htaccess + +++ b/public/.htaccess + @@ -16,16 +16,6 @@ Options -Indexes + # http://httpd.apache.org/docs/current/mod/mod_rewrite.html#rewritebase + # RewriteBase / + + - # Redirect Trailing Slashes... + - RewriteCond %{REQUEST_FILENAME} !-d + - RewriteCond %{REQUEST_URI} (.+)/$ + - RewriteRule ^ %1 [L,R=301] + - + - # Rewrite "www.example.com -> example.com" + - RewriteCond %{HTTPS} !=on + - RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC] + - RewriteRule ^ http://%1%{REQUEST_URI} [R=301,L] + - + # Checks to see if the user is attempting to access a valid file, + # such as an image or css document, if this isn't true it sends the + # request to the front controller, index.php + Hosting with mod_userdir (Shared Hosts) ======================================= @@ -505,6 +529,31 @@ And edit **.htaccess** as follows: Satisfy All + +And remove the redirect settings in **public/.htaccess**: + +.. code-block:: diff + + --- a/public/.htaccess + +++ b/public/.htaccess + @@ -16,16 +16,6 @@ Options -Indexes + # http://httpd.apache.org/docs/current/mod/mod_rewrite.html#rewritebase + # RewriteBase / + + - # Redirect Trailing Slashes... + - RewriteCond %{REQUEST_FILENAME} !-d + - RewriteCond %{REQUEST_URI} (.+)/$ + - RewriteRule ^ %1 [L,R=301] + - + - # Rewrite "www.example.com -> example.com" + - RewriteCond %{HTTPS} !=on + - RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC] + - RewriteRule ^ http://%1%{REQUEST_URI} [R=301,L] + - + # Checks to see if the user is attempting to access a valid file, + # such as an image or css document, if this isn't true it sends the + # request to the front controller, index.php + ********************* Bootstrapping the App ********************* From 13071b8ce564e03043c9313587c9a17d5a6efc21 Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 1 Apr 2024 10:01:13 +0900 Subject: [PATCH 41/65] docs: fix notation for $db --- user_guide_src/source/database/metadata.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/database/metadata.rst b/user_guide_src/source/database/metadata.rst index 9ae08e5aae8d..f617872322a3 100644 --- a/user_guide_src/source/database/metadata.rst +++ b/user_guide_src/source/database/metadata.rst @@ -51,7 +51,7 @@ $db->getFieldNames() Returns an array containing the field names. This query can be called two ways: -1. You can supply the table name and call it from the ``$db->object``: +1. You can supply the table name and call it from the ``$db`` object: .. literalinclude:: metadata/003.php From 1b970658c7da0eb1547398b1ad8fbf4bad810bd1 Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 1 Apr 2024 10:01:41 +0900 Subject: [PATCH 42/65] docs: fix RST format --- user_guide_src/source/database/metadata.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/user_guide_src/source/database/metadata.rst b/user_guide_src/source/database/metadata.rst index f617872322a3..85dcdc0b1d33 100644 --- a/user_guide_src/source/database/metadata.rst +++ b/user_guide_src/source/database/metadata.rst @@ -53,12 +53,12 @@ two ways: 1. You can supply the table name and call it from the ``$db`` object: - .. literalinclude:: metadata/003.php + .. literalinclude:: metadata/003.php 2. You can gather the field names associated with any query you run by -calling the function from your query result object: + calling the function from your query result object: -.. literalinclude:: metadata/004.php + .. literalinclude:: metadata/004.php Determine If a Field is Present in a Table ========================================== From 04483e3fa04d31ab35ce19631804facb2f590bf0 Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 3 Apr 2024 12:59:10 +0900 Subject: [PATCH 43/65] docs: update descriptions --- user_guide_src/source/outgoing/localization.rst | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/user_guide_src/source/outgoing/localization.rst b/user_guide_src/source/outgoing/localization.rst index f84e9b029004..3e611ceab2ec 100644 --- a/user_guide_src/source/outgoing/localization.rst +++ b/user_guide_src/source/outgoing/localization.rst @@ -104,12 +104,19 @@ Setting the Current Locale If you want to set the locale directly, you may use ``IncomingRequest::setLocale(string $locale)``. -You must set supported locales in **app/Config/App.php**: + +Before setting the locale, you must set valid locales. Because any attempt to +set a locale that are not valid will result in +the :ref:`default locale ` being set. + +By default, the valid locales are defined in ``Config\App::$supportedLocales`` +in **app/Config/App.php**: .. literalinclude:: localization/003.php -.. note:: Any attempt to set a locale not included in this array will result in - the :ref:`default locale ` being set. +.. note:: Since v4.4.0, ``IncomingRequest::setValidLocales()`` has been added to + set (and reset) valid locales. Use it if you want to change the valid locales + dynamically. Retrieving the Current Locale ============================= From 8ed690e4a5d3eb3fc138b532c2e1008ceba033d3 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Wed, 3 Apr 2024 14:10:13 +0700 Subject: [PATCH 44/65] chore: remove useless @return BaseConnection|ConnectionInterface on BaseBuilder::db() --- system/Database/BaseBuilder.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/system/Database/BaseBuilder.php b/system/Database/BaseBuilder.php index 8a33e220d84d..2e32bc68790f 100644 --- a/system/Database/BaseBuilder.php +++ b/system/Database/BaseBuilder.php @@ -332,8 +332,6 @@ public function __construct($tableName, ConnectionInterface $db, ?array $options /** * Returns the current database connection - * - * @return BaseConnection|ConnectionInterface */ public function db(): ConnectionInterface { From a2802a693498fe3d08570ddfd42df2f726ee74f0 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Wed, 3 Apr 2024 15:56:07 +0700 Subject: [PATCH 45/65] update to @return BaseConnection --- system/Database/BaseBuilder.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/system/Database/BaseBuilder.php b/system/Database/BaseBuilder.php index 2e32bc68790f..65dea86da2b3 100644 --- a/system/Database/BaseBuilder.php +++ b/system/Database/BaseBuilder.php @@ -332,6 +332,8 @@ public function __construct($tableName, ConnectionInterface $db, ?array $options /** * Returns the current database connection + * + * @return BaseConnection */ public function db(): ConnectionInterface { From 98ce544fb246a379ad1539c053fee9f9c2fe1357 Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 3 Apr 2024 19:03:55 +0900 Subject: [PATCH 46/65] fix: Pager::only([]) does not work --- system/Pager/Pager.php | 6 +++--- tests/system/Pager/PagerTest.php | 8 ++++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/system/Pager/Pager.php b/system/Pager/Pager.php index d9de5faab401..0834fe09f0e4 100644 --- a/system/Pager/Pager.php +++ b/system/Pager/Pager.php @@ -60,9 +60,9 @@ class Pager implements PagerInterface /** * List of only permitted queries * - * @var array + * @var list|null */ - protected $only = []; + protected $only; /** * Constructor. @@ -276,7 +276,7 @@ public function getPageURI(?int $page = null, string $group = 'default', bool $r $uri->addQuery($this->groups[$group]['pageSelector'], $page); } - if ($this->only) { + if ($this->only !== null) { $query = array_intersect_key($_GET, array_flip($this->only)); if (! $segment) { diff --git a/tests/system/Pager/PagerTest.php b/tests/system/Pager/PagerTest.php index f5059f082b53..25c923265f33 100644 --- a/tests/system/Pager/PagerTest.php +++ b/tests/system/Pager/PagerTest.php @@ -165,6 +165,10 @@ public function testStoreWithQueries(): void 'http://example.com/?foo=bar&page=5', $this->pager->only(['foo'])->getPageURI(5) ); + $this->assertSame( + 'http://example.com/?page=5', + $this->pager->only([])->getPageURI(5) + ); } public function testStoreWithSegments(): void @@ -181,6 +185,10 @@ public function testStoreWithSegments(): void 'http://example.com/5?foo=bar', $this->pager->only(['foo'])->getPageURI(5) ); + $this->assertSame( + 'http://example.com/5', + $this->pager->only([])->getPageURI(5) + ); } public function testGetPageURIWithURIReturnObject(): void From 22f4c38b564a5b02cf45d05faae3dad2de747dd2 Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 3 Apr 2024 19:11:40 +0900 Subject: [PATCH 47/65] chore: remove ignoreErrors --- phpstan-baseline.php | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/phpstan-baseline.php b/phpstan-baseline.php index 83a947a76f94..83986e548050 100644 --- a/phpstan-baseline.php +++ b/phpstan-baseline.php @@ -8041,21 +8041,11 @@ 'count' => 1, 'path' => __DIR__ . '/system/Pager/Pager.php', ]; -$ignoreErrors[] = [ - 'message' => '#^Only booleans are allowed in an if condition, array given\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/Pager/Pager.php', -]; $ignoreErrors[] = [ 'message' => '#^Property CodeIgniter\\\\Pager\\\\Pager\\:\\:\\$groups type has no value type specified in iterable type array\\.$#', 'count' => 1, 'path' => __DIR__ . '/system/Pager/Pager.php', ]; -$ignoreErrors[] = [ - 'message' => '#^Property CodeIgniter\\\\Pager\\\\Pager\\:\\:\\$only type has no value type specified in iterable type array\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/Pager/Pager.php', -]; $ignoreErrors[] = [ 'message' => '#^Property CodeIgniter\\\\Pager\\\\Pager\\:\\:\\$segment type has no value type specified in iterable type array\\.$#', 'count' => 1, From 0987c9ac0b1ff6a593c4fc9a5f3531bc2fa21c7c Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Wed, 3 Apr 2024 22:25:35 +0700 Subject: [PATCH 48/65] chore: [Rector] Apply ExplicitBoolCompareRector --- phpstan-baseline.php | 130 --------------------- rector.php | 2 + system/CLI/CLI.php | 4 +- system/Commands/Database/MigrateStatus.php | 2 +- system/Config/DotEnv.php | 2 +- system/Controller.php | 2 +- system/Database/OCI8/Builder.php | 2 +- system/HTTP/DownloadResponse.php | 2 +- system/Helpers/filesystem_helper.php | 18 +-- system/Helpers/form_helper.php | 2 +- system/Helpers/html_helper.php | 2 +- system/Helpers/url_helper.php | 2 +- system/Router/RouteCollection.php | 2 +- system/Session/Handlers/FileHandler.php | 2 +- system/View/Table.php | 2 +- tests/system/Cache/ResponseCacheTest.php | 2 +- 16 files changed, 25 insertions(+), 153 deletions(-) diff --git a/phpstan-baseline.php b/phpstan-baseline.php index 83a947a76f94..74a118c60a18 100644 --- a/phpstan-baseline.php +++ b/phpstan-baseline.php @@ -446,16 +446,6 @@ 'count' => 1, 'path' => __DIR__ . '/system/CLI/CLI.php', ]; -$ignoreErrors[] = [ - 'message' => '#^Only booleans are allowed in a negated boolean, array given\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/CLI/CLI.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Only booleans are allowed in a ternary operator condition, string given\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/CLI/CLI.php', -]; $ignoreErrors[] = [ 'message' => '#^Only booleans are allowed in a ternary operator condition, string\\|null given\\.$#', 'count' => 1, @@ -766,11 +756,6 @@ 'count' => 1, 'path' => __DIR__ . '/system/Commands/Database/MigrateStatus.php', ]; -$ignoreErrors[] = [ - 'message' => '#^Only booleans are allowed in a negated boolean, array\\, array\\\\> given\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/Commands/Database/MigrateStatus.php', -]; $ignoreErrors[] = [ 'message' => '#^Parameter \\#1 \\$params \\(array\\\\) of method CodeIgniter\\\\Commands\\\\Database\\\\MigrateStatus\\:\\:run\\(\\) should be contravariant with parameter \\$params \\(array\\\\) of method CodeIgniter\\\\CLI\\\\BaseCommand\\:\\:run\\(\\)$#', 'count' => 1, @@ -2131,11 +2116,6 @@ 'count' => 1, 'path' => __DIR__ . '/system/Config/DotEnv.php', ]; -$ignoreErrors[] = [ - 'message' => '#^Only booleans are allowed in a negated boolean, string given\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/Config/DotEnv.php', -]; $ignoreErrors[] = [ 'message' => '#^Method CodeIgniter\\\\Config\\\\Factories\\:\\:__callStatic\\(\\) has parameter \\$arguments with no value type specified in iterable type array\\.$#', 'count' => 1, @@ -2286,11 +2266,6 @@ 'count' => 1, 'path' => __DIR__ . '/system/Controller.php', ]; -$ignoreErrors[] = [ - 'message' => '#^Only booleans are allowed in a negated boolean, array given\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/Controller.php', -]; $ignoreErrors[] = [ 'message' => '#^Property CodeIgniter\\\\Controller\\:\\:\\$request \\(CodeIgniter\\\\HTTP\\\\CLIRequest\\|CodeIgniter\\\\HTTP\\\\IncomingRequest\\) does not accept CodeIgniter\\\\HTTP\\\\RequestInterface\\.$#', 'count' => 1, @@ -3731,11 +3706,6 @@ 'count' => 1, 'path' => __DIR__ . '/system/Database/OCI8/Builder.php', ]; -$ignoreErrors[] = [ - 'message' => '#^Only booleans are allowed in a ternary operator condition, int given\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/Database/OCI8/Builder.php', -]; $ignoreErrors[] = [ 'message' => '#^PHPDoc type CodeIgniter\\\\Database\\\\OCI8\\\\Connection of property CodeIgniter\\\\Database\\\\OCI8\\\\Builder\\:\\:\\$db is not the same as PHPDoc type CodeIgniter\\\\Database\\\\BaseConnection of overridden property CodeIgniter\\\\Database\\\\BaseBuilder\\:\\:\\$db\\.$#', 'count' => 1, @@ -6216,11 +6186,6 @@ 'count' => 1, 'path' => __DIR__ . '/system/HTTP/DownloadResponse.php', ]; -$ignoreErrors[] = [ - 'message' => '#^Only booleans are allowed in an if condition, string given\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/HTTP/DownloadResponse.php', -]; $ignoreErrors[] = [ 'message' => '#^Return type \\(CodeIgniter\\\\HTTP\\\\DownloadResponse\\) of method CodeIgniter\\\\HTTP\\\\DownloadResponse\\:\\:sendBody\\(\\) should be covariant with return type \\(\\$this\\(CodeIgniter\\\\HTTP\\\\Response\\)\\) of method CodeIgniter\\\\HTTP\\\\Response\\:\\:sendBody\\(\\)$#', 'count' => 1, @@ -7116,66 +7081,6 @@ 'count' => 1, 'path' => __DIR__ . '/system/Helpers/filesystem_helper.php', ]; -$ignoreErrors[] = [ - 'message' => '#^Only booleans are allowed in a ternary operator condition, int\\<0, 1024\\> given\\.$#', - 'count' => 2, - 'path' => __DIR__ . '/system/Helpers/filesystem_helper.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Only booleans are allowed in a ternary operator condition, int\\<0, 128\\> given\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/Helpers/filesystem_helper.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Only booleans are allowed in a ternary operator condition, int\\<0, 16\\> given\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/Helpers/filesystem_helper.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Only booleans are allowed in a ternary operator condition, int\\<0, 1\\> given\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/Helpers/filesystem_helper.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Only booleans are allowed in a ternary operator condition, int\\<0, 2048\\> given\\.$#', - 'count' => 2, - 'path' => __DIR__ . '/system/Helpers/filesystem_helper.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Only booleans are allowed in a ternary operator condition, int\\<0, 256\\> given\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/Helpers/filesystem_helper.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Only booleans are allowed in a ternary operator condition, int\\<0, 2\\> given\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/Helpers/filesystem_helper.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Only booleans are allowed in a ternary operator condition, int\\<0, 32\\> given\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/Helpers/filesystem_helper.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Only booleans are allowed in a ternary operator condition, int\\<0, 4\\> given\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/Helpers/filesystem_helper.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Only booleans are allowed in a ternary operator condition, int\\<0, 512\\> given\\.$#', - 'count' => 2, - 'path' => __DIR__ . '/system/Helpers/filesystem_helper.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Only booleans are allowed in a ternary operator condition, int\\<0, 64\\> given\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/Helpers/filesystem_helper.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Only booleans are allowed in a ternary operator condition, int\\<0, 8\\> given\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/Helpers/filesystem_helper.php', -]; $ignoreErrors[] = [ 'message' => '#^Right side of && is always true\\.$#', 'count' => 1, @@ -7386,11 +7291,6 @@ 'count' => 1, 'path' => __DIR__ . '/system/Helpers/form_helper.php', ]; -$ignoreErrors[] = [ - 'message' => '#^Only booleans are allowed in a negated boolean, string given\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/Helpers/form_helper.php', -]; $ignoreErrors[] = [ 'message' => '#^Function _list\\(\\) has parameter \\$attributes with no value type specified in iterable type array\\.$#', 'count' => 1, @@ -7471,11 +7371,6 @@ 'count' => 1, 'path' => __DIR__ . '/system/Helpers/html_helper.php', ]; -$ignoreErrors[] = [ - 'message' => '#^Only booleans are allowed in a ternary operator condition, string given\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/Helpers/html_helper.php', -]; $ignoreErrors[] = [ 'message' => '#^Function d\\(\\) has parameter \\$vars with no value type specified in iterable type array\\.$#', 'count' => 1, @@ -7576,11 +7471,6 @@ 'count' => 1, 'path' => __DIR__ . '/system/Helpers/url_helper.php', ]; -$ignoreErrors[] = [ - 'message' => '#^Only booleans are allowed in a ternary operator condition, string given\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/Helpers/url_helper.php', -]; $ignoreErrors[] = [ 'message' => '#^Variable \\$atts might not be defined\\.$#', 'count' => 1, @@ -8386,11 +8276,6 @@ 'count' => 1, 'path' => __DIR__ . '/system/Router/RouteCollection.php', ]; -$ignoreErrors[] = [ - 'message' => '#^Only booleans are allowed in a ternary operator condition, string given\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/Router/RouteCollection.php', -]; $ignoreErrors[] = [ 'message' => '#^Only booleans are allowed in a ternary operator condition, string\\|null given\\.$#', 'count' => 1, @@ -8641,11 +8526,6 @@ 'count' => 1, 'path' => __DIR__ . '/system/Session/Handlers/FileHandler.php', ]; -$ignoreErrors[] = [ - 'message' => '#^Only booleans are allowed in a negated boolean, string given\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/Session/Handlers/FileHandler.php', -]; $ignoreErrors[] = [ 'message' => '#^PHPDoc type string of property CodeIgniter\\\\Session\\\\Handlers\\\\FileHandler\\:\\:\\$savePath is not the same as PHPDoc type array\\|string of overridden property CodeIgniter\\\\Session\\\\Handlers\\\\BaseHandler\\:\\:\\$savePath\\.$#', 'count' => 1, @@ -10606,11 +10486,6 @@ 'count' => 1, 'path' => __DIR__ . '/system/View/Table.php', ]; -$ignoreErrors[] = [ - 'message' => '#^Only booleans are allowed in a ternary operator condition, float given\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/View/Table.php', -]; $ignoreErrors[] = [ 'message' => '#^Only booleans are allowed in an if condition, string\\|null given\\.$#', 'count' => 1, @@ -12036,11 +11911,6 @@ 'count' => 1, 'path' => __DIR__ . '/tests/system/Cache/ResponseCacheTest.php', ]; -$ignoreErrors[] = [ - 'message' => '#^Only booleans are allowed in a ternary operator condition, array given\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/tests/system/Cache/ResponseCacheTest.php', -]; $ignoreErrors[] = [ 'message' => '#^Re\\-assigning arrays to \\$_GET directly is discouraged\\.$#', 'count' => 3, diff --git a/rector.php b/rector.php index 321bf3930b06..e52be465b4e5 100644 --- a/rector.php +++ b/rector.php @@ -21,6 +21,7 @@ use Rector\CodeQuality\Rector\FuncCall\SingleInArrayToCompareRector; use Rector\CodeQuality\Rector\FunctionLike\SimplifyUselessVariableRector; use Rector\CodeQuality\Rector\If_\CombineIfRector; +use Rector\CodeQuality\Rector\If_\ExplicitBoolCompareRector; use Rector\CodeQuality\Rector\If_\ShortenElseIfRector; use Rector\CodeQuality\Rector\If_\SimplifyIfElseToTernaryRector; use Rector\CodeQuality\Rector\If_\SimplifyIfReturnBoolRector; @@ -158,6 +159,7 @@ $rectorConfig->rule(BooleanInIfConditionRuleFixerRector::class); $rectorConfig->rule(SingleInArrayToCompareRector::class); $rectorConfig->rule(VersionCompareFuncCallToConstantRector::class); + $rectorConfig->rule(ExplicitBoolCompareRector::class); $rectorConfig ->ruleWithConfiguration(StringClassNameToClassConstantRector::class, [ diff --git a/system/CLI/CLI.php b/system/CLI/CLI.php index 9a50e4038184..12e95ed37388 100644 --- a/system/CLI/CLI.php +++ b/system/CLI/CLI.php @@ -262,7 +262,7 @@ public static function prompt(string $field, $options = null, $validation = null $default = $options[0]; } - static::fwrite(STDOUT, $field . (trim($field) ? ' ' : '') . $extraOutput . ': '); + static::fwrite(STDOUT, $field . (trim($field) !== '' && trim($field) !== '0' ? ' ' : '') . $extraOutput . ': '); // Read the input from keyboard. $input = trim(static::input()) ?: $default; @@ -386,7 +386,7 @@ public static function promptByMultipleKeys(string $text, array $options): array */ private static function isZeroOptions(array $options): void { - if (! $options) { + if ($options === []) { throw new InvalidArgumentException('No options to select from were provided'); } } diff --git a/system/Commands/Database/MigrateStatus.php b/system/Commands/Database/MigrateStatus.php index cbbe2f70ce47..ecd074278d47 100644 --- a/system/Commands/Database/MigrateStatus.php +++ b/system/Commands/Database/MigrateStatus.php @@ -144,7 +144,7 @@ public function run(array $params) } } - if (! $status) { + if ($status === []) { // @codeCoverageIgnoreStart CLI::error(lang('Migrations.noneFound'), 'light_gray', 'red'); CLI::newLine(); diff --git a/system/Config/DotEnv.php b/system/Config/DotEnv.php index 05ecab900c3e..01bbb1ba6467 100644 --- a/system/Config/DotEnv.php +++ b/system/Config/DotEnv.php @@ -140,7 +140,7 @@ public function normaliseVariable(string $name, string $value = ''): array */ protected function sanitizeValue(string $value): string { - if (! $value) { + if ($value === '' || $value === '0') { return $value; } diff --git a/system/Controller.php b/system/Controller.php index 0cc14c569651..83bc4f4b120e 100644 --- a/system/Controller.php +++ b/system/Controller.php @@ -187,7 +187,7 @@ private function setValidator($rules, array $messages): void } // If no error message is defined, use the error message in the Config\Validation file - if (! $messages) { + if ($messages === []) { $errorName = $rules . '_errors'; $messages = $validation->{$errorName} ?? []; } diff --git a/system/Database/OCI8/Builder.php b/system/Database/OCI8/Builder.php index f5527ce745c6..37ba54a27392 100644 --- a/system/Database/OCI8/Builder.php +++ b/system/Database/OCI8/Builder.php @@ -222,7 +222,7 @@ protected function _limit(string $sql, bool $offsetIgnore = false): string } $this->limitUsed = true; - $limitTemplateQuery = 'SELECT * FROM (SELECT INNER_QUERY.*, ROWNUM RNUM FROM (%s) INNER_QUERY WHERE ROWNUM < %d)' . ($offset ? ' WHERE RNUM >= %d' : ''); + $limitTemplateQuery = 'SELECT * FROM (SELECT INNER_QUERY.*, ROWNUM RNUM FROM (%s) INNER_QUERY WHERE ROWNUM < %d)' . ($offset !== 0 ? ' WHERE RNUM >= %d' : ''); return sprintf($limitTemplateQuery, $sql, $offset + $this->QBLimit + 1, $offset); } diff --git a/system/HTTP/DownloadResponse.php b/system/HTTP/DownloadResponse.php index 14dc3fec1421..df9eef8c147f 100644 --- a/system/HTTP/DownloadResponse.php +++ b/system/HTTP/DownloadResponse.php @@ -194,7 +194,7 @@ private function getContentDisposition(): string $result = sprintf('attachment; filename="%s"', $downloadFilename); - if ($utf8Filename) { + if ($utf8Filename !== '' && $utf8Filename !== '0') { $result .= '; filename*=UTF-8\'\'' . rawurlencode($utf8Filename); } diff --git a/system/Helpers/filesystem_helper.php b/system/Helpers/filesystem_helper.php index 4e0e8b1532e9..46700ffc2797 100644 --- a/system/Helpers/filesystem_helper.php +++ b/system/Helpers/filesystem_helper.php @@ -379,19 +379,19 @@ function symbolic_permissions(int $perms): string } // Owner - $symbolic .= (($perms & 0x0100) ? 'r' : '-') - . (($perms & 0x0080) ? 'w' : '-') - . (($perms & 0x0040) ? (($perms & 0x0800) ? 's' : 'x') : (($perms & 0x0800) ? 'S' : '-')); + $symbolic .= ((($perms & 0x0100) !== 0) ? 'r' : '-') + . ((($perms & 0x0080) !== 0) ? 'w' : '-') + . ((($perms & 0x0040) !== 0) ? ((($perms & 0x0800) !== 0) ? 's' : 'x') : ((($perms & 0x0800) !== 0) ? 'S' : '-')); // Group - $symbolic .= (($perms & 0x0020) ? 'r' : '-') - . (($perms & 0x0010) ? 'w' : '-') - . (($perms & 0x0008) ? (($perms & 0x0400) ? 's' : 'x') : (($perms & 0x0400) ? 'S' : '-')); + $symbolic .= ((($perms & 0x0020) !== 0) ? 'r' : '-') + . ((($perms & 0x0010) !== 0) ? 'w' : '-') + . ((($perms & 0x0008) !== 0) ? ((($perms & 0x0400) !== 0) ? 's' : 'x') : ((($perms & 0x0400) !== 0) ? 'S' : '-')); // World - $symbolic .= (($perms & 0x0004) ? 'r' : '-') - . (($perms & 0x0002) ? 'w' : '-') - . (($perms & 0x0001) ? (($perms & 0x0200) ? 't' : 'x') : (($perms & 0x0200) ? 'T' : '-')); + $symbolic .= ((($perms & 0x0004) !== 0) ? 'r' : '-') + . ((($perms & 0x0002) !== 0) ? 'w' : '-') + . ((($perms & 0x0001) !== 0) ? ((($perms & 0x0200) !== 0) ? 't' : 'x') : ((($perms & 0x0200) !== 0) ? 'T' : '-')); return $symbolic; } diff --git a/system/Helpers/form_helper.php b/system/Helpers/form_helper.php index 098222e90e9f..e21b27787937 100644 --- a/system/Helpers/form_helper.php +++ b/system/Helpers/form_helper.php @@ -29,7 +29,7 @@ function form_open(string $action = '', $attributes = [], array $hidden = []): string { // If no action is provided then set to the current url - if (! $action) { + if ($action === '' || $action === '0') { $action = current_url(true); } // If an action is not a full URL then turn it into one elseif (strpos($action, '://') === false) { diff --git a/system/Helpers/html_helper.php b/system/Helpers/html_helper.php index 0f7e154a02bf..9fe4334f413b 100755 --- a/system/Helpers/html_helper.php +++ b/system/Helpers/html_helper.php @@ -195,7 +195,7 @@ function doctype(string $type = 'html5'): string function script_tag($src = '', bool $indexPage = false): string { $cspNonce = csp_script_nonce(); - $cspNonce = $cspNonce ? ' ' . $cspNonce : $cspNonce; + $cspNonce = $cspNonce !== '' && $cspNonce !== '0' ? ' ' . $cspNonce : $cspNonce; $script = ' $src]; diff --git a/system/Helpers/url_helper.php b/system/Helpers/url_helper.php index f992eac391f8..d1ff870c2f6e 100644 --- a/system/Helpers/url_helper.php +++ b/system/Helpers/url_helper.php @@ -318,7 +318,7 @@ function safe_mailto(string $email, string $title = '', $attributes = ''): strin // improve obfuscation by eliminating newlines & whitespace $cspNonce = csp_script_nonce(); - $cspNonce = $cspNonce ? ' ' . $cspNonce : $cspNonce; + $cspNonce = $cspNonce !== '' && $cspNonce !== '0' ? ' ' . $cspNonce : $cspNonce; $output = '' . 'var l=new Array();'; diff --git a/system/Router/RouteCollection.php b/system/Router/RouteCollection.php index 2adb0ed98638..4bac4dd4403b 100644 --- a/system/Router/RouteCollection.php +++ b/system/Router/RouteCollection.php @@ -767,7 +767,7 @@ public function group(string $name, ...$params) // To register a route, we'll set a flag so that our router // will see the group name. // If the group name is empty, we go on using the previously built group name. - $this->group = $name ? trim($oldGroup . '/' . $name, '/') : $oldGroup; + $this->group = $name !== '' && $name !== '0' ? trim($oldGroup . '/' . $name, '/') : $oldGroup; $callback = array_pop($params); diff --git a/system/Session/Handlers/FileHandler.php b/system/Session/Handlers/FileHandler.php index 9eb642408381..cad7283f8073 100644 --- a/system/Session/Handlers/FileHandler.php +++ b/system/Session/Handlers/FileHandler.php @@ -73,7 +73,7 @@ public function __construct(SessionConfig $config, string $ipAddress) } else { $sessionPath = rtrim(ini_get('session.save_path'), '/\\'); - if (! $sessionPath) { + if ($sessionPath === '' || $sessionPath === '0') { $sessionPath = WRITEPATH . 'session'; } diff --git a/system/View/Table.php b/system/View/Table.php index c9cd33d39ac6..65b43195d988 100644 --- a/system/View/Table.php +++ b/system/View/Table.php @@ -365,7 +365,7 @@ public function generate($tableData = null) foreach ($this->rows as $row) { // We use modulus to alternate the row colors - $name = fmod($i++, 2) ? '' : 'alt_'; + $name = fmod($i++, 2) !== 0.0 ? '' : 'alt_'; $out .= $this->template['row_' . $name . 'start'] . $this->newline; diff --git a/tests/system/Cache/ResponseCacheTest.php b/tests/system/Cache/ResponseCacheTest.php index b34cfce95c37..d55d625cd7e2 100644 --- a/tests/system/Cache/ResponseCacheTest.php +++ b/tests/system/Cache/ResponseCacheTest.php @@ -48,7 +48,7 @@ private function createIncomingRequest( ): IncomingRequest { $_POST = $_GET = $_SERVER = $_REQUEST = $_ENV = $_COOKIE = $_SESSION = []; - $_SERVER['REQUEST_URI'] = '/' . $uri . ($query ? '?' . http_build_query($query) : ''); + $_SERVER['REQUEST_URI'] = '/' . $uri . ($query !== [] ? '?' . http_build_query($query) : ''); $_SERVER['SCRIPT_NAME'] = '/index.php'; $appConfig ??= $this->appConfig; From a7ab6c2e9663ec74c7a25a6966d64daecbbe3a7d Mon Sep 17 00:00:00 2001 From: kenjis Date: Thu, 28 Mar 2024 13:00:24 +0900 Subject: [PATCH 49/65] docs: add sub section OWASP Top 10 2021 --- user_guide_src/source/concepts/security.rst | 76 ++++++++++----------- 1 file changed, 35 insertions(+), 41 deletions(-) diff --git a/user_guide_src/source/concepts/security.rst b/user_guide_src/source/concepts/security.rst index 69f2221a644f..aa990cf57e48 100644 --- a/user_guide_src/source/concepts/security.rst +++ b/user_guide_src/source/concepts/security.rst @@ -17,11 +17,14 @@ the CodeIgniter provisions to address the problem. .. contents:: :local: - :depth: 1 + :depth: 2 + +***************** +OWASP Top 10 2021 +***************** -****************************** A01:2021 Broken Access Control -****************************** +============================== Access control enforces policy such that users cannot act outside of their intended permissions. Failures typically lead to unauthorized information disclosure, @@ -49,7 +52,7 @@ Common access control vulnerabilities include: pages as a standard user. OWASP recommendations -===================== +--------------------- Access control is only effective in trusted server-side code or server-less API, where the attacker cannot modify the access control check or metadata. @@ -71,7 +74,7 @@ where the attacker cannot modify the access control check or metadata. follow the OAuth standards to revoke access. CodeIgniter provisions -====================== +---------------------- - :ref:`Public ` folder, with application and system outside @@ -83,9 +86,8 @@ CodeIgniter provisions - An official authentication and authorization framework :ref:`CodeIgniter Shield ` - Easy to add third party authentication -******************************* A02:2021 Cryptographic Failures -******************************* +=============================== The first thing is to determine the protection needs of data in transit and at rest. For example, passwords, credit card numbers, health records, personal @@ -123,7 +125,7 @@ or regulations, e.g., financial data protection such as PCI Data Security Standa example in the form of padding oracle attacks? OWASP recommendations -===================== +--------------------- Do the following, at a minimum, and consult the references: @@ -161,7 +163,7 @@ Do the following, at a minimum, and consult the references: - Verify independently the effectiveness of configuration and settings. CodeIgniter provisions -====================== +---------------------- - The config for global secure access (``Config\App::$forceGlobalSecureRequests``) - :php:func:`force_https()` function @@ -170,9 +172,8 @@ CodeIgniter provisions - An official authentication and authorization framework :ref:`CodeIgniter Shield ` -****************** A03:2021 Injection -****************** +================== An application is vulnerable to attack when: @@ -194,7 +195,7 @@ dynamic (DAST), and interactive (IAST) application security testing tools into the CI/CD pipeline to identify introduced injection flaws before production deployment. OWASP recommendations -===================== +--------------------- Preventing injection requires keeping data separate from commands and queries: @@ -218,7 +219,7 @@ Preventing injection requires keeping data separate from commands and queries: records in case of SQL injection. CodeIgniter provisions -====================== +---------------------- - :ref:`urls-uri-security` - :ref:`invalidchars` filter @@ -231,9 +232,8 @@ CodeIgniter provisions - :ref:`Database escape methods ` - :ref:`database-queries-query-bindings` -************************ A04:2021 Insecure Design -************************ +======================== Insecure design is a broad category representing different weaknesses, expressed as “missing or ineffective control design.” Insecure design is not the source for @@ -249,7 +249,7 @@ lack of business risk profiling inherent in the software or system being develop and thus the failure to determine what level of security design is required. OWASP recommendations -===================== +--------------------- - Establish and use a secure development lifecycle with AppSec professionals to help evaluate and design security and privacy-related controls @@ -269,15 +269,14 @@ OWASP recommendations - Limit resource consumption by user or service CodeIgniter provisions -====================== +---------------------- - :doc:`PHPUnit testing <../testing/overview>` - :doc:`../libraries/throttler` for rate limit - An official authentication and authorization framework :ref:`CodeIgniter Shield ` -********************************** A05:2021 Security Misconfiguration -********************************** +================================== The application might be vulnerable if the application is: @@ -301,7 +300,7 @@ Without a concerted, repeatable application security configuration process, systems are at a higher risk. OWASP recommendations -===================== +--------------------- Secure installation processes should be implemented, including: @@ -323,14 +322,13 @@ Secure installation processes should be implemented, including: settings in all environments. CodeIgniter provisions -====================== +---------------------- - :ref:`Production mode ` by default - :ref:`secureheaders` filter -******************************************* A06:2021 Vulnerable and Outdated Components -******************************************* +=========================================== You are likely vulnerable: @@ -352,7 +350,7 @@ You are likely vulnerable: Misconfiguration). OWASP recommendations -===================== +--------------------- There should be a patch management process in place to: @@ -378,13 +376,12 @@ applying updates or configuration changes for the lifetime of the application or portfolio. CodeIgniter provisions -====================== +---------------------- - Easy :ref:`app-starter-upgrading` by Composer -*************************************************** A07:2021 Identification and Authentication Failures -*************************************************** +=================================================== Confirmation of the user's identity, authentication, and session management is critical to protect against authentication-related attacks. There may be @@ -406,7 +403,7 @@ authentication weaknesses if the application: or a period of inactivity. OWASP recommendations -===================== +--------------------- - Where possible, implement multi-factor authentication to prevent automated credential stuffing, brute force, and stolen credential reuse attacks. @@ -427,15 +424,14 @@ OWASP recommendations timeouts. CodeIgniter provisions -====================== +---------------------- - :doc:`Session <../libraries/sessions>` library - An official authentication and authorization framework :ref:`CodeIgniter Shield ` -********************************************* A08:2021 Software and Data Integrity Failures -********************************************* +============================================= Software and data integrity failures relate to code and infrastructure that does not protect against integrity violations. An example of this is where an application @@ -452,7 +448,7 @@ Another example is where objects or data are encoded or serialized into a struct that an attacker can see and modify is vulnerable to insecure deserialization. OWASP recommendations -===================== +--------------------- - Use digital signatures or similar mechanisms to verify the software or data is from the expected source and has not been altered. @@ -473,13 +469,12 @@ OWASP recommendations tampering or replay of the serialized data CodeIgniter provisions -====================== +---------------------- - n/a -************************************************* A09:2021 Security Logging and Monitoring Failures -************************************************* +================================================= This category is to help detect, escalate, and respond to active breaches. Without logging and monitoring, breaches cannot be detected. Insufficient logging, detection, @@ -501,7 +496,7 @@ You are vulnerable to information leakage by making logging and alerting events visible to a user or an attacker (see A01:2021-Broken Access Control). OWASP recommendations -===================== +--------------------- Developers should implement some or all the following controls, depending on the risk of the application: @@ -525,15 +520,14 @@ such as the Elasticsearch, Logstash, Kibana (ELK) stack, that feature custom dashboards and alerting. CodeIgniter provisions -====================== +---------------------- - :doc:`Logging <../general/logging>` library - An official authentication and authorization framework :ref:`CodeIgniter Shield ` -******************************************* A10:2021 Server-Side Request Forgery (SSRF) -******************************************* +=========================================== SSRF flaws occur whenever a web application is fetching a remote resource without validating the user-supplied URL. It allows an attacker to coerce the application @@ -546,7 +540,7 @@ Also, the severity of SSRF is becoming higher due to cloud services and the complexity of architectures. OWASP recommendations -===================== +--------------------- Developers can prevent SSRF by implementing some or all the following defense in depth controls: @@ -578,7 +572,7 @@ Do not mitigate SSRF via the use of a deny list or regular expression. Attackers have payload lists, tools, and skills to bypass deny lists. CodeIgniter provisions -====================== +---------------------- - :doc:`../libraries/validation` library - :doc:`HTTP library <../incoming/incomingrequest>` provides for From 24089a3e99a36b17ef5c6a3ba4ab5ba67f1b3f56 Mon Sep 17 00:00:00 2001 From: kenjis Date: Thu, 28 Mar 2024 17:52:19 +0900 Subject: [PATCH 50/65] docs: add "OWASP API Security Top 10 2023" --- user_guide_src/source/concepts/security.rst | 358 +++++++++++++++++++- user_guide_src/source/models/model.rst | 2 + 2 files changed, 358 insertions(+), 2 deletions(-) diff --git a/user_guide_src/source/concepts/security.rst b/user_guide_src/source/concepts/security.rst index aa990cf57e48..6d8f5839e8f3 100644 --- a/user_guide_src/source/concepts/security.rst +++ b/user_guide_src/source/concepts/security.rst @@ -10,8 +10,9 @@ We respect the `Open Web Application Security Project (OWASP) `_, -identifying the top vulnerabilities for web applications. +`OWASP Top Ten `_ and +`OWASP API Security Top 10 `_ +identifying the top vulnerabilities for web applications and apis. For each, we provide a brief description, the OWASP recommendations, and then the CodeIgniter provisions to address the problem. @@ -577,3 +578,356 @@ CodeIgniter provisions - :doc:`../libraries/validation` library - :doc:`HTTP library <../incoming/incomingrequest>` provides for :ref:`input field filtering ` + +****************************** +OWASP API Security Top 10 2023 +****************************** + +API1:2023 Broken Object Level Authorization +=========================================== + +APIs tend to expose endpoints that handle object identifiers, creating a wide +attack surface of Object Level Access Control issues. Object level authorization +checks should be considered in every function that accesses a data source using +an ID from the user. + +OWASP recommendations +--------------------- + +- Implement a proper authorization mechanism that relies on the user policies and + hierarchy. +- Use the authorization mechanism to check if the logged-in user has access to + perform the requested action on the record in every function that uses an input + from the client to access a record in the database. +- Prefer the use of random and unpredictable values as GUIDs for records' IDs. +- Write tests to evaluate the vulnerability of the authorization mechanism. Do + not deploy changes that make the tests fail. + +CodeIgniter provisions +---------------------- + +- An official authentication and authorization framework + :ref:`CodeIgniter Shield ` + +API2:2023 Broken Authentication +=============================== + +Authentication mechanisms are often implemented incorrectly, allowing attackers +to compromise authentication tokens or to exploit implementation flaws to assume +other user's identities temporarily or permanently. Compromising a system's +ability to identify the client/user, compromises API security overall. + +OWASP recommendations +--------------------- + +- Make sure you know all the possible flows to authenticate to the API (mobile/ + web/deep links that implement one-click authentication/etc.). Ask your engineers + what flows you missed. +- Read about your authentication mechanisms. Make sure you understand what and + how they are used. OAuth is not authentication, and neither are API keys. +- Don't reinvent the wheel in authentication, token generation, or password storage. + Use the standards. +- Credential recovery/forgot password endpoints should be treated as login + endpoints in terms of brute force, rate limiting, and lockout protections. +- Require re-authentication for sensitive operations (e.g. changing the account + owner email address/2FA phone number). +- Use the OWASP Authentication Cheatsheet. +- Where possible, implement multi-factor authentication. +- Implement anti-brute force mechanisms to mitigate credential stuffing, dictionary + attacks, and brute force attacks on your authentication endpoints. This mechanism + should be stricter than the regular rate limiting mechanisms on your APIs. +- Implement account lockout/captcha mechanisms to prevent brute force attacks + against specific users. Implement weak-password checks. +- API keys should not be used for user authentication. They should only be used + for API clients authentication. + +CodeIgniter provisions +---------------------- + +- An official authentication and authorization framework + :ref:`CodeIgniter Shield ` + +API3:2023 Broken Object Property Level Authorization +==================================================== + +This category combines API3:2019 Excessive Data Exposure and API6:2019 - Mass +Assignment, focusing on the root cause: the lack of or improper authorization +validation at the object property level. This leads to information exposure or +manipulation by unauthorized parties. + +OWASP recommendations +--------------------- + +- When exposing an object using an API endpoint, always make sure that the user + should have access to the object's properties you expose. +- Avoid using generic methods such as to_json() and to_string(). Instead, + cherry-pick specific object properties you specifically want to return. +- If possible, avoid using functions that automatically bind a client's input + into code variables, internal objects, or object properties ("Mass Assignment"). +- Allow changes only to the object's properties that should be updated by the + client. +- Implement a schema-based response validation mechanism as an extra layer of + security. As part of this mechanism, define and enforce data returned by all + API methods. +- Keep returned data structures to the bare minimum, according to the + business/functional requirements for the endpoint. + +CodeIgniter provisions +---------------------- + +- Model's :ref:`model-allowed-fields` +- An official authentication and authorization framework + :ref:`CodeIgniter Shield ` + +API4:2023 Unrestricted Resource Consumption +=========================================== + +Satisfying API requests requires resources such as network bandwidth, CPU, memory, +and storage. Other resources such as emails/SMS/phone calls or biometrics validation +are made available by service providers via API integrations, and paid for per +request. Successful attacks can lead to Denial of Service or an increase of +operational costs. + +OWASP recommendations +--------------------- + +- Use a solution that makes it easy to limit memory, CPU, number of restarts, + file descriptors, and processes such as Containers / Serverless code (e.g. Lambdas). +- Define and enforce a maximum size of data on all incoming parameters and payloads, + such as maximum length for strings, maximum number of elements in arrays, and + maximum upload file size (regardless of whether it is stored locally or in + cloud storage). +- Implement a limit on how often a client can interact with the API within a + defined timeframe (rate limiting). +- Rate limiting should be fine tuned based on the business needs. Some API Endpoints + might require stricter policies. +- Limit/throttle how many times or how often a single API client/user can execute + a single operation (e.g. validate an OTP, or request password recovery without + visiting the one-time URL). +- Add proper server-side validation for query string and request body parameters, + specifically the one that controls the number of records to be returned in the + response. +- Configure spending limits for all service providers/API integrations. When setting + spending limits is not possible, billing alerts should be configured instead. + +CodeIgniter provisions +---------------------- + +- :doc:`../libraries/validation` library +- :doc:`../libraries/throttler` for rate limit + +API5:2023 Broken Function Level Authorization +============================================= + +Complex access control policies with different hierarchies, groups, and roles, +and an unclear separation between administrative and regular functions, tend to +lead to authorization flaws. By exploiting these issues, attackers can gain +access to other users’ resources and/or administrative functions. + +OWASP recommendations +--------------------- + +Your application should have a consistent and easy-to-analyze authorization module +that is invoked from all your business functions. Frequently, such protection is +provided by one or more components external to the application code. + +- The enforcement mechanism(s) should deny all access by default, requiring explicit + grants to specific roles for access to every function. +- Review your API endpoints against function level authorization flaws, while + keeping in mind the business logic of the application and groups hierarchy. +- Make sure that all of your administrative controllers inherit from an + administrative abstract controller that implements authorization checks based + on the user's group/role. +- Make sure that administrative functions inside a regular controller implement + authorization checks based on the user's group and role. + +CodeIgniter provisions +---------------------- + +- An official authentication and authorization framework + :ref:`CodeIgniter Shield ` + +API6:2023 Unrestricted Access to Sensitive Business Flows +========================================================= + +APIs vulnerable to this risk expose a business flow - such as buying a ticket, +or posting a comment - without compensating for how the functionality could harm +the business if used excessively in an automated manner. This doesn't necessarily +come from implementation bugs. + +OWASP recommendations +--------------------- + +The mitigation planning should be done in two layers: + +- Business - identify the business flows that might harm the business if they + are excessively used. +- Engineering - choose the right protection mechanisms to mitigate the business + risk. + +Some of the protection mechanisms are more simple while others are more difficult +to implement. The following methods are used to slow down automated threats: + +- Device fingerprinting: denying service to unexpected client devices (e.g + headless browsers) tends to make threat actors use more sophisticated solutions, + thus more costly for them +- Human detection: using either captcha or more advanced biometric solutions + (e.g. typing patterns) +- Non-human patterns: analyze the user flow to detect non-human patterns + (e.g. the user accessed the "add to cart" and "complete purchase" functions in + less than one second) +- Consider blocking IP addresses of Tor exit nodes and well-known proxies + +Secure and limit access to APIs that are consumed directly by machines (such as +developer and B2B APIs). They tend to be an easy target for attackers because +they often don't implement all the required protection mechanisms. + +CodeIgniter provisions +---------------------- + +- n/a + +API7:2023 Server Side Request Forgery +===================================== + +Server-Side Request Forgery (SSRF) flaws can occur when an API is fetching a +remote resource without validating the user-supplied URI. This enables an attacker +to coerce the application to send a crafted request to an unexpected destination, +even when protected by a firewall or a VPN. + +OWASP recommendations +--------------------- + +- Isolate the resource fetching mechanism in your network: usually these features + are aimed to retrieve remote resources and not internal ones. +- Whenever possible, use allow lists of: + + - Remote origins users are expected to download resources from (e.g. Google Drive, + Gravatar, etc.) + - URL schemes and ports + - Accepted media types for a given functionality +- Disable HTTP redirections. +- Use a well-tested and maintained URL parser to avoid issues caused by URL parsing inconsistencies. +- Validate and sanitize all client-supplied input data. +- Do not send raw responses to clients. + +CodeIgniter provisions +---------------------- + +- :doc:`../libraries/validation` library +- :doc:`HTTP library <../incoming/incomingrequest>` provides for + :ref:`input field filtering ` +- :doc:`CURLRequest <../libraries/curlrequest>` class +- :doc:`URI <../libraries/uri>` class + +API8:2023 Security Misconfiguration +=================================== + +APIs and the systems supporting them typically contain complex configurations, +meant to make the APIs more customizable. Software and DevOps engineers can miss +these configurations, or don't follow security best practices when it comes to +configuration, opening the door for different types of attacks. + +OWASP recommendations +--------------------- + +The API life cycle should include: + +- A repeatable hardening process leading to fast and easy deployment of a properly + locked down environment +- A task to review and update configurations across the entire API stack. The + review should include: orchestration files, API components, and cloud services + (e.g. S3 bucket permissions) +- An automated process to continuously assess the effectiveness of the configuration + and settings in all environments + +Furthermore: + +- Ensure that all API communications from the client to the API server and any + downstream/upstream components happen over an encrypted communication channel + (TLS), regardless of whether it is an internal or public-facing API. +- Be specific about which HTTP verbs each API can be accessed by: all other HTTP + verbs should be disabled (e.g. HEAD). +- APIs expecting to be accessed from browser-based clients (e.g., WebApp front-end) + should, at least: + + - implement a proper Cross-Origin Resource Sharing (CORS) policy + - include applicable Security Headers +- Restrict incoming content types/data formats to those that meet the business/ + functional requirements. +- Ensure all servers in the HTTP server chain (e.g. load balancers, reverse and + forward proxies, and back-end servers) process incoming requests in a uniform + manner to avoid desync issues. +- Where applicable, define and enforce all API response payload schemas, including + error responses, to prevent exception traces and other valuable information from + being sent back to attackers. + +CodeIgniter provisions +---------------------- + +- The config for global secure access (``Config\App::$forceGlobalSecureRequests``) +- :php:func:`force_https()` function +- :ref:`Defined Route Routing ` +- :ref:`auto-routing-improved` + +API9:2023 Improper Inventory Management +======================================= + +APIs tend to expose more endpoints than traditional web applications, making +proper and updated documentation highly important. A proper inventory of hosts +and deployed API versions also are important to mitigate issues such as deprecated +API versions and exposed debug endpoints. + +OWASP recommendations +--------------------- + +- Inventory all API hosts and document important aspects of each one of them, + focusing on the API environment (e.g. production, staging, test, development), + who should have network access to the host (e.g. public, internal, partners) + and the API version. +- Inventory integrated services and document important aspects such as their + role in the system, what data is exchanged (data flow), and their sensitivity. +- Document all aspects of your API such as authentication, errors, redirects, + rate limiting, cross-origin resource sharing (CORS) policy, and endpoints, + including their parameters, requests, and responses. +- Generate documentation automatically by adopting open standards. Include the + documentation build in your CI/CD pipeline. +- Make API documentation available only to those authorized to use the API. +- Use external protection measures such as API security specific solutions for + all exposed versions of your APIs, not just for the current production version. +- Avoid using production data with non-production API deployments. If this is + unavoidable, these endpoints should get the same security treatment as the + production ones. +- When newer versions of APIs include security improvements, perform a risk + analysis to inform the mitigation actions required for the older versions. For + example, whether it is possible to backport the improvements without breaking + API compatibility or if you need to take the older version out quickly and + force all clients to move to the latest version. + +CodeIgniter provisions +---------------------- + +- :ref:`routing-spark-routes` command + +API10:2023 Unsafe Consumption of APIs +===================================== + +Developers tend to trust data received from third-party APIs more than user input, +and so tend to adopt weaker security standards. In order to compromise APIs, +attackers go after integrated third-party services instead of trying to compromise +the target API directly. + +OWASP recommendations +--------------------- + +- When evaluating service providers, assess their API security posture. +- Ensure all API interactions happen over a secure communication channel (TLS). +- Always validate and properly sanitize data received from integrated APIs before + using it. +- Maintain an allowlist of well-known locations integrated APIs may redirect yours + to: do not blindly follow redirects. + +CodeIgniter provisions +---------------------- + +- :doc:`../libraries/validation` library diff --git a/user_guide_src/source/models/model.rst b/user_guide_src/source/models/model.rst index 41a754bc25ee..ce561e0be810 100644 --- a/user_guide_src/source/models/model.rst +++ b/user_guide_src/source/models/model.rst @@ -151,6 +151,8 @@ configured to any name of your choice by using `$deletedField`_ property. .. important:: The ``deleted_at`` field in the database must be nullable. +.. _model-allowed-fields: + $allowedFields -------------- From ea56ea29f25ec61e667e665312bb92404fa7ee2f Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 3 Apr 2024 21:28:18 +0900 Subject: [PATCH 51/65] docs: fix PHPDoc types in Database --- phpstan-baseline.php | 30 -------------------------- system/Database/BaseConnection.php | 10 +++++++-- system/Database/MySQLi/Connection.php | 4 ++-- system/Database/OCI8/Connection.php | 4 ++-- system/Database/Postgre/Connection.php | 4 ++-- system/Database/SQLSRV/Connection.php | 4 ++-- system/Database/SQLite3/Connection.php | 4 ++-- system/Database/SQLite3/Table.php | 6 +++--- 8 files changed, 21 insertions(+), 45 deletions(-) diff --git a/phpstan-baseline.php b/phpstan-baseline.php index 83a947a76f94..5d77400e623c 100644 --- a/phpstan-baseline.php +++ b/phpstan-baseline.php @@ -2851,16 +2851,6 @@ 'count' => 1, 'path' => __DIR__ . '/system/Database/BaseConnection.php', ]; -$ignoreErrors[] = [ - 'message' => '#^Method CodeIgniter\\\\Database\\\\BaseConnection\\:\\:_foreignKeyData\\(\\) return type has no value type specified in iterable type array\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/Database/BaseConnection.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method CodeIgniter\\\\Database\\\\BaseConnection\\:\\:_indexData\\(\\) return type has no value type specified in iterable type array\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/Database/BaseConnection.php', -]; $ignoreErrors[] = [ 'message' => '#^Method CodeIgniter\\\\Database\\\\BaseConnection\\:\\:callFunction\\(\\) has parameter \\$params with no value type specified in iterable type array\\.$#', 'count' => 1, @@ -2896,11 +2886,6 @@ 'count' => 1, 'path' => __DIR__ . '/system/Database/BaseConnection.php', ]; -$ignoreErrors[] = [ - 'message' => '#^Method CodeIgniter\\\\Database\\\\BaseConnection\\:\\:foreignKeyDataToObjects\\(\\) return type has no value type specified in iterable type array\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/Database/BaseConnection.php', -]; $ignoreErrors[] = [ 'message' => '#^Method CodeIgniter\\\\Database\\\\BaseConnection\\:\\:getFieldNames\\(\\) return type has no value type specified in iterable type array\\.$#', 'count' => 1, @@ -2911,11 +2896,6 @@ 'count' => 1, 'path' => __DIR__ . '/system/Database/BaseConnection.php', ]; -$ignoreErrors[] = [ - 'message' => '#^Method CodeIgniter\\\\Database\\\\BaseConnection\\:\\:getIndexData\\(\\) return type has no value type specified in iterable type array\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/Database/BaseConnection.php', -]; $ignoreErrors[] = [ 'message' => '#^Method CodeIgniter\\\\Database\\\\BaseConnection\\:\\:listTables\\(\\) return type has no value type specified in iterable type array\\.$#', 'count' => 1, @@ -9391,16 +9371,6 @@ 'count' => 1, 'path' => __DIR__ . '/system/Test/Mock/MockConnection.php', ]; -$ignoreErrors[] = [ - 'message' => '#^Method CodeIgniter\\\\Test\\\\Mock\\\\MockConnection\\:\\:_foreignKeyData\\(\\) return type has no value type specified in iterable type array\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/Test/Mock/MockConnection.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method CodeIgniter\\\\Test\\\\Mock\\\\MockConnection\\:\\:_indexData\\(\\) return type has no value type specified in iterable type array\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/Test/Mock/MockConnection.php', -]; $ignoreErrors[] = [ 'message' => '#^Method CodeIgniter\\\\Test\\\\Mock\\\\MockConnection\\:\\:shouldReturn\\(\\) has no return type specified\\.$#', 'count' => 1, diff --git a/system/Database/BaseConnection.php b/system/Database/BaseConnection.php index ae5c4b3805f1..93bde5ddca6d 100644 --- a/system/Database/BaseConnection.php +++ b/system/Database/BaseConnection.php @@ -1527,7 +1527,7 @@ public function getFieldData(string $table) /** * Returns an object with key data * - * @return array + * @return array */ public function getIndexData(string $table) { @@ -1547,7 +1547,9 @@ public function getForeignKeyData(string $table) /** * Converts array of arrays generated by _foreignKeyData() to array of objects * - * @return array[ + * @return array + * + * array[ * {constraint_name} => * stdClass[ * 'constraint_name' => string, @@ -1704,6 +1706,8 @@ abstract protected function _fieldData(string $table): array; * Platform-specific index data. * * @see getIndexData() + * + * @return array */ abstract protected function _indexData(string $table): array; @@ -1711,6 +1715,8 @@ abstract protected function _indexData(string $table): array; * Platform-specific foreign keys data. * * @see getForeignKeyData() + * + * @return array */ abstract protected function _foreignKeyData(string $table): array; diff --git a/system/Database/MySQLi/Connection.php b/system/Database/MySQLi/Connection.php index 03aab408398a..b499e2ca81d6 100644 --- a/system/Database/MySQLi/Connection.php +++ b/system/Database/MySQLi/Connection.php @@ -443,7 +443,7 @@ protected function _fieldData(string $table): array /** * Returns an array of objects with index data * - * @return list + * @return array * * @throws DatabaseException * @throws LogicException @@ -489,7 +489,7 @@ protected function _indexData(string $table): array /** * Returns an array of objects with Foreign key data * - * @return list + * @return array * * @throws DatabaseException */ diff --git a/system/Database/OCI8/Connection.php b/system/Database/OCI8/Connection.php index fc7920e2a8ff..2feb2d36df4b 100644 --- a/system/Database/OCI8/Connection.php +++ b/system/Database/OCI8/Connection.php @@ -325,7 +325,7 @@ protected function _fieldData(string $table): array /** * Returns an array of objects with index data * - * @return list + * @return array * * @throws DatabaseException */ @@ -374,7 +374,7 @@ protected function _indexData(string $table): array /** * Returns an array of objects with Foreign key data * - * @return list + * @return array * * @throws DatabaseException */ diff --git a/system/Database/Postgre/Connection.php b/system/Database/Postgre/Connection.php index baef703b7bb3..7da3d26c9dd8 100644 --- a/system/Database/Postgre/Connection.php +++ b/system/Database/Postgre/Connection.php @@ -332,7 +332,7 @@ protected function _fieldData(string $table): array /** * Returns an array of objects with index data * - * @return list + * @return array * * @throws DatabaseException */ @@ -371,7 +371,7 @@ protected function _indexData(string $table): array /** * Returns an array of objects with Foreign key data * - * @return list + * @return array * * @throws DatabaseException */ diff --git a/system/Database/SQLSRV/Connection.php b/system/Database/SQLSRV/Connection.php index bc3e036078c9..77a1ab34bcf3 100755 --- a/system/Database/SQLSRV/Connection.php +++ b/system/Database/SQLSRV/Connection.php @@ -231,7 +231,7 @@ protected function _listColumns(string $table = ''): string /** * Returns an array of objects with index data * - * @return list + * @return array * * @throws DatabaseException */ @@ -269,7 +269,7 @@ protected function _indexData(string $table): array * Returns an array of objects with Foreign key data * referenced_object_id parent_object_id * - * @return list + * @return array * * @throws DatabaseException */ diff --git a/system/Database/SQLite3/Connection.php b/system/Database/SQLite3/Connection.php index be4ebc25f0ca..a7ac179dadca 100644 --- a/system/Database/SQLite3/Connection.php +++ b/system/Database/SQLite3/Connection.php @@ -286,7 +286,7 @@ protected function _fieldData(string $table): array /** * Returns an array of objects with index data * - * @return list + * @return array * * @throws DatabaseException */ @@ -343,7 +343,7 @@ protected function _indexData(string $table): array /** * Returns an array of objects with Foreign key data * - * @return list + * @return array */ protected function _foreignKeyData(string $table): array { diff --git a/system/Database/SQLite3/Table.php b/system/Database/SQLite3/Table.php index 47e6d1a7097e..44757a3d57ca 100644 --- a/system/Database/SQLite3/Table.php +++ b/system/Database/SQLite3/Table.php @@ -280,7 +280,7 @@ public function addForeignKey(array $foreignKeys) /** * Creates the new table based on our current fields. * - * @return mixed + * @return bool */ protected function createTable() { @@ -449,9 +449,9 @@ private function isNumericType(string $type): bool * Converts keys retrieved from the database to * the format needed to create later. * - * @param mixed $keys + * @param array $keys * - * @return mixed + * @return array */ protected function formatKeys($keys) { From bf3677c6c86bc81b319a346750005e75ac1562a1 Mon Sep 17 00:00:00 2001 From: kenjis Date: Thu, 4 Apr 2024 07:02:36 +0900 Subject: [PATCH 52/65] refactor: remove unneeded code --- system/Database/SQLite3/Table.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/system/Database/SQLite3/Table.php b/system/Database/SQLite3/Table.php index 44757a3d57ca..bd2056d581d9 100644 --- a/system/Database/SQLite3/Table.php +++ b/system/Database/SQLite3/Table.php @@ -455,10 +455,6 @@ private function isNumericType(string $type): bool */ protected function formatKeys($keys) { - if (! is_array($keys)) { - return $keys; - } - $return = []; foreach ($keys as $name => $key) { From f1d6b1be358fd0b7a389dd11a25e1377e3d884fb Mon Sep 17 00:00:00 2001 From: kenjis Date: Thu, 4 Apr 2024 10:11:00 +0900 Subject: [PATCH 53/65] style: fix indentations For consistency. --- app/.htaccess | 4 ++-- public/.htaccess | 2 +- tests/.htaccess | 4 ++-- user_guide_src/.htaccess | 4 ++-- writable/.htaccess | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/.htaccess b/app/.htaccess index f24db0accc73..3462048add78 100644 --- a/app/.htaccess +++ b/app/.htaccess @@ -1,6 +1,6 @@ - Require all denied + Require all denied - Deny from all + Deny from all diff --git a/public/.htaccess b/public/.htaccess index dbed322fdc1e..abac3cb6b949 100644 --- a/public/.htaccess +++ b/public/.htaccess @@ -45,5 +45,5 @@ Options -Indexes # Disable server signature start - ServerSignature Off +ServerSignature Off # Disable server signature end diff --git a/tests/.htaccess b/tests/.htaccess index f24db0accc73..3462048add78 100644 --- a/tests/.htaccess +++ b/tests/.htaccess @@ -1,6 +1,6 @@ - Require all denied + Require all denied - Deny from all + Deny from all diff --git a/user_guide_src/.htaccess b/user_guide_src/.htaccess index f24db0accc73..3462048add78 100644 --- a/user_guide_src/.htaccess +++ b/user_guide_src/.htaccess @@ -1,6 +1,6 @@ - Require all denied + Require all denied - Deny from all + Deny from all diff --git a/writable/.htaccess b/writable/.htaccess index f24db0accc73..3462048add78 100755 --- a/writable/.htaccess +++ b/writable/.htaccess @@ -1,6 +1,6 @@ - Require all denied + Require all denied - Deny from all + Deny from all From 78afe57e5019a10d1c173a5f799d73edae074d25 Mon Sep 17 00:00:00 2001 From: kenjis Date: Thu, 4 Apr 2024 10:15:40 +0900 Subject: [PATCH 54/65] docs: add escaping to variables It is bad practice to output variables without escaping in HTML. --- user_guide_src/source/outgoing/alternative_php.rst | 4 ++-- user_guide_src/source/outgoing/alternative_php/001.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/user_guide_src/source/outgoing/alternative_php.rst b/user_guide_src/source/outgoing/alternative_php.rst index b4a2b10257e5..742c59d404c8 100644 --- a/user_guide_src/source/outgoing/alternative_php.rst +++ b/user_guide_src/source/outgoing/alternative_php.rst @@ -15,11 +15,11 @@ Alternative Echos Normally to echo, or print out a variable you would do this:: - + With the alternative syntax you can instead do it this way:: - + Alternative Control Structures ============================== diff --git a/user_guide_src/source/outgoing/alternative_php/001.php b/user_guide_src/source/outgoing/alternative_php/001.php index 9158fe27dccc..b9c543ef4211 100644 --- a/user_guide_src/source/outgoing/alternative_php/001.php +++ b/user_guide_src/source/outgoing/alternative_php/001.php @@ -2,7 +2,7 @@ -

  • +
  • From ec84410d4cf6bb8f45b9c8f4fb3a1b1f9632616d Mon Sep 17 00:00:00 2001 From: kenjis Date: Thu, 4 Apr 2024 10:36:22 +0900 Subject: [PATCH 55/65] docs: add "// ..." to sample code To make it clear that only a part of the code is shown. --- user_guide_src/source/models/model/003.php | 2 ++ user_guide_src/source/models/model/004.php | 2 ++ user_guide_src/source/models/model/027.php | 2 ++ user_guide_src/source/models/model/034.php | 2 ++ user_guide_src/source/models/model/038.php | 2 ++ user_guide_src/source/models/model/040.php | 2 ++ user_guide_src/source/models/model/041.php | 2 ++ user_guide_src/source/models/model/050.php | 2 ++ user_guide_src/source/models/model/051.php | 4 ++++ user_guide_src/source/models/model/052.php | 4 ++++ user_guide_src/source/models/model/054.php | 2 ++ 11 files changed, 26 insertions(+) diff --git a/user_guide_src/source/models/model/003.php b/user_guide_src/source/models/model/003.php index 1b70cea84a84..c2ef0d257efc 100644 --- a/user_guide_src/source/models/model/003.php +++ b/user_guide_src/source/models/model/003.php @@ -6,6 +6,8 @@ class UserModel extends UserAuthModel { + // ... + /** * Called during initialization. Appends * our custom field to the module's model. diff --git a/user_guide_src/source/models/model/004.php b/user_guide_src/source/models/model/004.php index 1ae028acbf7a..1005e6f7f2e7 100644 --- a/user_guide_src/source/models/model/004.php +++ b/user_guide_src/source/models/model/004.php @@ -7,4 +7,6 @@ class UserModel extends Model { protected $DBGroup = 'group_name'; + + // ... } diff --git a/user_guide_src/source/models/model/027.php b/user_guide_src/source/models/model/027.php index 3a039df49465..86a5a5f4c71a 100644 --- a/user_guide_src/source/models/model/027.php +++ b/user_guide_src/source/models/model/027.php @@ -6,6 +6,8 @@ class UserModel extends Model { + // ... + protected $validationRules = [ 'username' => 'required|max_length[30]|alpha_numeric_space|min_length[3]', 'email' => 'required|max_length[254]|valid_email|is_unique[users.email]', diff --git a/user_guide_src/source/models/model/034.php b/user_guide_src/source/models/model/034.php index b18544ee555e..ac8989263ee2 100644 --- a/user_guide_src/source/models/model/034.php +++ b/user_guide_src/source/models/model/034.php @@ -6,5 +6,7 @@ class UserModel extends Model { + // ... + protected $validationRules = 'users'; } diff --git a/user_guide_src/source/models/model/038.php b/user_guide_src/source/models/model/038.php index 5ed40baae0eb..2e61e1fc94f8 100644 --- a/user_guide_src/source/models/model/038.php +++ b/user_guide_src/source/models/model/038.php @@ -6,6 +6,8 @@ class MyModel extends Model { + // ... + protected $validationRules = [ 'id' => 'max_length[19]|is_natural_no_zero', 'email' => 'required|max_length[254]|valid_email|is_unique[users.email,id,{id}]', diff --git a/user_guide_src/source/models/model/040.php b/user_guide_src/source/models/model/040.php index df4d628d1c9d..94de0d042171 100644 --- a/user_guide_src/source/models/model/040.php +++ b/user_guide_src/source/models/model/040.php @@ -6,6 +6,8 @@ class MyModel extends Model { + // ... + protected $validationRules = [ 'id' => 'max_length[19]|is_natural_no_zero', 'email' => 'required|max_length[254]|valid_email|is_unique[users.email,id,4]', diff --git a/user_guide_src/source/models/model/041.php b/user_guide_src/source/models/model/041.php index 94ad48e0effb..d5b73ec3869e 100644 --- a/user_guide_src/source/models/model/041.php +++ b/user_guide_src/source/models/model/041.php @@ -6,5 +6,7 @@ class MyModel extends Model { + // ... + protected $allowedFields = ['name', 'email', 'address']; } diff --git a/user_guide_src/source/models/model/050.php b/user_guide_src/source/models/model/050.php index 9f6d334609f1..5f5a4fdb7278 100644 --- a/user_guide_src/source/models/model/050.php +++ b/user_guide_src/source/models/model/050.php @@ -6,6 +6,8 @@ class MyModel extends Model { + // ... + protected function hashPassword(array $data) { if (! isset($data['data']['password'])) { diff --git a/user_guide_src/source/models/model/051.php b/user_guide_src/source/models/model/051.php index 0ca5ee317a37..28229e22edcb 100644 --- a/user_guide_src/source/models/model/051.php +++ b/user_guide_src/source/models/model/051.php @@ -6,6 +6,10 @@ class MyModel extends Model { + // ... + protected $beforeInsert = ['hashPassword']; protected $beforeUpdate = ['hashPassword']; + + // ... } diff --git a/user_guide_src/source/models/model/052.php b/user_guide_src/source/models/model/052.php index 292b752bdc3c..d049e938c713 100644 --- a/user_guide_src/source/models/model/052.php +++ b/user_guide_src/source/models/model/052.php @@ -6,5 +6,9 @@ class MyModel extends Model { + // ... + protected $allowCallbacks = false; + + // ... } diff --git a/user_guide_src/source/models/model/054.php b/user_guide_src/source/models/model/054.php index 6b72095f087f..c67b4828acfd 100644 --- a/user_guide_src/source/models/model/054.php +++ b/user_guide_src/source/models/model/054.php @@ -6,6 +6,8 @@ class MyModel extends Model { + // ... + protected $beforeFind = ['checkCache']; // ... From e2ac94ab035c674d7a805205dec5c5ab24acab62 Mon Sep 17 00:00:00 2001 From: kenjis Date: Thu, 4 Apr 2024 10:51:50 +0900 Subject: [PATCH 56/65] docs: update @var types --- phpstan-baseline.php | 120 ------------------------------------------- system/BaseModel.php | 24 ++++----- 2 files changed, 12 insertions(+), 132 deletions(-) diff --git a/phpstan-baseline.php b/phpstan-baseline.php index 83a947a76f94..9e0f8ebed3d0 100644 --- a/phpstan-baseline.php +++ b/phpstan-baseline.php @@ -261,66 +261,6 @@ 'count' => 1, 'path' => __DIR__ . '/system/BaseModel.php', ]; -$ignoreErrors[] = [ - 'message' => '#^Property CodeIgniter\\\\BaseModel\\:\\:\\$afterDelete type has no value type specified in iterable type array\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/BaseModel.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Property CodeIgniter\\\\BaseModel\\:\\:\\$afterFind type has no value type specified in iterable type array\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/BaseModel.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Property CodeIgniter\\\\BaseModel\\:\\:\\$afterInsert type has no value type specified in iterable type array\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/BaseModel.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Property CodeIgniter\\\\BaseModel\\:\\:\\$afterInsertBatch type has no value type specified in iterable type array\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/BaseModel.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Property CodeIgniter\\\\BaseModel\\:\\:\\$afterUpdate type has no value type specified in iterable type array\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/BaseModel.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Property CodeIgniter\\\\BaseModel\\:\\:\\$afterUpdateBatch type has no value type specified in iterable type array\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/BaseModel.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Property CodeIgniter\\\\BaseModel\\:\\:\\$beforeDelete type has no value type specified in iterable type array\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/BaseModel.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Property CodeIgniter\\\\BaseModel\\:\\:\\$beforeFind type has no value type specified in iterable type array\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/BaseModel.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Property CodeIgniter\\\\BaseModel\\:\\:\\$beforeInsert type has no value type specified in iterable type array\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/BaseModel.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Property CodeIgniter\\\\BaseModel\\:\\:\\$beforeInsertBatch type has no value type specified in iterable type array\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/BaseModel.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Property CodeIgniter\\\\BaseModel\\:\\:\\$beforeUpdate type has no value type specified in iterable type array\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/BaseModel.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Property CodeIgniter\\\\BaseModel\\:\\:\\$beforeUpdateBatch type has no value type specified in iterable type array\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/BaseModel.php', -]; $ignoreErrors[] = [ 'message' => '#^Strict comparison using \\!\\=\\= between mixed and null will always evaluate to true\\.$#', 'count' => 1, @@ -11281,71 +11221,11 @@ 'count' => 1, 'path' => __DIR__ . '/tests/_support/Models/EventModel.php', ]; -$ignoreErrors[] = [ - 'message' => '#^Property Tests\\\\Support\\\\Models\\\\EventModel\\:\\:\\$afterDelete type has no value type specified in iterable type array\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/tests/_support/Models/EventModel.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Property Tests\\\\Support\\\\Models\\\\EventModel\\:\\:\\$afterFind type has no value type specified in iterable type array\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/tests/_support/Models/EventModel.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Property Tests\\\\Support\\\\Models\\\\EventModel\\:\\:\\$afterInsert type has no value type specified in iterable type array\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/tests/_support/Models/EventModel.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Property Tests\\\\Support\\\\Models\\\\EventModel\\:\\:\\$afterInsertBatch type has no value type specified in iterable type array\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/tests/_support/Models/EventModel.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Property Tests\\\\Support\\\\Models\\\\EventModel\\:\\:\\$afterUpdate type has no value type specified in iterable type array\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/tests/_support/Models/EventModel.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Property Tests\\\\Support\\\\Models\\\\EventModel\\:\\:\\$afterUpdateBatch type has no value type specified in iterable type array\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/tests/_support/Models/EventModel.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Property Tests\\\\Support\\\\Models\\\\EventModel\\:\\:\\$beforeDelete type has no value type specified in iterable type array\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/tests/_support/Models/EventModel.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Property Tests\\\\Support\\\\Models\\\\EventModel\\:\\:\\$beforeFind type has no value type specified in iterable type array\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/tests/_support/Models/EventModel.php', -]; $ignoreErrors[] = [ 'message' => '#^Property Tests\\\\Support\\\\Models\\\\EventModel\\:\\:\\$beforeFindReturnData has no type specified\\.$#', 'count' => 1, 'path' => __DIR__ . '/tests/_support/Models/EventModel.php', ]; -$ignoreErrors[] = [ - 'message' => '#^Property Tests\\\\Support\\\\Models\\\\EventModel\\:\\:\\$beforeInsert type has no value type specified in iterable type array\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/tests/_support/Models/EventModel.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Property Tests\\\\Support\\\\Models\\\\EventModel\\:\\:\\$beforeInsertBatch type has no value type specified in iterable type array\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/tests/_support/Models/EventModel.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Property Tests\\\\Support\\\\Models\\\\EventModel\\:\\:\\$beforeUpdate type has no value type specified in iterable type array\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/tests/_support/Models/EventModel.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Property Tests\\\\Support\\\\Models\\\\EventModel\\:\\:\\$beforeUpdateBatch type has no value type specified in iterable type array\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/tests/_support/Models/EventModel.php', -]; $ignoreErrors[] = [ 'message' => '#^Property Tests\\\\Support\\\\Models\\\\EventModel\\:\\:\\$eventData has no type specified\\.$#', 'count' => 1, diff --git a/system/BaseModel.php b/system/BaseModel.php index 9b085efa8400..cd2bdd6beb22 100644 --- a/system/BaseModel.php +++ b/system/BaseModel.php @@ -246,84 +246,84 @@ abstract class BaseModel /** * Callbacks for beforeInsert * - * @var array + * @var list */ protected $beforeInsert = []; /** * Callbacks for afterInsert * - * @var array + * @var list */ protected $afterInsert = []; /** * Callbacks for beforeUpdate * - * @var array + * @var list */ protected $beforeUpdate = []; /** * Callbacks for afterUpdate * - * @var array + * @var list */ protected $afterUpdate = []; /** * Callbacks for beforeInsertBatch * - * @var array + * @var list */ protected $beforeInsertBatch = []; /** * Callbacks for afterInsertBatch * - * @var array + * @var list */ protected $afterInsertBatch = []; /** * Callbacks for beforeUpdateBatch * - * @var array + * @var list */ protected $beforeUpdateBatch = []; /** * Callbacks for afterUpdateBatch * - * @var array + * @var list */ protected $afterUpdateBatch = []; /** * Callbacks for beforeFind * - * @var array + * @var list */ protected $beforeFind = []; /** * Callbacks for afterFind * - * @var array + * @var list */ protected $afterFind = []; /** * Callbacks for beforeDelete * - * @var array + * @var list */ protected $beforeDelete = []; /** * Callbacks for afterDelete * - * @var array + * @var list */ protected $afterDelete = []; From 0d84ce83a98bdbe5504ea58ba047259607917285 Mon Sep 17 00:00:00 2001 From: Pebryan Ibrahim <33095305+Pebryan354@users.noreply.github.com> Date: Thu, 4 Apr 2024 10:20:22 +0700 Subject: [PATCH 57/65] docs: update return type --- system/Database/BaseResult.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/Database/BaseResult.php b/system/Database/BaseResult.php index 8a624029b3a2..4ef73d6d210a 100644 --- a/system/Database/BaseResult.php +++ b/system/Database/BaseResult.php @@ -260,7 +260,7 @@ public function getResultObject(): array * @param string $type The type of result object. 'array', 'object' or class name. * @phpstan-param class-string|'array'|'object' $type * - * @return array|object|stdClass|null + * @return array|object|stdClass|null|string * @phpstan-return ($type is 'object' ? stdClass|null : ($type is 'array' ? array|null : T|null)) */ public function getRow($n = 0, string $type = 'object') From 1455e8dac6d75c05f595af958065608e0d48e0e0 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Thu, 4 Apr 2024 15:38:56 +0700 Subject: [PATCH 58/65] remove !== "0" check --- system/CLI/CLI.php | 2 +- system/Config/DotEnv.php | 2 +- system/HTTP/DownloadResponse.php | 2 +- system/Helpers/form_helper.php | 2 +- system/Helpers/html_helper.php | 2 +- system/Helpers/url_helper.php | 2 +- system/Router/RouteCollection.php | 2 +- system/Session/Handlers/FileHandler.php | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/system/CLI/CLI.php b/system/CLI/CLI.php index 12e95ed37388..5bd8543896ef 100644 --- a/system/CLI/CLI.php +++ b/system/CLI/CLI.php @@ -262,7 +262,7 @@ public static function prompt(string $field, $options = null, $validation = null $default = $options[0]; } - static::fwrite(STDOUT, $field . (trim($field) !== '' && trim($field) !== '0' ? ' ' : '') . $extraOutput . ': '); + static::fwrite(STDOUT, $field . (trim($field) !== '' ? ' ' : '') . $extraOutput . ': '); // Read the input from keyboard. $input = trim(static::input()) ?: $default; diff --git a/system/Config/DotEnv.php b/system/Config/DotEnv.php index 01bbb1ba6467..f8e757e9eab5 100644 --- a/system/Config/DotEnv.php +++ b/system/Config/DotEnv.php @@ -140,7 +140,7 @@ public function normaliseVariable(string $name, string $value = ''): array */ protected function sanitizeValue(string $value): string { - if ($value === '' || $value === '0') { + if ($value === '') { return $value; } diff --git a/system/HTTP/DownloadResponse.php b/system/HTTP/DownloadResponse.php index df9eef8c147f..d815a9af6e03 100644 --- a/system/HTTP/DownloadResponse.php +++ b/system/HTTP/DownloadResponse.php @@ -194,7 +194,7 @@ private function getContentDisposition(): string $result = sprintf('attachment; filename="%s"', $downloadFilename); - if ($utf8Filename !== '' && $utf8Filename !== '0') { + if ($utf8Filename !== '') { $result .= '; filename*=UTF-8\'\'' . rawurlencode($utf8Filename); } diff --git a/system/Helpers/form_helper.php b/system/Helpers/form_helper.php index e21b27787937..5f62cca83d6d 100644 --- a/system/Helpers/form_helper.php +++ b/system/Helpers/form_helper.php @@ -29,7 +29,7 @@ function form_open(string $action = '', $attributes = [], array $hidden = []): string { // If no action is provided then set to the current url - if ($action === '' || $action === '0') { + if ($action === '') { $action = current_url(true); } // If an action is not a full URL then turn it into one elseif (strpos($action, '://') === false) { diff --git a/system/Helpers/html_helper.php b/system/Helpers/html_helper.php index 9fe4334f413b..6d9a51356af6 100755 --- a/system/Helpers/html_helper.php +++ b/system/Helpers/html_helper.php @@ -195,7 +195,7 @@ function doctype(string $type = 'html5'): string function script_tag($src = '', bool $indexPage = false): string { $cspNonce = csp_script_nonce(); - $cspNonce = $cspNonce !== '' && $cspNonce !== '0' ? ' ' . $cspNonce : $cspNonce; + $cspNonce = $cspNonce !== '' ? ' ' . $cspNonce : $cspNonce; $script = ' $src]; diff --git a/system/Helpers/url_helper.php b/system/Helpers/url_helper.php index d1ff870c2f6e..988d7de88a2f 100644 --- a/system/Helpers/url_helper.php +++ b/system/Helpers/url_helper.php @@ -318,7 +318,7 @@ function safe_mailto(string $email, string $title = '', $attributes = ''): strin // improve obfuscation by eliminating newlines & whitespace $cspNonce = csp_script_nonce(); - $cspNonce = $cspNonce !== '' && $cspNonce !== '0' ? ' ' . $cspNonce : $cspNonce; + $cspNonce = $cspNonce !== '' ? ' ' . $cspNonce : $cspNonce; $output = '' . 'var l=new Array();'; diff --git a/system/Router/RouteCollection.php b/system/Router/RouteCollection.php index 4bac4dd4403b..117f3a218da6 100644 --- a/system/Router/RouteCollection.php +++ b/system/Router/RouteCollection.php @@ -767,7 +767,7 @@ public function group(string $name, ...$params) // To register a route, we'll set a flag so that our router // will see the group name. // If the group name is empty, we go on using the previously built group name. - $this->group = $name !== '' && $name !== '0' ? trim($oldGroup . '/' . $name, '/') : $oldGroup; + $this->group = $name !== '' ? trim($oldGroup . '/' . $name, '/') : $oldGroup; $callback = array_pop($params); diff --git a/system/Session/Handlers/FileHandler.php b/system/Session/Handlers/FileHandler.php index cad7283f8073..62513d399c4a 100644 --- a/system/Session/Handlers/FileHandler.php +++ b/system/Session/Handlers/FileHandler.php @@ -73,7 +73,7 @@ public function __construct(SessionConfig $config, string $ipAddress) } else { $sessionPath = rtrim(ini_get('session.save_path'), '/\\'); - if ($sessionPath === '' || $sessionPath === '0') { + if ($sessionPath === '') { $sessionPath = WRITEPATH . 'session'; } From e2ccc919d2748611da893eceddea6db5589b1291 Mon Sep 17 00:00:00 2001 From: Pebryan Ibrahim <33095305+Pebryan354@users.noreply.github.com> Date: Fri, 5 Apr 2024 11:02:07 +0700 Subject: [PATCH 59/65] add: return type int & float --- system/Database/BaseResult.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/Database/BaseResult.php b/system/Database/BaseResult.php index 4ef73d6d210a..44c35aa62041 100644 --- a/system/Database/BaseResult.php +++ b/system/Database/BaseResult.php @@ -260,7 +260,7 @@ public function getResultObject(): array * @param string $type The type of result object. 'array', 'object' or class name. * @phpstan-param class-string|'array'|'object' $type * - * @return array|object|stdClass|null|string + * @return array|float|int|object|stdClass|string|null * @phpstan-return ($type is 'object' ? stdClass|null : ($type is 'array' ? array|null : T|null)) */ public function getRow($n = 0, string $type = 'object') From 716d3628ba74f20aa7c8b9b0f73592d2251b3471 Mon Sep 17 00:00:00 2001 From: Pebryan Ibrahim <33095305+Pebryan354@users.noreply.github.com> Date: Fri, 5 Apr 2024 14:48:22 +0700 Subject: [PATCH 60/65] Update system/Database/BaseResult.php Co-authored-by: kenjis --- system/Database/BaseResult.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/Database/BaseResult.php b/system/Database/BaseResult.php index 44c35aa62041..8720b5fe111c 100644 --- a/system/Database/BaseResult.php +++ b/system/Database/BaseResult.php @@ -261,7 +261,7 @@ public function getResultObject(): array * @phpstan-param class-string|'array'|'object' $type * * @return array|float|int|object|stdClass|string|null - * @phpstan-return ($type is 'object' ? stdClass|null : ($type is 'array' ? array|null : T|null)) + * @phpstan-return ($n is string ? float|int|string|null : ($type is 'object' ? stdClass|null : ($type is 'array' ? array|null : T|null))) */ public function getRow($n = 0, string $type = 'object') { From d6347e602e2db6d2810aedccb6557c1de56b68c9 Mon Sep 17 00:00:00 2001 From: Pebryan Ibrahim <33095305+Pebryan354@users.noreply.github.com> Date: Fri, 5 Apr 2024 14:50:29 +0700 Subject: [PATCH 61/65] apply suggestion --- system/Database/ResultInterface.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system/Database/ResultInterface.php b/system/Database/ResultInterface.php index ea5f0de2800f..2b1deb98c6b6 100644 --- a/system/Database/ResultInterface.php +++ b/system/Database/ResultInterface.php @@ -63,8 +63,8 @@ public function getResultObject(): array; * @param string $type The type of result object. 'array', 'object' or class name. * @phpstan-param class-string|'array'|'object' $type * - * @return array|object|stdClass|null - * @phpstan-return ($type is 'object' ? stdClass|null : ($type is 'array' ? array|null : T|null)) + * @return array|float|int|object|stdClass|string|null + * @phpstan-return ($n is string ? float|int|string|null : ($type is 'object' ? stdClass|null : ($type is 'array' ? array|null : T|null))) */ public function getRow($n = 0, string $type = 'object'); From 88b6fb926bc807ac811e8a51a1d541671b0aaba8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 5 Apr 2024 15:59:12 +0000 Subject: [PATCH 62/65] chore(deps-dev): update rector/rector requirement from 1.0.3 to 1.0.4 Updates the requirements on [rector/rector](https://github.com/rectorphp/rector) to permit the latest version. - [Release notes](https://github.com/rectorphp/rector/releases) - [Commits](https://github.com/rectorphp/rector/compare/1.0.3...1.0.4) --- updated-dependencies: - dependency-name: rector/rector dependency-type: direct:development ... Signed-off-by: dependabot[bot] --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 4021508a3a87..b5baf427017a 100644 --- a/composer.json +++ b/composer.json @@ -34,7 +34,7 @@ "phpunit/phpcov": "^8.2", "phpunit/phpunit": "^9.1", "predis/predis": "^1.1 || ^2.0", - "rector/rector": "1.0.3", + "rector/rector": "1.0.4", "vimeo/psalm": "^5.0" }, "replace": { From f9ed5a6fc88527745675ae7b3ae19a27049527dc Mon Sep 17 00:00:00 2001 From: kenjis Date: Sat, 6 Apr 2024 11:11:59 +0900 Subject: [PATCH 63/65] docs: fix @param types --- system/HTTP/Files/UploadedFile.php | 30 +++++++++------------ system/HTTP/Files/UploadedFileInterface.php | 17 ++++++------ 2 files changed, 20 insertions(+), 27 deletions(-) diff --git a/system/HTTP/Files/UploadedFile.php b/system/HTTP/Files/UploadedFile.php index 70ece748d834..e0725e7c90d5 100644 --- a/system/HTTP/Files/UploadedFile.php +++ b/system/HTTP/Files/UploadedFile.php @@ -15,8 +15,6 @@ use CodeIgniter\HTTP\Exceptions\HTTPException; use Config\Mimes; use Exception; -use InvalidArgumentException; -use RuntimeException; /** * Value object representing a single file uploaded through an @@ -80,12 +78,12 @@ class UploadedFile extends File implements UploadedFileInterface /** * Accepts the file information as would be filled in from the $_FILES array. * - * @param string $path The temporary location of the uploaded file. - * @param string $originalName The client-provided filename. - * @param string $mimeType The type of file as provided by PHP - * @param int $size The size of the file, in bytes - * @param int $error The error constant of the upload (one of PHP's UPLOADERRXXX constants) - * @param string $clientPath The webkit relative path of the uploaded file. + * @param string $path The temporary location of the uploaded file. + * @param string $originalName The client-provided filename. + * @param string|null $mimeType The type of file as provided by PHP + * @param int|null $size The size of the file, in bytes + * @param int|null $error The error constant of the upload (one of PHP's UPLOADERRXXX constants) + * @param string|null $clientPath The webkit relative path of the uploaded file. */ public function __construct(string $path, string $originalName, ?string $mimeType = null, ?int $size = null, ?int $error = null, ?string $clientPath = null) { @@ -122,16 +120,12 @@ public function __construct(string $path, string $originalName, ?string $mimeTyp * @see http://php.net/is_uploaded_file * @see http://php.net/move_uploaded_file * - * @param string $targetPath Path to which to move the uploaded file. - * @param string $name the name to rename the file to. - * @param bool $overwrite State for indicating whether to overwrite the previously generated file with the same - * name or not. + * @param string $targetPath Path to which to move the uploaded file. + * @param string|null $name the name to rename the file to. + * @param bool $overwrite State for indicating whether to overwrite the previously generated file with the same + * name or not. * * @return bool - * - * @throws InvalidArgumentException if the $path specified is invalid. - * @throws RuntimeException on any error during the move operation. - * @throws RuntimeException on the second or subsequent call to the method. */ public function move(string $targetPath, ?string $name = null, bool $overwrite = false) { @@ -345,8 +339,8 @@ public function isValid(): bool * By default, upload files are saved in writable/uploads directory. The YYYYMMDD folder * and random file name will be created. * - * @param string $folderName the folder name to writable/uploads directory. - * @param string $fileName the name to rename the file to. + * @param string|null $folderName the folder name to writable/uploads directory. + * @param string|null $fileName the name to rename the file to. * * @return string file full path */ diff --git a/system/HTTP/Files/UploadedFileInterface.php b/system/HTTP/Files/UploadedFileInterface.php index 170f81c2de9e..ef2073f2b876 100644 --- a/system/HTTP/Files/UploadedFileInterface.php +++ b/system/HTTP/Files/UploadedFileInterface.php @@ -26,12 +26,12 @@ interface UploadedFileInterface /** * Accepts the file information as would be filled in from the $_FILES array. * - * @param string $path The temporary location of the uploaded file. - * @param string $originalName The client-provided filename. - * @param string $mimeType The type of file as provided by PHP - * @param int $size The size of the file, in bytes - * @param int $error The error constant of the upload (one of PHP's UPLOADERRXXX constants) - * @param string $clientPath The webkit relative path of the uploaded file. + * @param string $path The temporary location of the uploaded file. + * @param string $originalName The client-provided filename. + * @param string|null $mimeType The type of file as provided by PHP + * @param int|null $size The size of the file, in bytes + * @param int|null $error The error constant of the upload (one of PHP's UPLOADERRXXX constants) + * @param string|null $clientPath The webkit relative path of the uploaded file. */ public function __construct(string $path, string $originalName, ?string $mimeType = null, ?int $size = null, ?int $error = null, ?string $clientPath = null); @@ -57,13 +57,12 @@ public function __construct(string $path, string $originalName, ?string $mimeTyp * @see http://php.net/is_uploaded_file * @see http://php.net/move_uploaded_file * - * @param string $targetPath Path to which to move the uploaded file. - * @param string $name the name to rename the file to. + * @param string $targetPath Path to which to move the uploaded file. + * @param string|null $name the name to rename the file to. * * @return bool * * @throws InvalidArgumentException if the $path specified is invalid. - * @throws RuntimeException on any error during the move operation. * @throws RuntimeException on the second or subsequent call to the method. */ public function move(string $targetPath, ?string $name = null); From a1e5d645c6fcdc7aa857f73e5394d25011c46730 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sat, 6 Apr 2024 11:21:18 +0900 Subject: [PATCH 64/65] docs: make CI3 notation consistent --- user_guide_src/source/incoming/controllers.rst | 2 +- user_guide_src/source/incoming/routing.rst | 2 +- user_guide_src/source/installation/index.rst | 2 +- user_guide_src/source/installation/upgrade_4xx.rst | 4 ++-- .../source/installation/upgrade_configuration.rst | 4 ++-- user_guide_src/source/installation/upgrade_controllers.rst | 4 ++-- user_guide_src/source/installation/upgrade_database.rst | 4 ++-- user_guide_src/source/installation/upgrade_emails.rst | 4 ++-- user_guide_src/source/installation/upgrade_encryption.rst | 4 ++-- user_guide_src/source/installation/upgrade_file_upload.rst | 4 ++-- user_guide_src/source/installation/upgrade_html_tables.rst | 4 ++-- user_guide_src/source/installation/upgrade_images.rst | 4 ++-- user_guide_src/source/installation/upgrade_localization.rst | 4 ++-- user_guide_src/source/installation/upgrade_migrations.rst | 4 ++-- user_guide_src/source/installation/upgrade_models.rst | 4 ++-- user_guide_src/source/installation/upgrade_pagination.rst | 4 ++-- user_guide_src/source/installation/upgrade_responses.rst | 4 ++-- user_guide_src/source/installation/upgrade_routing.rst | 4 ++-- user_guide_src/source/installation/upgrade_security.rst | 4 ++-- user_guide_src/source/installation/upgrade_sessions.rst | 4 ++-- user_guide_src/source/installation/upgrade_validations.rst | 4 ++-- user_guide_src/source/installation/upgrade_view_parser.rst | 4 ++-- user_guide_src/source/installation/upgrade_views.rst | 4 ++-- user_guide_src/source/libraries/uploaded_files.rst | 6 +++--- 24 files changed, 46 insertions(+), 46 deletions(-) diff --git a/user_guide_src/source/incoming/controllers.rst b/user_guide_src/source/incoming/controllers.rst index b1aa0e65a9a5..6c5984615fb6 100644 --- a/user_guide_src/source/incoming/controllers.rst +++ b/user_guide_src/source/incoming/controllers.rst @@ -185,7 +185,7 @@ Auto Routing (Improved) Since v4.2.0, the new more secure Auto Routing has been introduced. .. note:: If you are familiar with Auto Routing, which was enabled by default - from CodeIgniter 3 through 4.1.x, you can see the differences in + from CodeIgniter 3.x through 4.1.x, you can see the differences in :ref:`ChangeLog v4.2.0 `. This section describes the functionality of the new auto-routing. diff --git a/user_guide_src/source/incoming/routing.rst b/user_guide_src/source/incoming/routing.rst index f05ac3298467..8557b8f7fa2c 100644 --- a/user_guide_src/source/incoming/routing.rst +++ b/user_guide_src/source/incoming/routing.rst @@ -705,7 +705,7 @@ Auto Routing (Improved) Since v4.2.0, the new more secure Auto Routing has been introduced. .. note:: If you are familiar with Auto Routing, which was enabled by default - from CodeIgniter 3 through 4.1.x, you can see the differences in + from CodeIgniter 3.x through 4.1.x, you can see the differences in :ref:`ChangeLog v4.2.0 `. When no defined route is found that matches the URI, the system will attempt to match that URI against the controllers and methods when Auto Routing is enabled. diff --git a/user_guide_src/source/installation/index.rst b/user_guide_src/source/installation/index.rst index 8b71ea2833b0..0eab10b25b76 100644 --- a/user_guide_src/source/installation/index.rst +++ b/user_guide_src/source/installation/index.rst @@ -7,7 +7,7 @@ or using `Composer `_. Which is right for you? - We recommend the Composer installation because it keeps CodeIgniter up to date easily. -- If you would like the simple "download & go" install that CodeIgniter3 +- If you would like the simple "download & go" install that CodeIgniter 3 is known for, choose the manual installation. However you choose to install and run CodeIgniter4, the latest diff --git a/user_guide_src/source/installation/upgrade_4xx.rst b/user_guide_src/source/installation/upgrade_4xx.rst index 97a7a567a79d..45205c4ec2d8 100644 --- a/user_guide_src/source/installation/upgrade_4xx.rst +++ b/user_guide_src/source/installation/upgrade_4xx.rst @@ -149,8 +149,8 @@ Helpers - `String Helper `_ functions in CI3 are included in :doc:`../helpers/text_helper` in CI4. - In CI4, ``redirect()`` is completely changed from CI3's. - - `redirect() Documentation CodeIgniter 3.X `_ - - `redirect() Documentation CodeIgniter 4.X <../general/common_functions.html#redirect>`_ + - `redirect() Documentation CodeIgniter 3.x `_ + - `redirect() Documentation CodeIgniter 4.x <../general/common_functions.html#redirect>`_ - In CI4, :php:func:`redirect()` returns a ``RedirectResponse`` instance instead of redirecting and terminating script execution. You must return it from Controllers or Controller Filters. diff --git a/user_guide_src/source/installation/upgrade_configuration.rst b/user_guide_src/source/installation/upgrade_configuration.rst index 6150b9a01ff6..04aa67c17ba4 100644 --- a/user_guide_src/source/installation/upgrade_configuration.rst +++ b/user_guide_src/source/installation/upgrade_configuration.rst @@ -8,8 +8,8 @@ Upgrade Configuration Documentations ============== -- `Config Documentation CodeIgniter 3.X `_ -- :doc:`Configuration Documentation CodeIgniter 4.X ` +- `Config Documentation CodeIgniter 3.x `_ +- :doc:`Configuration Documentation CodeIgniter 4.x ` What has been changed ===================== diff --git a/user_guide_src/source/installation/upgrade_controllers.rst b/user_guide_src/source/installation/upgrade_controllers.rst index 1a2dc9a9b4ce..20e93d376aa4 100644 --- a/user_guide_src/source/installation/upgrade_controllers.rst +++ b/user_guide_src/source/installation/upgrade_controllers.rst @@ -8,8 +8,8 @@ Upgrade Controllers Documentations ============== -- `Controller Documentation CodeIgniter 3.X `_ -- :doc:`Controller Documentation CodeIgniter 4.X ` +- `Controller Documentation CodeIgniter 3.x `_ +- :doc:`Controller Documentation CodeIgniter 4.x ` What has been changed ===================== diff --git a/user_guide_src/source/installation/upgrade_database.rst b/user_guide_src/source/installation/upgrade_database.rst index 5dc22ddda820..b596ca01d775 100644 --- a/user_guide_src/source/installation/upgrade_database.rst +++ b/user_guide_src/source/installation/upgrade_database.rst @@ -8,8 +8,8 @@ Upgrade Database Documentations ============== -- `Database Reference Documentation CodeIgniter 3.X `_ -- :doc:`Working with Databases Documentation CodeIgniter 4.X ` +- `Database Reference Documentation CodeIgniter 3.x `_ +- :doc:`Working with Databases Documentation CodeIgniter 4.x ` What has been changed ===================== diff --git a/user_guide_src/source/installation/upgrade_emails.rst b/user_guide_src/source/installation/upgrade_emails.rst index 828cc83ce5f7..546debff6add 100644 --- a/user_guide_src/source/installation/upgrade_emails.rst +++ b/user_guide_src/source/installation/upgrade_emails.rst @@ -8,8 +8,8 @@ Upgrade Emails Documentations ============== -- `Email Documentation CodeIgniter 3.X `_ -- :doc:`Email Documentation CodeIgniter 4.X ` +- `Email Documentation CodeIgniter 3.x `_ +- :doc:`Email Documentation CodeIgniter 4.x ` What has been changed ===================== diff --git a/user_guide_src/source/installation/upgrade_encryption.rst b/user_guide_src/source/installation/upgrade_encryption.rst index 247c546582ea..26c137079a93 100644 --- a/user_guide_src/source/installation/upgrade_encryption.rst +++ b/user_guide_src/source/installation/upgrade_encryption.rst @@ -8,8 +8,8 @@ Upgrade Encryption Documentations ************** -- `Encryption Library Documentation CodeIgniter 3.X `_ -- :doc:`Encryption Service Documentation CodeIgniter 4.X ` +- `Encryption Library Documentation CodeIgniter 3.x `_ +- :doc:`Encryption Service Documentation CodeIgniter 4.x ` What has been changed ********************* diff --git a/user_guide_src/source/installation/upgrade_file_upload.rst b/user_guide_src/source/installation/upgrade_file_upload.rst index adb200b9f0f7..8b9c6bc78789 100644 --- a/user_guide_src/source/installation/upgrade_file_upload.rst +++ b/user_guide_src/source/installation/upgrade_file_upload.rst @@ -7,8 +7,8 @@ Upgrade Working with Uploaded Files Documentations ============== -- `File Uploading Class Documentation CodeIgniter 3.X `_ -- :doc:`Working with Uploaded Files Documentation CodeIgniter 4.X ` +- `File Uploading Class Documentation CodeIgniter 3.x `_ +- :doc:`Working with Uploaded Files Documentation CodeIgniter 4.x ` What has been changed ===================== diff --git a/user_guide_src/source/installation/upgrade_html_tables.rst b/user_guide_src/source/installation/upgrade_html_tables.rst index 09059e547abe..a8bd56fe5977 100644 --- a/user_guide_src/source/installation/upgrade_html_tables.rst +++ b/user_guide_src/source/installation/upgrade_html_tables.rst @@ -8,8 +8,8 @@ Upgrade HTML Tables Documentations ============== -- `HTML Table Documentation CodeIgniter 3.X `_ -- :doc:`HTML Table Documentation CodeIgniter 4.X ` +- `HTML Table Documentation CodeIgniter 3.x `_ +- :doc:`HTML Table Documentation CodeIgniter 4.x ` What has been changed ===================== diff --git a/user_guide_src/source/installation/upgrade_images.rst b/user_guide_src/source/installation/upgrade_images.rst index 1e69bb260553..c10d6398a596 100644 --- a/user_guide_src/source/installation/upgrade_images.rst +++ b/user_guide_src/source/installation/upgrade_images.rst @@ -8,8 +8,8 @@ Upgrade Image Manipulation Class Documentations ============== -- `Image Manipulation Class Documentation CodeIgniter 3.X `_ -- :doc:`Image Manipulation Class Documentation CodeIgniter 4.X <../libraries/images>` +- `Image Manipulation Class Documentation CodeIgniter 3.x `_ +- :doc:`Image Manipulation Class Documentation CodeIgniter 4.x <../libraries/images>` What has been changed ===================== diff --git a/user_guide_src/source/installation/upgrade_localization.rst b/user_guide_src/source/installation/upgrade_localization.rst index d962eb462fd4..589dd57ec669 100644 --- a/user_guide_src/source/installation/upgrade_localization.rst +++ b/user_guide_src/source/installation/upgrade_localization.rst @@ -8,8 +8,8 @@ Upgrade Localization Documentations ============== -- `Language Documentation CodeIgniter 3.X `_ -- :doc:`Localization Documentation CodeIgniter 4.X ` +- `Language Documentation CodeIgniter 3.x `_ +- :doc:`Localization Documentation CodeIgniter 4.x ` What has been changed ===================== diff --git a/user_guide_src/source/installation/upgrade_migrations.rst b/user_guide_src/source/installation/upgrade_migrations.rst index 67d826863968..0936c48ad400 100644 --- a/user_guide_src/source/installation/upgrade_migrations.rst +++ b/user_guide_src/source/installation/upgrade_migrations.rst @@ -8,8 +8,8 @@ Upgrade Migrations Documentations ============== -- `Database Migrations Documentation CodeIgniter 3.X `_ -- :doc:`Database Migrations Documentation CodeIgniter 4.X ` +- `Database Migrations Documentation CodeIgniter 3.x `_ +- :doc:`Database Migrations Documentation CodeIgniter 4.x ` What has been changed ===================== diff --git a/user_guide_src/source/installation/upgrade_models.rst b/user_guide_src/source/installation/upgrade_models.rst index bc5fbf08292f..a47cd8fc8ae3 100644 --- a/user_guide_src/source/installation/upgrade_models.rst +++ b/user_guide_src/source/installation/upgrade_models.rst @@ -8,8 +8,8 @@ Upgrade Models Documentations ============== -- `Model Documentation CodeIgniter 3.X `_ -- :doc:`Model Documentation CodeIgniter 4.X ` +- `Model Documentation CodeIgniter 3.x `_ +- :doc:`Model Documentation CodeIgniter 4.x ` What has been changed ===================== diff --git a/user_guide_src/source/installation/upgrade_pagination.rst b/user_guide_src/source/installation/upgrade_pagination.rst index 69e19d57164c..a53a60f52ca2 100644 --- a/user_guide_src/source/installation/upgrade_pagination.rst +++ b/user_guide_src/source/installation/upgrade_pagination.rst @@ -8,8 +8,8 @@ Upgrade Pagination Documentations ============== -- `Pagination Class Documentation CodeIgniter 3.X `_ -- :doc:`Pagination Documentation CodeIgniter 4.X ` +- `Pagination Class Documentation CodeIgniter 3.x `_ +- :doc:`Pagination Documentation CodeIgniter 4.x ` What has been changed ===================== diff --git a/user_guide_src/source/installation/upgrade_responses.rst b/user_guide_src/source/installation/upgrade_responses.rst index 2059647b230d..26ccfc658e3f 100644 --- a/user_guide_src/source/installation/upgrade_responses.rst +++ b/user_guide_src/source/installation/upgrade_responses.rst @@ -7,8 +7,8 @@ Upgrade HTTP Responses Documentations ============== -- `Output Class Documentation CodeIgniter 3.X `_ -- :doc:`HTTP Responses Documentation CodeIgniter 4.X ` +- `Output Class Documentation CodeIgniter 3.x `_ +- :doc:`HTTP Responses Documentation CodeIgniter 4.x ` What has been changed ===================== diff --git a/user_guide_src/source/installation/upgrade_routing.rst b/user_guide_src/source/installation/upgrade_routing.rst index 83bc5c5ee8a5..6dfe13e1e965 100644 --- a/user_guide_src/source/installation/upgrade_routing.rst +++ b/user_guide_src/source/installation/upgrade_routing.rst @@ -8,8 +8,8 @@ Upgrade Routing Documentations ============== -- `URI Routing Documentation CodeIgniter 3.X `_ -- :doc:`URI Routing Documentation CodeIgniter 4.X ` +- `URI Routing Documentation CodeIgniter 3.x `_ +- :doc:`URI Routing Documentation CodeIgniter 4.x ` What has been changed ===================== diff --git a/user_guide_src/source/installation/upgrade_security.rst b/user_guide_src/source/installation/upgrade_security.rst index 81bf5e290d56..c84fb071adb1 100644 --- a/user_guide_src/source/installation/upgrade_security.rst +++ b/user_guide_src/source/installation/upgrade_security.rst @@ -8,8 +8,8 @@ Upgrade Security Documentations ============== -- `Security Class Documentation CodeIgniter 3.X `_ -- :doc:`Security Documentation CodeIgniter 4.X ` +- `Security Class Documentation CodeIgniter 3.x `_ +- :doc:`Security Documentation CodeIgniter 4.x ` .. note:: If you use the :doc:`../helpers/form_helper` and enable the CSRF filter globally, then :php:func:`form_open()` will automatically insert a hidden CSRF field in your forms. So you do not have to upgrade this by yourself. diff --git a/user_guide_src/source/installation/upgrade_sessions.rst b/user_guide_src/source/installation/upgrade_sessions.rst index ecc27360f310..d03b1c465267 100644 --- a/user_guide_src/source/installation/upgrade_sessions.rst +++ b/user_guide_src/source/installation/upgrade_sessions.rst @@ -8,8 +8,8 @@ Upgrade Sessions Documentations ============== -- `Session Library Documentation CodeIgniter 3.X `_ -- :doc:`Session Library Documentation CodeIgniter 4.X ` +- `Session Library Documentation CodeIgniter 3.x `_ +- :doc:`Session Library Documentation CodeIgniter 4.x ` What has been changed ===================== diff --git a/user_guide_src/source/installation/upgrade_validations.rst b/user_guide_src/source/installation/upgrade_validations.rst index 7f42cf8e1f70..116bc2e48d59 100644 --- a/user_guide_src/source/installation/upgrade_validations.rst +++ b/user_guide_src/source/installation/upgrade_validations.rst @@ -8,8 +8,8 @@ Upgrade Validations Documentations of Library ========================= -- `Form Validation Documentation CodeIgniter 3.X `_ -- :doc:`Validation Documentation CodeIgniter 4.X ` +- `Form Validation Documentation CodeIgniter 3.x `_ +- :doc:`Validation Documentation CodeIgniter 4.x ` What has been changed ===================== diff --git a/user_guide_src/source/installation/upgrade_view_parser.rst b/user_guide_src/source/installation/upgrade_view_parser.rst index c58aeb8dd491..1464e7e4d5b0 100644 --- a/user_guide_src/source/installation/upgrade_view_parser.rst +++ b/user_guide_src/source/installation/upgrade_view_parser.rst @@ -8,8 +8,8 @@ Upgrade View Parser Documentations ============== -- `Template Parser Documentation CodeIgniter 3.X `_ -- :doc:`View Parser Documentation CodeIgniter 4.X ` +- `Template Parser Documentation CodeIgniter 3.x `_ +- :doc:`View Parser Documentation CodeIgniter 4.x ` What has been changed ===================== diff --git a/user_guide_src/source/installation/upgrade_views.rst b/user_guide_src/source/installation/upgrade_views.rst index 09ffa427d33c..9b2e4867a804 100644 --- a/user_guide_src/source/installation/upgrade_views.rst +++ b/user_guide_src/source/installation/upgrade_views.rst @@ -8,8 +8,8 @@ Upgrade Views Documentations ============== -- `View Documentation CodeIgniter 3.X `_ -- :doc:`View Documentation CodeIgniter 4.X ` +- `View Documentation CodeIgniter 3.x `_ +- :doc:`View Documentation CodeIgniter 4.x ` What has been changed ===================== diff --git a/user_guide_src/source/libraries/uploaded_files.rst b/user_guide_src/source/libraries/uploaded_files.rst index 560706c2b73f..118af85e86ee 100644 --- a/user_guide_src/source/libraries/uploaded_files.rst +++ b/user_guide_src/source/libraries/uploaded_files.rst @@ -5,8 +5,8 @@ Working with Uploaded Files CodeIgniter makes working with files uploaded through a form much simpler and more secure than using PHP's ``$_FILES`` array directly. This extends the :doc:`File class ` and thus gains all of the features of that class. -.. note:: This is not the same as the File Uploading class in CodeIgniter v3.x. This provides a raw - interface to the uploaded files with a few small features. +.. note:: This is not the same as the File Uploading class in CodeIgniter 3. + This provides a raw interface to the uploaded files with a few small features. .. contents:: :local: @@ -309,7 +309,7 @@ getClientPath() .. versionadded:: 4.4.0 -Returns the `webkit relative path `_ of the uploaded file when the client has uploaded files via directory upload. +Returns the `webkit relative path `_ of the uploaded file when the client has uploaded files via directory upload. In PHP versions below 8.1, this returns ``null`` .. literalinclude:: uploaded_files/023.php From 00a15fc7ffd48103ca3de69128004e83b0247200 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sun, 7 Apr 2024 08:29:56 +0900 Subject: [PATCH 65/65] Prep for 4.4.8 release --- CHANGELOG.md | 17 +++++++++++ phpdoc.dist.xml | 2 +- system/CodeIgniter.php | 2 +- user_guide_src/source/changelogs/v4.4.8.rst | 14 +--------- user_guide_src/source/conf.py | 2 +- .../source/installation/upgrade_448.rst | 28 +++---------------- 6 files changed, 25 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 36c47ff5e99a..f0147f72b57b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,22 @@ # Changelog +## [v4.4.8](https://github.com/codeigniter4/CodeIgniter4/tree/v4.4.8) (2024-04-07) +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.4.7...v4.4.8) + +### Fixed Bugs + +* fix: [ImageMagickHandler] early terminate processing of invalid library path by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/8680 +* docs: fix PHPDoc types in BaseModel by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/8679 +* fix: the error view is determined by Exception code by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/8689 +* fix: `Pager::only([])` does not work by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/8702 +* refactor: remove unneeded code in SQLite3\Table and fix PHPDoc types in Database by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/8703 +* docs: fix return type in BaseResult by @Pebryan354 in https://github.com/codeigniter4/CodeIgniter4/pull/8709 + +### Refactoring + +* refactor: simplify ImageMagickHandler::getVersion() by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/8681 +* refactor: [Rector] Apply ExplicitBoolCompareRector by @samsonasik in https://github.com/codeigniter4/CodeIgniter4/pull/8704 + ## [v4.4.7](https://github.com/codeigniter4/CodeIgniter4/tree/v4.4.7) (2024-03-29) [Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.4.6...v4.4.7) diff --git a/phpdoc.dist.xml b/phpdoc.dist.xml index ad7f3f7d8066..616f6aeae98d 100644 --- a/phpdoc.dist.xml +++ b/phpdoc.dist.xml @@ -10,7 +10,7 @@ api/build/ api/cache/ - + system diff --git a/system/CodeIgniter.php b/system/CodeIgniter.php index 760a71f29259..6a2dca68c1e5 100644 --- a/system/CodeIgniter.php +++ b/system/CodeIgniter.php @@ -54,7 +54,7 @@ class CodeIgniter /** * The current version of CodeIgniter Framework */ - public const CI_VERSION = '4.4.7'; + public const CI_VERSION = '4.4.8'; /** * App startup time. diff --git a/user_guide_src/source/changelogs/v4.4.8.rst b/user_guide_src/source/changelogs/v4.4.8.rst index 4ff846be528a..723f129175ec 100644 --- a/user_guide_src/source/changelogs/v4.4.8.rst +++ b/user_guide_src/source/changelogs/v4.4.8.rst @@ -2,7 +2,7 @@ Version 4.4.8 ############# -Release Date: Unreleased +Release Date: April 7, 2024 **4.4.8 release of CodeIgniter4** @@ -19,18 +19,6 @@ BREAKING The third parameter ``int $statusCode = 500`` has been added to ``CodeIgniter\Debug\ExceptionHandler::determineView()`` for this purpose. -*************** -Message Changes -*************** - -******* -Changes -******* - -************ -Deprecations -************ - ********** Bugs Fixed ********** diff --git a/user_guide_src/source/conf.py b/user_guide_src/source/conf.py index d5735e310a47..bf80b5abef81 100644 --- a/user_guide_src/source/conf.py +++ b/user_guide_src/source/conf.py @@ -26,7 +26,7 @@ version = '4.4' # The full version, including alpha/beta/rc tags. -release = '4.4.7' +release = '4.4.8' # -- General configuration --------------------------------------------------- diff --git a/user_guide_src/source/installation/upgrade_448.rst b/user_guide_src/source/installation/upgrade_448.rst index 580a1ba8edab..5dd3da148a03 100644 --- a/user_guide_src/source/installation/upgrade_448.rst +++ b/user_guide_src/source/installation/upgrade_448.rst @@ -12,18 +12,6 @@ Please refer to the upgrade instructions corresponding to your installation meth :local: :depth: 2 -********************** -Mandatory File Changes -********************** - -**************** -Breaking Changes -**************** - -********************* -Breaking Enhancements -********************* - ************* Project Files ************* @@ -34,21 +22,13 @@ these files being outside of the **system** scope they will not be changed witho There are some third-party CodeIgniter modules available to assist with merging changes to the project space: `Explore on Packagist `_. -Content Changes -=============== - -The following files received significant changes (including deprecations or visual adjustments) -and it is recommended that you merge the updated versions with your application: - -Config ------- - -- @TODO - All Changes =========== This is a list of all files in the **project space** that received changes; many will be simple comments or formatting that have no effect on the runtime: -- @TODO +- app/.htaccess +- composer.json +- public/.htaccess +- tests/.htaccess