diff --git a/.github/mergeable.yml b/.github/mergeable.yml index 95766c23fcee..d20aa796f5d0 100644 --- a/.github/mergeable.yml +++ b/.github/mergeable.yml @@ -32,3 +32,38 @@ mergeable: Sincerely, the mergeable bot 🤖 - do: close + + - when: pull_request.opened, pull_request.ready_for_review + filter: + - do: payload + pull_request: + author_association: + must_include: + regex: ^NONE|FIRST_TIME_CONTRIBUTOR|FIRST_TIMER$ + validate: [] + pass: + - do: comment + payload: + body: | + Hi there, @@author! :wave: + + Thank you for sending this PR! + + We expect the following in all Pull Requests (PRs). + - PRs must be sent to the [appropriate branch](https://github.com/codeigniter4/CodeIgniter4/blob/develop/contributing/pull_request.md#branching) + - All git commits must be [GPG-signed](https://github.com/codeigniter4/CodeIgniter4/blob/develop/contributing/pull_request.md#signing) + - Must follow our [style guide](https://github.com/codeigniter4/CodeIgniter4/blob/develop/contributing/pull_request.md#php-style) + - Be [commented](https://github.com/codeigniter4/CodeIgniter4/blob/develop/contributing/pull_request.md#comments) in the PHP source file + - Be documented in the [user guide](https://github.com/codeigniter4/CodeIgniter4/blob/develop/contributing/pull_request.md#user-guide) + - Be [unit tested](https://github.com/codeigniter4/CodeIgniter4/blob/develop/contributing/pull_request.md#unit-testing) + - Pass all checks in GitHub Actions + + > [!IMPORTANT] + > We expect all code changes or bug-fixes to be accompanied by one or more tests added to our test suite to prove the code works. + + If pull requests do not comply with the above, they will likely be closed. Since we are a team of volunteers, we don't have any more time to work + on the framework than you do. Please make it as painless for your contributions to be included as possible. + + See https://github.com/codeigniter4/CodeIgniter4/blob/develop/contributing/pull_request.md + + Sincerely, the mergeable bot 🤖 diff --git a/.github/workflows/deploy-apidocs.yml b/.github/workflows/deploy-apidocs.yml index fb9665327578..ca77c6a08c26 100644 --- a/.github/workflows/deploy-apidocs.yml +++ b/.github/workflows/deploy-apidocs.yml @@ -49,7 +49,7 @@ jobs: - name: Download latest phpDocumentor working-directory: source - run: phive --no-progress install --trust-gpg-keys 8AC0BAA79732DD42 phpDocumentor + run: phive --no-progress install --trust-gpg-keys 6DA3ACC4991FFAE5 phpDocumentor - name: Prepare API repo working-directory: api diff --git a/CHANGELOG.md b/CHANGELOG.md index 876a767edb70..3556e750e802 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,39 @@ # Changelog +## [v4.6.1](https://github.com/codeigniter4/CodeIgniter4/tree/v4.6.0) (2025-05-02) +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.6.0...v4.6.1) + +### Fixed Bugs +* fix(CURLRequest): multiple header sections after redirects by @ducng99 in https://github.com/codeigniter4/CodeIgniter4/pull/9426 +* fix: set headers for CORS by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/9437 +* fix: upsert with composite unique index by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/9454 +* fix: `getVersion()` for OCI8 and SQLSRV drivers by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/9471 +* fix: Toolbar when `maxHistory` is set to `0` by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/9506 +* fix: `Session::markAsTempdata()` adding wrong TTL by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/9536 +* fix: added "application/octet-stream" to the "stl" mime type in the M… by @Franky5831 in https://github.com/codeigniter4/CodeIgniter4/pull/9543 + +### Refactoring +* refactor: get upper first protocol only one call in Email by @ddevsr in https://github.com/codeigniter4/CodeIgniter4/pull/9449 +* refactor: PHPDocs in `env()` by @ddevsr in https://github.com/codeigniter4/CodeIgniter4/pull/9468 +* refactor: remove lowercase event name for logging by @ddevsr in https://github.com/codeigniter4/CodeIgniter4/pull/9483 +* refactor: OCI8 `limit()` method by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/9472 +* refactor: deprecate redundant `FileHandler` cache methods by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/9511 +* refactor: fix `variable.undefined` (and other) errors by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/9513 +* refactor: fix `return.unusedType` errors by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/9514 +* refactor: add `CITestStreamFilter` to phpstan-analysed list and fix errors by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/9515 +* refactor: fix `property.protected` errors by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/9517 +* refactor: fix `function.alreadyNarrowedType` errors by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/9518 +* refactor: fix `empty.property` errors by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/9519 +* refactor: import FQCNs by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/9520 +* refactor: fix `isset.property` errors by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/9522 +* refactor: fix `missingType.return` errors by @warcooft in https://github.com/codeigniter4/CodeIgniter4/pull/9523 +* refactor: fix `nullCoalesce.variable` errors by @warcooft in https://github.com/codeigniter4/CodeIgniter4/pull/9524 +* refactor: fix phpstan errors in `URI` and `SiteURI` by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/9525 +* refactor: fix `@readonly` property errors by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/9529 +* refactor: fix `missingType.return` errors in system files by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/9530 +* refactor: fix `codeigniter.modelArgumentType` errors by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/9533 +* refactor: fix `Session` and `SessionInterface` code by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/9535 + ## [v4.6.0](https://github.com/codeigniter4/CodeIgniter4/tree/v4.6.0) (2025-01-19) [Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.5.8...v4.6.0) diff --git a/admin/RELEASE.md b/admin/RELEASE.md index 9ed91e55c183..697af85ff712 100644 --- a/admin/RELEASE.md +++ b/admin/RELEASE.md @@ -3,6 +3,7 @@ > Documentation guide based on the releases of `4.0.5` and `4.1.0` on January 31, 2021. > > Updated for `4.5.0` on April 7, 2024. +> Updated for `4.6.0` on January 19, 2025. > > -MGatner, kenjis @@ -33,10 +34,12 @@ git push upstream HEAD If you release a new minor version. -* [ ] Create PR to merge `4.y` into `develop` and merge it +* [ ] Create PR to merge `4.y` into `develop`: + * Title: `4.y.0 Merge code` + * Description: blank * [ ] Rename the current minor version (e.g., `4.5`) in Setting > Branches > - "Branch protection rules" to the next minor version. E.g. `4.5` → `4.6` -* [ ] Delete the merged `4.y` branch (This closes all PRs to the branch) + "Branch protection rules" to the next minor version (e.g. `4.5` → `4.6`). +* [ ] Delete the merged `4.y` branch (this closes all PRs to the branch). ## Preparation @@ -60,7 +63,7 @@ Work off direct clones of the repos so the release branches persist for a time. ## Changelog -When generating the changelog each Pull Request to be included must have one of +When generating the changelog, each pull request to be included must have one of the following [labels](https://github.com/codeigniter4/CodeIgniter4/labels): - **bug** ... PRs that fix bugs - **enhancement** ... PRs to improve existing functionalities @@ -72,11 +75,11 @@ PRs with breaking changes must have the following additional label: ### Generate Changelog -To auto-generate, navigate to the +To auto-generate the changelog, navigate to the [Releases](https://github.com/codeigniter4/CodeIgniter4/releases) page, click the "Draft a new release" button. -* Tag: `v4.x.x` (Create new tag) +* Choose a tag: `v4.x.x` (Create new tag: v4.x.x on publish) * Target: `develop` Click the "Generate release notes" button. @@ -85,7 +88,7 @@ Check the resulting content. If there are items in the *Others* section which should be included in the changelog, add a label to the PR and regenerate the changelog. -Copy the resulting content into **CHANGELOG.md** and adjust the format to match +Copy the resulting contents into **CHANGELOG.md** and adjust the format to match the existing content. ## Process @@ -95,23 +98,27 @@ the existing content. > been included with their PR, so this process assumes you will not be > generating much new content. -* [ ] Merge any Security Advisory PRs in private forks -* [ ] Replace **CHANGELOG.md** with the new version generated above +* [ ] Merge any security advisory PRs in private forks. +* [ ] Add the current version to **CHANGELOG.md** with the contents generated above. * [ ] 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_4xx.rst** - * [ ] fill in the "All Changes" section, and add it to **upgrade_4xx.rst** - * git diff --name-status origin/master -- . ':!system' ':!tests' ':!user_guide_src' - * Note: `tests/` is not used for distribution repos. See `admin/starter/tests/` + * [ ] fill in the "All Changes" section using the following command, and add it to **upgrade_4xx.rst**: + ``` + git diff --name-status origin/master -- . ':!.github/' ':!admin/' ':!system/' ':!tests/' \ + ':!user_guide_src/' ':!utils/' ':!*.json' ':!*.xml' ':!*.dist' ':!rector.php' \ + ':!phpstan*' ':!psalm*' ':!.php-cs-fixer.*' ':!LICENSE' ':!CHANGELOG.md' + ``` + * Note: `tests/` is not used for distribution repos. See `admin/starter/tests/`. * [ ] Remove the section titles that have no items - * [ ] [Minor version only] Update the "from" version in the title. E.g., `from 4.3.x` → `from 4.3.8` -* [ ] Run `php admin/prepare-release.php 4.x.x` and push to origin + * [ ] [Minor version only] Update the "from" version in the title, (e.g., `from 4.3.x` → `from 4.3.8`). +* [ ] Run `php admin/prepare-release.php 4.x.x` and push to origin. * The above command does the following: * Create a new branch `release-4.x.x` * Update **system/CodeIgniter.php** with the new version number: `const CI_VERSION = '4.x.x';` - * Update **user_guide_src/source/conf.py** with the new `version = '4.x'` (if applicable) - and `release = '4.x.x'` + * Update **user_guide_src/source/conf.py** with the new `version = '4.x'` (if releasing + the minor version) and `release = '4.x.x'`. * Update **user_guide_src/source/changelogs/{version}.rst** * Set the date to format `Release Date: January 31, 2021` * Update **phpdoc.dist.xml** with the new `CodeIgniter v4.x API` @@ -126,15 +133,17 @@ the existing content. Previous version: #xxxx Release Code: TODO New Changelog: TODO - ``` + (plus checklist) -* [ ] Let all tests run, then review and merge the PR + ``` + +* [ ] Let all tests run, then review and merge the PR. * [ ] Create a new PR from `develop` to `master`: * Title: `4.x.x Ready code` * Description: blank * [ ] Merge the PR and wait for all tests. * [ ] Create a new Release: - * Tag: `v4.x.x` (Create new tag) + * Choose a tag: `v4.x.x` (Create new tag: v4.x.x on publish) * Target: `master` * Title: `CodeIgniter 4.x.x` * Description: @@ -167,31 +176,31 @@ the existing content. created when v4.3.8 was released. * [ ] Fast-forward `develop` branch to catch the merge commit from `master` ```console - git fetch origin + git fetch upstream git checkout develop - git merge origin/develop - git merge origin/master - git push origin HEAD + git merge upstream/develop + git merge upstream/master + git push upstream HEAD ``` * [ ] Update the next minor version branch `4.y`: ```console - git fetch origin - git checkout 4.y - git merge origin/4.y - git merge origin/develop - git push origin HEAD + git fetch upstream + git switch 4.y + git merge upstream/4.y + git merge upstream/develop + git push upstream HEAD ``` * [ ] [Minor version only] Create the new next minor version branch `4.z`: ```console - git fetch origin + git fetch upstream git switch develop git switch -c 4.z - git push origin HEAD + git push upstream HEAD ``` -* [ ] Request CVEs and Publish any Security Advisories that were resolved from private forks - (note: publishing is restricted to administrators): +* [ ] Request CVEs and publish any security advisories that were resolved from private forks + (note: publishing is restricted to administrators). * [ ] Announce the release on the forums and Slack channel - (note: this forum is restricted to administrators): + (note: this forum is restricted to administrators). * Make a new topic in the "News & Discussion" forums: https://forum.codeigniter.com/forum-2.html * The content is somewhat organic, but should include any major features and diff --git a/admin/css/debug-toolbar/toolbar.scss b/admin/css/debug-toolbar/toolbar.scss index eec3d25546e3..65f4b802e22c 100644 --- a/admin/css/debug-toolbar/toolbar.scss +++ b/admin/css/debug-toolbar/toolbar.scss @@ -457,6 +457,31 @@ } } +@media screen and (max-width: 768px) { + #debug-bar { + table { + display: block; + overflow-x: auto; + font-size: 12px; + margin: 5px 5px 10px 5px; + + td, + th { + padding: 4px 6px; + } + } + + .timeline { + display: block; + white-space: nowrap; + font-size: 12px; + } + + .toolbar { + overflow-x: auto; + } + } + } // THEMES // ========================================================================== */ diff --git a/admin/framework/.gitignore b/admin/framework/.gitignore index 8071bd3d07f8..e24e7ce497d1 100644 --- a/admin/framework/.gitignore +++ b/admin/framework/.gitignore @@ -100,15 +100,15 @@ _modules/* .idea/ *.iml -# Netbeans -nbproject/ -build/ -nbbuild/ -dist/ -nbdist/ -nbactions.xml -nb-configuration.xml -.nb-gradle/ +# NetBeans +/nbproject/ +/build/ +/nbbuild/ +/dist/ +/nbdist/ +/nbactions.xml +/nb-configuration.xml +/.nb-gradle/ # Sublime Text *.tmlanguage.cache diff --git a/admin/starter/.gitignore b/admin/starter/.gitignore index 8071bd3d07f8..e24e7ce497d1 100644 --- a/admin/starter/.gitignore +++ b/admin/starter/.gitignore @@ -100,15 +100,15 @@ _modules/* .idea/ *.iml -# Netbeans -nbproject/ -build/ -nbbuild/ -dist/ -nbdist/ -nbactions.xml -nb-configuration.xml -.nb-gradle/ +# NetBeans +/nbproject/ +/build/ +/nbbuild/ +/dist/ +/nbdist/ +/nbactions.xml +/nb-configuration.xml +/.nb-gradle/ # Sublime Text *.tmlanguage.cache diff --git a/admin/starter/tests/_support/Libraries/ConfigReader.php b/admin/starter/tests/_support/Libraries/ConfigReader.php index 40d057acbe22..0bb4a8fcf598 100644 --- a/admin/starter/tests/_support/Libraries/ConfigReader.php +++ b/admin/starter/tests/_support/Libraries/ConfigReader.php @@ -2,6 +2,8 @@ namespace Tests\Support\Libraries; +use Config\App; + /** * Class ConfigReader * @@ -9,7 +11,7 @@ * loading external values. Used to read actual local values from * a config file. */ -class ConfigReader extends \Config\App +class ConfigReader extends App { public function __construct() { diff --git a/app/Config/Autoload.php b/app/Config/Autoload.php index 76cd9263556e..9a928241a0eb 100644 --- a/app/Config/Autoload.php +++ b/app/Config/Autoload.php @@ -17,8 +17,6 @@ * * NOTE: This class is required prior to Autoloader instantiation, * and does not extend BaseConfig. - * - * @immutable */ class Autoload extends AutoloadConfig { diff --git a/app/Config/Cache.php b/app/Config/Cache.php index 9ae2e843ac91..e6efa3acf6f5 100644 --- a/app/Config/Cache.php +++ b/app/Config/Cache.php @@ -108,6 +108,7 @@ class Cache extends BaseConfig * ------------------------------------------------------------------------- * Redis settings * ------------------------------------------------------------------------- + * * Your Redis server can be specified below, if you are using * the Redis or Predis drivers. * diff --git a/app/Config/DocTypes.php b/app/Config/DocTypes.php index 7e8aaacf09dd..788d68fdc117 100644 --- a/app/Config/DocTypes.php +++ b/app/Config/DocTypes.php @@ -2,9 +2,6 @@ namespace Config; -/** - * @immutable - */ class DocTypes { /** diff --git a/app/Config/Mimes.php b/app/Config/Mimes.php index 7722444abe7b..c2db7340cd7e 100644 --- a/app/Config/Mimes.php +++ b/app/Config/Mimes.php @@ -3,8 +3,6 @@ namespace Config; /** - * Mimes - * * This file contains an array of mime types. It is used by the * Upload class to help identify allowed file types. * @@ -15,8 +13,6 @@ * * When working with mime types, please make sure you have the ´fileinfo´ * extension enabled to reliably detect the media types. - * - * @immutable */ class Mimes { @@ -482,6 +478,8 @@ class Mimes 'application/sla', 'application/vnd.ms-pki.stl', 'application/x-navistyle', + 'model/stl', + 'application/octet-stream', ], ]; diff --git a/app/Config/Modules.php b/app/Config/Modules.php index 8d4bf56544b0..9e03fa491260 100644 --- a/app/Config/Modules.php +++ b/app/Config/Modules.php @@ -9,8 +9,6 @@ * * NOTE: This class is required prior to Autoloader instantiation, * and does not extend BaseConfig. - * - * @immutable */ class Modules extends BaseModules { diff --git a/app/Config/Optimize.php b/app/Config/Optimize.php index 6fb441fd2288..481e645f57b2 100644 --- a/app/Config/Optimize.php +++ b/app/Config/Optimize.php @@ -7,8 +7,6 @@ * * NOTE: This class does not extend BaseConfig for performance reasons. * So you cannot replace the property values with Environment Variables. - * - * @immutable */ class Optimize { diff --git a/app/Config/Paths.php b/app/Config/Paths.php index 262c745ffa95..3dc9c5d93951 100644 --- a/app/Config/Paths.php +++ b/app/Config/Paths.php @@ -15,8 +15,6 @@ * * NOTE: This class is required prior to Autoloader instantiation, * and does not extend BaseConfig. - * - * @immutable */ class Paths { diff --git a/app/Views/errors/html/debug.css b/app/Views/errors/html/debug.css index b20f36a53ce2..b8539a420221 100644 --- a/app/Views/errors/html/debug.css +++ b/app/Views/errors/html/debug.css @@ -3,7 +3,7 @@ --main-text-color: #555; --dark-text-color: #222; --light-text-color: #c7c7c7; - --brand-primary-color: #E06E3F; + --brand-primary-color: #DC4814; --light-bg-color: #ededee; --dark-bg-color: #404040; } @@ -71,7 +71,7 @@ p.lead { text-align: center; padding: calc(4px + 0.2083vw); width: 100%; - margin-top: -2.14rem; + top: 0; position: fixed; } diff --git a/composer.json b/composer.json index c762845f9a7e..1f07772400f4 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,7 @@ "psr/log": "^3.0" }, "require-dev": { - "codeigniter/phpstan-codeigniter": "^1.5.1", + "codeigniter/phpstan-codeigniter": "1.x-dev", "fakerphp/faker": "^1.24", "kint-php/kint": "^6.0", "mikey179/vfsstream": "^1.6.12", @@ -28,7 +28,7 @@ "phpunit/phpcov": "^9.0.2 || ^10.0", "phpunit/phpunit": "^10.5.16 || ^11.2", "predis/predis": "^1.1 || ^2.3", - "rector/rector": "2.0.6", + "rector/rector": "2.0.14", "shipmonk/phpstan-baseline-per-identifier": "^2.0" }, "replace": { @@ -81,8 +81,7 @@ }, "extra": { "branch-alias": { - "dev-develop": "4.x-dev", - "dev-master": "4.x-dev" + "dev-*": "4.6.x-dev" } }, "scripts": { diff --git a/phpdoc.dist.xml b/phpdoc.dist.xml index 533ad3e033f0..07612e40acf8 100644 --- a/phpdoc.dist.xml +++ b/phpdoc.dist.xml @@ -10,7 +10,7 @@ api/build/ api/cache/ - + system diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 999972f556b8..e6cc288cc678 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -1,45 +1,50 @@ includes: - - utils/phpstan-baseline/loader.neon + - utils/phpstan-baseline/loader.neon parameters: - phpVersion: 80100 - tmpDir: build/phpstan - level: 6 - bootstrapFiles: - - phpstan-bootstrap.php - paths: - - admin/starter/tests - - app - - system - - tests - excludePaths: - - app/Views/errors/cli/* - - app/Views/errors/html/* - - system/Commands/Generators/Views/* - - system/Debug/Toolbar/Views/toolbar.tpl.php - - system/Images/Handlers/GDHandler.php - - system/Test/Filters/CITestStreamFilter.php - - system/ThirdParty/* - - system/Validation/Views/single.php - - tests/system/View/Views/* - scanDirectories: - - system/Helpers - ignoreErrors: - - - identifier: missingType.generics - checkMissingCallableSignature: true - treatPhpDocTypesAsCertain: false - strictRules: - allRules: false - disallowedLooseComparison: true - booleansInConditions: true - disallowedBacktick: true - disallowedEmpty: true - disallowedImplicitArrayCreation: true - disallowedShortTernary: true - matchingInheritedMethodNames: true - codeigniter: - additionalServices: - - AfterAutoloadModule\Config\Services - shipmonkBaselinePerIdentifier: - directory: %currentWorkingDirectory% + phpVersion: 80100 + tmpDir: build/phpstan + level: 6 + bootstrapFiles: + - phpstan-bootstrap.php + paths: + - admin/starter/tests + - app + - system + - tests + excludePaths: + analyseAndScan: + - app/Views/errors/cli/* + - app/Views/errors/html/* + - system/Commands/Generators/Views/* + - system/Debug/Toolbar/Views/toolbar.tpl.php + - system/Images/Handlers/GDHandler.php + - system/ThirdParty/* + - system/Validation/Views/single.php + - tests/system/View/Views/* + analyse: + - tests/_support/* + scanDirectories: + - system/Helpers + ignoreErrors: + - + identifier: missingType.generics + checkMissingCallableSignature: true + treatPhpDocTypesAsCertain: false + strictRules: + allRules: false + disallowedLooseComparison: true + booleansInConditions: true + disallowedBacktick: true + disallowedEmpty: true + disallowedImplicitArrayCreation: true + disallowedShortTernary: true + matchingInheritedMethodNames: true + codeigniter: + additionalServices: + - AfterAutoloadModule\Config\Services + additionalModelNamespaces: + - Tests\Support\Models + checkArgumentTypeOfModel: false + shipmonkBaselinePerIdentifier: + directory: %currentWorkingDirectory% diff --git a/preload.php b/preload.php index 0b453122345e..86322d5e7137 100644 --- a/preload.php +++ b/preload.php @@ -9,6 +9,9 @@ * the LICENSE file that was distributed with this source code. */ +use CodeIgniter\Boot; +use Config\Paths; + /* *--------------------------------------------------------------- * Sample file for Preloading @@ -69,10 +72,10 @@ public function __construct() private function loadAutoloader(): void { - $paths = new Config\Paths(); + $paths = new Paths(); require rtrim($paths->systemDirectory, '\\/ ') . DIRECTORY_SEPARATOR . 'Boot.php'; - CodeIgniter\Boot::preload($paths); + Boot::preload($paths); } /** diff --git a/public/index.php b/public/index.php index eb7f5c988838..a0a20db43dbf 100644 --- a/public/index.php +++ b/public/index.php @@ -1,5 +1,8 @@ systemDirectory . '/Boot.php'; -exit(CodeIgniter\Boot::bootWeb($paths)); +exit(Boot::bootWeb($paths)); diff --git a/rector.php b/rector.php index ffa21b4e663e..b8b5e1ccc73f 100644 --- a/rector.php +++ b/rector.php @@ -18,7 +18,6 @@ use Rector\CodeQuality\Rector\FuncCall\ChangeArrayPushToArrayAssignRector; use Rector\CodeQuality\Rector\FuncCall\CompactToVariablesRector; use Rector\CodeQuality\Rector\FunctionLike\SimplifyUselessVariableRector; -use Rector\CodeQuality\Rector\Identical\FlipTypeControlToUseExclusiveTypeRector; use Rector\CodeQuality\Rector\Ternary\TernaryEmptyArrayArrayDimFetchToCoalesceRector; use Rector\CodingStyle\Rector\ClassMethod\FuncGetArgsToVariadicParamRector; use Rector\CodingStyle\Rector\ClassMethod\MakeInheritedMethodVisibilitySameAsParentRector; @@ -36,6 +35,7 @@ use Rector\Php70\Rector\FuncCall\RandomFunctionRector; use Rector\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector; use Rector\Php81\Rector\FuncCall\NullToStrictStringFuncCallArgRector; +use Rector\PHPUnit\CodeQuality\Rector\Class_\RemoveDataProviderParamKeysRector; use Rector\PHPUnit\CodeQuality\Rector\Class_\YieldDataProviderRector; use Rector\Privatization\Rector\Property\PrivatizeFinalClassPropertyRector; use Rector\Strict\Rector\Empty_\DisallowedEmptyRuleFixerRector; @@ -45,7 +45,6 @@ use Rector\TypeDeclaration\Rector\ClassMethod\ReturnNeverTypeRector; use Rector\TypeDeclaration\Rector\Closure\AddClosureVoidReturnTypeWhereNoReturnRector; use Rector\TypeDeclaration\Rector\Closure\ClosureReturnTypeRector; -use Rector\TypeDeclaration\Rector\Empty_\EmptyOnNullableObjectToInstanceOfRector; use Rector\TypeDeclaration\Rector\Function_\AddFunctionVoidReturnTypeWhereNoReturnRector; use Rector\TypeDeclaration\Rector\Property\TypedPropertyFromAssignsRector; use Rector\TypeDeclaration\Rector\StmtsAwareInterface\DeclareStrictTypesRector; @@ -168,6 +167,8 @@ NullToStrictStringFuncCallArgRector::class, CompactToVariablesRector::class, + + RemoveDataProviderParamKeysRector::class, ]) // auto import fully qualified class names ->withImportNames(removeUnusedImports: true) @@ -189,7 +190,6 @@ MakeInheritedMethodVisibilitySameAsParentRector::class, SimplifyEmptyCheckOnEmptyArrayRector::class, TernaryEmptyArrayArrayDimFetchToCoalesceRector::class, - EmptyOnNullableObjectToInstanceOfRector::class, DisallowedEmptyRuleFixerRector::class, PrivatizeFinalClassPropertyRector::class, BooleanInIfConditionRuleFixerRector::class, @@ -199,7 +199,6 @@ AddMethodCallBasedStrictParamTypeRector::class, TypedPropertyFromAssignsRector::class, ClosureReturnTypeRector::class, - FlipTypeControlToUseExclusiveTypeRector::class, AddArrowFunctionReturnTypeRector::class, ]) ->withConfiguredRule(StringClassNameToClassConstantRector::class, [ diff --git a/spark b/spark index 856dc3f8a227..e7871ea8e682 100755 --- a/spark +++ b/spark @@ -10,6 +10,9 @@ * the LICENSE file that was distributed with this source code. */ +use CodeIgniter\Boot; +use Config\Paths; + /* * -------------------------------------------------------------------- * CODEIGNITER COMMAND-LINE TOOLS @@ -76,9 +79,9 @@ chdir(FCPATH); require FCPATH . '../app/Config/Paths.php'; // ^^^ Change this line if you move your application folder -$paths = new Config\Paths(); +$paths = new Paths(); // LOAD THE FRAMEWORK BOOTSTRAP FILE require $paths->systemDirectory . '/Boot.php'; -exit(CodeIgniter\Boot::bootSpark($paths)); +exit(Boot::bootSpark($paths)); diff --git a/system/Autoloader/Autoloader.php b/system/Autoloader/Autoloader.php index a88c673d5fc0..2e628f1590d6 100644 --- a/system/Autoloader/Autoloader.php +++ b/system/Autoloader/Autoloader.php @@ -123,7 +123,7 @@ public function initialize(Autoload $config, Modules $modules) $this->files = $config->files; } - if (isset($config->helpers)) { + if ($config->helpers !== []) { $this->helpers = [...$this->helpers, ...$config->helpers]; } diff --git a/system/Autoloader/FileLocator.php b/system/Autoloader/FileLocator.php index 844ac7fc4cc9..13ecb817a56c 100644 --- a/system/Autoloader/FileLocator.php +++ b/system/Autoloader/FileLocator.php @@ -266,12 +266,6 @@ protected function getNamespaces() return array_merge($namespaces, $system); } - /** - * Find the qualified name of a file according to - * the namespace of the first matched namespace path. - * - * @return false|string The qualified name or false if the path is not found - */ public function findQualifiedNameFromPath(string $path) { $resolvedPath = realpath($path); @@ -299,7 +293,9 @@ public function findQualifiedNameFromPath(string $path) ), '\\', ); + // Remove the file extension (.php) + /** @var class-string */ $className = mb_substr($className, 0, -4); if (in_array($className, $this->invalidClassnames, true)) { diff --git a/system/Autoloader/FileLocatorInterface.php b/system/Autoloader/FileLocatorInterface.php index 3a1112dd480e..8f6129abee17 100644 --- a/system/Autoloader/FileLocatorInterface.php +++ b/system/Autoloader/FileLocatorInterface.php @@ -62,7 +62,7 @@ public function search(string $path, string $ext = 'php', bool $prioritizeApp = * Find the qualified name of a file according to * the namespace of the first matched namespace path. * - * @return false|string The qualified name or false if the path is not found + * @return class-string|false The qualified name or false if the path is not found */ public function findQualifiedNameFromPath(string $path); diff --git a/system/BaseModel.php b/system/BaseModel.php index b0be872050c6..cf8df09998f3 100644 --- a/system/BaseModel.php +++ b/system/BaseModel.php @@ -121,9 +121,13 @@ abstract class BaseModel protected ?DataConverter $converter = null; /** - * If this model should use "softDeletes" and - * simply set a date when rows are deleted, or - * do hard deletes. + * Determines whether the model should protect field names during + * mass assignment operations such as insert() and update(). + * + * When set to true, only the fields explicitly defined in the $allowedFields + * property will be allowed for mass assignment. This helps prevent + * unintended modification of database fields and improves security + * by avoiding mass assignment vulnerabilities. * * @var bool */ diff --git a/system/CLI/CLI.php b/system/CLI/CLI.php index a21996507d55..b8ea4714f39b 100644 --- a/system/CLI/CLI.php +++ b/system/CLI/CLI.php @@ -107,12 +107,12 @@ class CLI /** * List of array segments. * - * @var array + * @var list */ protected static $segments = []; /** - * @var array + * @var array */ protected static $options = []; @@ -944,6 +944,8 @@ public static function getSegment(int $index) /** * Returns the raw array of segments found. + * + * @return list */ public static function getSegments(): array { @@ -971,6 +973,8 @@ public static function getOption(string $name) /** * Returns the raw array of options found. + * + * @return array */ public static function getOptions(): array { diff --git a/system/CLI/Commands.php b/system/CLI/Commands.php index 732cb370382d..aecd22c3f348 100644 --- a/system/CLI/Commands.php +++ b/system/CLI/Commands.php @@ -21,13 +21,15 @@ /** * Core functionality for running, listing, etc commands. + * + * @phpstan-type commands_list array, 'file': string, 'group': string,'description': string}> */ class Commands { /** * The found commands. * - * @var array + * @var commands_list */ protected $commands = []; @@ -52,6 +54,8 @@ public function __construct($logger = null) /** * Runs a command given * + * @param array $params + * * @return int Exit code */ public function run(string $command, array $params) @@ -77,7 +81,7 @@ public function run(string $command, array $params) /** * Provide access to the list of commands. * - * @return array + * @return commands_list */ public function getCommands() { @@ -96,7 +100,7 @@ public function discoverCommands() return; } - /** @var FileLocatorInterface $locator */ + /** @var FileLocatorInterface */ $locator = service('locator'); $files = $locator->listFiles('Commands/'); @@ -109,6 +113,7 @@ public function discoverCommands() // Loop over each file checking to see if a command with that // alias exists in the class. foreach ($files as $file) { + /** @var class-string|false */ $className = $locator->findQualifiedNameFromPath($file); if ($className === false || ! class_exists($className)) { @@ -122,7 +127,6 @@ public function discoverCommands() continue; } - /** @var BaseCommand $class */ $class = new $className($this->logger, $this); if (isset($class->group) && ! isset($this->commands[$class->name])) { @@ -146,6 +150,8 @@ public function discoverCommands() /** * Verifies if the command being sought is found * in the commands list. + * + * @param commands_list $commands */ public function verifyCommand(string $command, array $commands): bool { @@ -153,9 +159,9 @@ public function verifyCommand(string $command, array $commands): bool return true; } - $message = lang('CLI.commandNotFound', [$command]); - + $message = lang('CLI.commandNotFound', [$command]); $alternatives = $this->getCommandAlternatives($command, $commands); + if ($alternatives !== []) { if (count($alternatives) === 1) { $message .= "\n\n" . lang('CLI.altCommandSingular') . "\n "; @@ -175,11 +181,17 @@ public function verifyCommand(string $command, array $commands): bool /** * Finds alternative of `$name` among collection * of commands. + * + * @param commands_list $collection + * + * @return list */ protected function getCommandAlternatives(string $name, array $collection): array { + /** @var array */ $alternatives = []; + /** @var string $commandName */ foreach (array_keys($collection) as $commandName) { $lev = levenshtein($name, $commandName); diff --git a/system/CLI/Console.php b/system/CLI/Console.php index 5cb8b57a0ad3..325eaa7b455c 100644 --- a/system/CLI/Console.php +++ b/system/CLI/Console.php @@ -38,14 +38,13 @@ public function run() $appConfig = config(App::class); Services::createRequest($appConfig, true); // Load Routes - Services::routes()->loadRoutes(); + service('routes')->loadRoutes(); - $runner = Services::commands(); $params = array_merge(CLI::getSegments(), CLI::getOptions()); $params = $this->parseParamsForHelpOption($params); $command = array_shift($params) ?? 'list'; - return $runner->run($command, $params); + return service('commands')->run($command, $params); } /** @@ -75,6 +74,8 @@ public function showHeader(bool $suppress = false) * If present, it will be found as `['help' => null]`. * We'll remove that as an option from `$params` and * unshift it as argument instead. + * + * @param array $params */ private function parseParamsForHelpOption(array $params): array { diff --git a/system/Cache/Handlers/FileHandler.php b/system/Cache/Handlers/FileHandler.php index 0a97a89f4862..4fd03e7921b0 100644 --- a/system/Cache/Handlers/FileHandler.php +++ b/system/Cache/Handlers/FileHandler.php @@ -63,6 +63,8 @@ public function __construct(Cache $config) $this->mode = $config->file['mode'] ?? 0640; $this->prefix = $config->prefix; + + helper('filesystem'); } /** @@ -96,7 +98,7 @@ public function save(string $key, $value, int $ttl = 60) 'data' => $value, ]; - if ($this->writeFile($this->path . $key, serialize($contents))) { + if (write_file($this->path . $key, serialize($contents))) { try { chmod($this->path . $key, $this->mode); @@ -176,7 +178,7 @@ public function decrement(string $key, int $offset = 1) */ public function clean() { - return $this->deleteFiles($this->path, false, true); + return delete_files($this->path, false, true); } /** @@ -184,7 +186,7 @@ public function clean() */ public function getCacheInfo() { - return $this->getDirFileInfo($this->path); + return get_dir_file_info($this->path); } /** @@ -251,6 +253,8 @@ protected function getItem(string $filename) /** * Writes a file to disk, or returns false if not successful. * + * @deprecated 4.6.1 Use `write_file()` instead. + * * @param string $path * @param string $data * @param string $mode @@ -265,7 +269,9 @@ protected function writeFile($path, $data, $mode = 'wb') flock($fp, LOCK_EX); - for ($result = $written = 0, $length = strlen($data); $written < $length; $written += $result) { + $result = 0; + + for ($written = 0, $length = strlen($data); $written < $length; $written += $result) { if (($result = fwrite($fp, substr($data, $written))) === false) { break; } @@ -283,6 +289,8 @@ protected function writeFile($path, $data, $mode = 'wb') * If the second parameter is set to TRUE, any directories contained * within the supplied base directory will be nuked as well. * + * @deprecated 4.6.1 Use `delete_files()` instead. + * * @param string $path File path * @param bool $delDir Whether to delete any directories found in the path * @param bool $htdocs Whether to skip deleting .htaccess and index page files @@ -318,6 +326,8 @@ protected function deleteFiles(string $path, bool $delDir = false, bool $htdocs * * Any sub-folders contained within the specified path are read as well. * + * @deprecated 4.6.1 Use `get_dir_file_info()` instead. + * * @param string $sourceDir Path to source * @param bool $topLevelOnly Look only at the top level directory specified? * @param bool $_recursion Internal variable to determine recursion status - do not use in calls @@ -360,6 +370,8 @@ protected function getDirFileInfo(string $sourceDir, bool $topLevelOnly = true, * Options are: name, server_path, size, date, readable, writable, executable, fileperms * Returns FALSE if the file cannot be found. * + * @deprecated 4.6.1 Use `get_file_info()` instead. + * * @param string $file Path to file * @param array|string $returnedValues Array or comma separated string of information returned * diff --git a/system/Cache/Handlers/RedisHandler.php b/system/Cache/Handlers/RedisHandler.php index 6cd43506bdb5..80c21e8559e8 100644 --- a/system/Cache/Handlers/RedisHandler.php +++ b/system/Cache/Handlers/RedisHandler.php @@ -181,7 +181,7 @@ public function deleteMatching(string $pattern) $iterator = null; do { - /** @var false|list|Redis $keys */ + /** @var false|list $keys */ $keys = $this->redis->scan($iterator, $pattern); if (is_array($keys)) { diff --git a/system/CodeIgniter.php b/system/CodeIgniter.php index 35f97f748fce..2c89a5e2e4f1 100644 --- a/system/CodeIgniter.php +++ b/system/CodeIgniter.php @@ -55,7 +55,7 @@ class CodeIgniter /** * The current version of CodeIgniter Framework */ - public const CI_VERSION = '4.6.0'; + public const CI_VERSION = '4.6.1'; /** * App startup time. @@ -948,7 +948,9 @@ protected function display404errors(PageNotFoundException $e) $this->response->setStatusCode($e->getCode()); // Is there a 404 Override available? - if ($override = $this->router->get404Override()) { + $override = $this->router->get404Override(); + + if ($override !== null) { $returned = null; if ($override instanceof Closure) { diff --git a/system/Commands/Generators/MigrationGenerator.php b/system/Commands/Generators/MigrationGenerator.php index b7d7d585e945..b5e64ec1bce1 100644 --- a/system/Commands/Generators/MigrationGenerator.php +++ b/system/Commands/Generators/MigrationGenerator.php @@ -112,10 +112,7 @@ protected function prepare(string $class): string $data['DBGroup'] = is_string($DBGroup) ? $DBGroup : 'default'; $data['DBDriver'] = config(Database::class)->{$data['DBGroup']}['DBDriver']; - /** @var SessionConfig|null $session */ - $session = config(SessionConfig::class); - - $data['matchIP'] = $session->matchIP; + $data['matchIP'] = config(SessionConfig::class)->matchIP; } return $this->parseTemplate($class, [], [], $data); diff --git a/system/Commands/Utilities/ConfigCheck.php b/system/Commands/Utilities/ConfigCheck.php index fa84cb287caa..4583c67dae87 100644 --- a/system/Commands/Utilities/ConfigCheck.php +++ b/system/Commands/Utilities/ConfigCheck.php @@ -73,7 +73,7 @@ final class ConfigCheck extends BaseCommand protected $options = []; /** - * {@inheritDoc} + * @return int */ public function run(array $params) { diff --git a/system/Commands/Utilities/Environment.php b/system/Commands/Utilities/Environment.php index 103591209d1e..22794fe9d51d 100644 --- a/system/Commands/Utilities/Environment.php +++ b/system/Commands/Utilities/Environment.php @@ -80,7 +80,7 @@ final class Environment extends BaseCommand ]; /** - * {@inheritDoc} + * @return int */ public function run(array $params) { @@ -88,7 +88,7 @@ public function run(array $params) CLI::write(sprintf('Your environment is currently set as %s.', CLI::color($_SERVER['CI_ENVIRONMENT'] ?? ENVIRONMENT, 'green'))); CLI::newLine(); - return; + return EXIT_ERROR; } $env = strtolower(array_shift($params)); @@ -98,21 +98,21 @@ public function run(array $params) CLI::error('You will not be able to run spark under a "testing" environment.', 'light_gray', 'red'); CLI::newLine(); - return; + return EXIT_ERROR; } if (! in_array($env, self::$knownTypes, true)) { CLI::error(sprintf('Invalid environment type "%s". Expected one of "%s".', $env, implode('" and "', self::$knownTypes)), 'light_gray', 'red'); CLI::newLine(); - return; + return EXIT_ERROR; } if (! $this->writeNewEnvironmentToEnvFile($env)) { CLI::error('Error in writing new environment to .env file.', 'light_gray', 'red'); CLI::newLine(); - return; + return EXIT_ERROR; } // force DotEnv to reload the new environment @@ -124,6 +124,8 @@ public function run(array $params) CLI::write(sprintf('Environment is successfully changed to "%s".', $env), 'green'); CLI::write('The ENVIRONMENT constant will be changed in the next script execution.'); CLI::newLine(); + + return EXIT_SUCCESS; } /** diff --git a/system/Commands/Utilities/Optimize.php b/system/Commands/Utilities/Optimize.php index 11872bebafa5..1a103a1b9c52 100644 --- a/system/Commands/Utilities/Optimize.php +++ b/system/Commands/Utilities/Optimize.php @@ -55,7 +55,7 @@ final class Optimize extends BaseCommand protected $usage = 'optimize'; /** - * {@inheritDoc} + * @return int */ public function run(array $params) { @@ -63,13 +63,13 @@ public function run(array $params) $this->enableCaching(); $this->clearCache(); $this->removeDevPackages(); + + return EXIT_SUCCESS; } catch (RuntimeException) { CLI::error('The "spark optimize" failed.'); return EXIT_ERROR; } - - return EXIT_SUCCESS; } private function clearCache(): void diff --git a/system/Commands/Utilities/PhpIniCheck.php b/system/Commands/Utilities/PhpIniCheck.php index eb2192434133..f25518fb283d 100644 --- a/system/Commands/Utilities/PhpIniCheck.php +++ b/system/Commands/Utilities/PhpIniCheck.php @@ -68,7 +68,7 @@ final class PhpIniCheck extends BaseCommand protected $options = []; /** - * {@inheritDoc} + * @return int */ public function run(array $params) { diff --git a/system/Common.php b/system/Common.php index 7fb4c5964254..910ec9b1cc28 100644 --- a/system/Common.php +++ b/system/Common.php @@ -124,7 +124,6 @@ function clean_path(string $path): string */ function command(string $command) { - $runner = service('commands'); $regexString = '([^\s]+?)(?:\s|(? */ $params = []; + $command = array_shift($args); $optionValue = false; foreach ($args as $i => $arg) { @@ -187,7 +187,7 @@ function command(string $command) } ob_start(); - $runner->run($command, $params); + service('commands')->run($command, $params); return ob_get_clean(); } @@ -371,9 +371,9 @@ function db_connect($db = null, bool $getShared = true) * retrieving values set from the .env file for * use in config files. * - * @param string|null $default + * @param array|bool|float|int|object|string|null $default * - * @return bool|string|null + * @return array|bool|float|int|object|string|null */ function env(string $key, $default = null) { diff --git a/system/ComposerScripts.php b/system/ComposerScripts.php index 993483c5775a..1794e7ffe9eb 100644 --- a/system/ComposerScripts.php +++ b/system/ComposerScripts.php @@ -67,7 +67,7 @@ final class ComposerScripts * This static method is called by Composer after every update event, * i.e., `composer install`, `composer update`, `composer remove`. */ - public static function postUpdate() + public static function postUpdate(): void { self::recursiveDelete(self::$path); diff --git a/system/Config/BaseConfig.php b/system/Config/BaseConfig.php index e056664e24be..ce6594d45d36 100644 --- a/system/Config/BaseConfig.php +++ b/system/Config/BaseConfig.php @@ -11,6 +11,7 @@ namespace CodeIgniter\Config; +use CodeIgniter\Autoloader\FileLocatorInterface; use CodeIgniter\Exceptions\ConfigException; use CodeIgniter\Exceptions\RuntimeException; use Config\Encryption; @@ -252,6 +253,7 @@ protected function registerProperties() static::$discovering = true; + /** @var FileLocatorInterface */ $locator = service('locator'); $registrarsFiles = $locator->search('Config/Registrar.php'); diff --git a/system/Config/Factories.php b/system/Config/Factories.php index 797f9fd9c7de..ab5343d15fb8 100644 --- a/system/Config/Factories.php +++ b/system/Config/Factories.php @@ -13,6 +13,7 @@ namespace CodeIgniter\Config; +use CodeIgniter\Autoloader\FileLocatorInterface; use CodeIgniter\Database\ConnectionInterface; use CodeIgniter\Exceptions\InvalidArgumentException; use CodeIgniter\Model; @@ -299,6 +300,7 @@ class_exists($alias, false) } // Have to do this the hard way... + /** @var FileLocatorInterface */ $locator = service('locator'); // Check if the class alias was namespaced @@ -511,6 +513,12 @@ public static function getBasename(string $alias): string /** * Gets component data for caching. * + * @return array{ + * options: array, + * aliases: array, + * instances: array, + * } + * * @internal For caching only */ public static function getComponentInstances(string $component): array diff --git a/system/Config/Filters.php b/system/Config/Filters.php index 562eae8a26ef..7eddc721bff6 100644 --- a/system/Config/Filters.php +++ b/system/Config/Filters.php @@ -113,6 +113,8 @@ class Filters extends BaseConfig * * Example: * 'isLoggedIn' => ['before' => ['account/*', 'profiles/*']] + * + * @var array>> */ public array $filters = []; } diff --git a/system/Database/BaseBuilder.php b/system/Database/BaseBuilder.php index e10dd557fda0..cba7f5cbb8b4 100644 --- a/system/Database/BaseBuilder.php +++ b/system/Database/BaseBuilder.php @@ -3380,6 +3380,8 @@ public function resetQuery() * Resets the query builder values. Called by the get() function * * @param array $qbResetItems An array of fields to reset + * + * @return void */ protected function resetRun(array $qbResetItems) { @@ -3390,6 +3392,8 @@ protected function resetRun(array $qbResetItems) /** * Resets the query builder values. Called by the get() function + * + * @return void */ protected function resetSelect() { @@ -3407,7 +3411,7 @@ protected function resetSelect() 'QBUnion' => [], ]); - if (! empty($this->db)) { + if ($this->db instanceof BaseConnection) { $this->db->setAliasedTables([]); } @@ -3421,6 +3425,8 @@ protected function resetSelect() * Resets the query builder "write" values. * * Called by the insert() update() insertBatch() updateBatch() and delete() functions + * + * @return void */ protected function resetWrite() { diff --git a/system/Database/Config.php b/system/Database/Config.php index 00d1b192cae3..6f25ad3c0059 100644 --- a/system/Database/Config.php +++ b/system/Database/Config.php @@ -18,8 +18,6 @@ use Config\Database as DbConfig; /** - * Class Config - * * @see \CodeIgniter\Database\ConfigTest */ class Config extends BaseConfig @@ -141,6 +139,8 @@ public static function seeder(?string $group = null) /** * Ensures the database Connection Manager/Factory is loaded and ready to use. + * + * @return void */ protected static function ensureFactory() { diff --git a/system/Database/Exceptions/DataException.php b/system/Database/Exceptions/DataException.php index a7ccd8c6f5c3..7cd9f526e6c2 100644 --- a/system/Database/Exceptions/DataException.php +++ b/system/Database/Exceptions/DataException.php @@ -65,21 +65,33 @@ public static function forInvalidArgument(string $argument) return new static(lang('Database.invalidArgument', [$argument])); } + /** + * @return DataException + */ public static function forInvalidAllowedFields(string $model) { return new static(lang('Database.invalidAllowedFields', [$model])); } + /** + * @return DataException + */ public static function forTableNotFound(string $table) { return new static(lang('Database.tableNotFound', [$table])); } + /** + * @return DataException + */ public static function forEmptyInputGiven(string $argument) { return new static(lang('Database.forEmptyInputGiven', [$argument])); } + /** + * @return DataException + */ public static function forFindColumnHaveMultipleColumns() { return new static(lang('Database.forFindColumnHaveMultipleColumns')); diff --git a/system/Database/Forge.php b/system/Database/Forge.php index 53c819fb24c5..8405d9f7143f 100644 --- a/system/Database/Forge.php +++ b/system/Database/Forge.php @@ -984,6 +984,8 @@ protected function _processColumn(array $processedField): string /** * Performs a data type mapping between different databases. + * + * @return void */ protected function _attributeType(array &$attributes) { @@ -999,6 +1001,8 @@ protected function _attributeType(array &$attributes) * if $attributes['TYPE'] is found in the array * - array(TYPE => UTYPE) will change $field['type'], * from TYPE to UTYPE in case of a match + * + * @return void */ protected function _attributeUnsigned(array &$attributes, array &$field) { @@ -1030,6 +1034,9 @@ protected function _attributeUnsigned(array &$attributes, array &$field) $field['unsigned'] = ($this->unsigned === true) ? ' UNSIGNED' : ''; } + /** + * @return void + */ protected function _attributeDefault(array &$attributes, array &$field) { if ($this->default === false) { @@ -1051,6 +1058,9 @@ protected function _attributeDefault(array &$attributes, array &$field) } } + /** + * @return void + */ protected function _attributeUnique(array &$attributes, array &$field) { if (! empty($attributes['UNIQUE']) && $attributes['UNIQUE'] === true) { @@ -1058,6 +1068,9 @@ protected function _attributeUnique(array &$attributes, array &$field) } } + /** + * @return void + */ protected function _attributeAutoIncrement(array &$attributes, array &$field) { if (! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === true @@ -1254,6 +1267,8 @@ protected function _processForeignKeys(string $table, bool $asQuery = false): ar /** * Resets table creation vars + * + * @return void */ public function reset() { diff --git a/system/Database/Migration.php b/system/Database/Migration.php index ca9e5e0cf266..f3a1dc457629 100644 --- a/system/Database/Migration.php +++ b/system/Database/Migration.php @@ -64,11 +64,15 @@ public function getDBGroup(): ?string /** * Perform a migration step. + * + * @return void */ abstract public function up(); /** * Revert a migration step. + * + * @return void */ abstract public function down(); } diff --git a/system/Database/MigrationRunner.php b/system/Database/MigrationRunner.php index 32842916769d..fdff86589132 100644 --- a/system/Database/MigrationRunner.php +++ b/system/Database/MigrationRunner.php @@ -317,6 +317,8 @@ public function regress(int $targetBatch = 0, ?string $group = null) * * @param string $path Full path to a valid migration file * @param string $path Namespace of the target migration + * + * @return bool */ public function force(string $path, string $namespace, ?string $group = null) { @@ -575,6 +577,8 @@ public function clearCliMessages() /** * Truncates the history table. + * + * @return void */ public function clearHistory() { @@ -587,6 +591,8 @@ public function clearHistory() * Add a history to the table. * * @param object $migration + * + * @return void */ protected function addHistory($migration, int $batch) { @@ -614,6 +620,8 @@ protected function addHistory($migration, int $batch) * Removes a single history * * @param object $history + * + * @return void */ protected function removeHistory($history) { @@ -752,6 +760,8 @@ public function getBatchEnd(int $batch): string /** * Ensures that we have created our migrations table * in the database. + * + * @return void */ public function ensureTable() { diff --git a/system/Database/OCI8/Builder.php b/system/Database/OCI8/Builder.php index 5b586761e2c0..2e62b3121cbd 100644 --- a/system/Database/OCI8/Builder.php +++ b/system/Database/OCI8/Builder.php @@ -48,16 +48,6 @@ class Builder extends BaseBuilder */ protected $countString = 'SELECT COUNT(1) '; - /** - * Limit used flag - * - * If we use LIMIT, we'll add a field that will - * throw off num_fields later. - * - * @var bool - */ - protected $limitUsed = false; - /** * A reference to the database connection. * @@ -214,28 +204,13 @@ protected function _update(string $table, array $values): string protected function _limit(string $sql, bool $offsetIgnore = false): string { $offset = (int) ($offsetIgnore === false ? $this->QBOffset : 0); - if (version_compare($this->db->getVersion(), '12.1', '>=')) { - // OFFSET-FETCH can be used only with the ORDER BY clause - if (empty($this->QBOrderBy)) { - $sql .= ' ORDER BY 1'; - } - return $sql . ' OFFSET ' . $offset . ' ROWS FETCH NEXT ' . $this->QBLimit . ' ROWS ONLY'; + // OFFSET-FETCH can be used only with the ORDER BY clause + if (empty($this->QBOrderBy)) { + $sql .= ' ORDER BY 1'; } - $this->limitUsed = true; - $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); - } - - /** - * Resets the query builder values. Called by the get() function - */ - protected function resetSelect() - { - $this->limitUsed = false; - parent::resetSelect(); + return $sql . ' OFFSET ' . $offset . ' ROWS FETCH NEXT ' . $this->QBLimit . ' ROWS ONLY'; } /** diff --git a/system/Database/OCI8/Connection.php b/system/Database/OCI8/Connection.php index b6078febd030..67921d2c13f8 100644 --- a/system/Database/OCI8/Connection.php +++ b/system/Database/OCI8/Connection.php @@ -193,9 +193,14 @@ public function getVersion(): string return $this->dataCache['version']; } - if (! $this->connID || ($versionString = oci_server_version($this->connID)) === false) { + if ($this->connID === false) { + $this->initialize(); + } + + if (($versionString = oci_server_version($this->connID)) === false) { return ''; } + if (preg_match('#Release\s(\d+(?:\.\d+)+)#', $versionString, $match)) { return $this->dataCache['version'] = $match[1]; } diff --git a/system/Database/Postgre/Builder.php b/system/Database/Postgre/Builder.php index 27a72b58c333..8d0d569d5e28 100644 --- a/system/Database/Postgre/Builder.php +++ b/system/Database/Postgre/Builder.php @@ -469,9 +469,8 @@ protected function _upsertBatch(string $table, array $keys, array $values): stri return ($index->type === 'UNIQUE' || $index->type === 'PRIMARY') && $hasAllFields; }); - foreach (array_map(static fn ($index) => $index->fields, $allIndexes) as $index) { - $constraints[] = current($index); - // only one index can be used? + foreach ($allIndexes as $index) { + $constraints = $index->fields; break; } diff --git a/system/Database/Postgre/Connection.php b/system/Database/Postgre/Connection.php index 06ef62a9f842..584c452b233d 100644 --- a/system/Database/Postgre/Connection.php +++ b/system/Database/Postgre/Connection.php @@ -517,6 +517,8 @@ public function insertID() /** * Build a DSN from the provided parameters + * + * @return void */ protected function buildDSN() { diff --git a/system/Database/Query.php b/system/Database/Query.php index f0b75513c6b5..c74ba6a687ab 100644 --- a/system/Database/Query.php +++ b/system/Database/Query.php @@ -287,6 +287,8 @@ public function getOriginalQuery(): string * Escapes and inserts any binds into the finalQueryString property. * * @see https://regex101.com/r/EUEhay/5 + * + * @return void */ protected function compileBinds() { diff --git a/system/Database/QueryInterface.php b/system/Database/QueryInterface.php index 19cbdd0edc4a..86eb01f4e169 100644 --- a/system/Database/QueryInterface.php +++ b/system/Database/QueryInterface.php @@ -26,7 +26,7 @@ interface QueryInterface * * @param mixed $binds * - * @return mixed + * @return $this */ public function setQuery(string $sql, $binds = null, bool $setEscape = true); @@ -34,7 +34,7 @@ public function setQuery(string $sql, $binds = null, bool $setEscape = true); * Returns the final, processed query string after binding, etal * has been performed. * - * @return mixed + * @return string */ public function getQuery(); @@ -43,7 +43,7 @@ public function getQuery(); * for it's start and end values. If no end value is present, will * use the current time to determine total duration. * - * @return mixed + * @return $this */ public function setDuration(float $start, ?float $end = null); @@ -57,6 +57,8 @@ public function getDuration(int $decimals = 6): string; /** * Stores the error description that happened for this query. + * + * @return $this */ public function setError(int $code, string $error); @@ -83,7 +85,7 @@ public function isWriteType(): bool; /** * Swaps out one table prefix for a new one. * - * @return mixed + * @return $this */ public function swapPrefix(string $orig, string $swap); } diff --git a/system/Database/ResultInterface.php b/system/Database/ResultInterface.php index ba26629cd518..e6383cab6585 100644 --- a/system/Database/ResultInterface.php +++ b/system/Database/ResultInterface.php @@ -170,6 +170,8 @@ public function getFieldData(): array; /** * Frees the current result. + * + * @return void */ public function freeResult(); diff --git a/system/Database/SQLSRV/Connection.php b/system/Database/SQLSRV/Connection.php index c8e1e3fbee12..7ef4f31901d1 100644 --- a/system/Database/SQLSRV/Connection.php +++ b/system/Database/SQLSRV/Connection.php @@ -561,11 +561,15 @@ public function getVersion(): string return $this->dataCache['version']; } - if (! $this->connID || ($info = sqlsrv_server_info($this->connID)) === []) { + if (! $this->connID) { $this->initialize(); } - return isset($info['SQLServerVersion']) ? $this->dataCache['version'] = $info['SQLServerVersion'] : false; + if (($info = sqlsrv_server_info($this->connID)) === []) { + return ''; + } + + return isset($info['SQLServerVersion']) ? $this->dataCache['version'] = $info['SQLServerVersion'] : ''; } /** diff --git a/system/Database/SQLite3/Builder.php b/system/Database/SQLite3/Builder.php index 6e62907ac1aa..3aa13edeb96a 100644 --- a/system/Database/SQLite3/Builder.php +++ b/system/Database/SQLite3/Builder.php @@ -151,8 +151,8 @@ protected function _upsertBatch(string $table, array $keys, array $values): stri return ($index->type === 'PRIMARY' || $index->type === 'UNIQUE') && $hasAllFields; }); - foreach (array_map(static fn ($index) => $index->fields, $allIndexes) as $index) { - $constraints[] = current($index); + foreach ($allIndexes as $index) { + $constraints = $index->fields; break; } diff --git a/system/Database/SQLite3/Table.php b/system/Database/SQLite3/Table.php index 095d96a2d333..674d26b7d6f2 100644 --- a/system/Database/SQLite3/Table.php +++ b/system/Database/SQLite3/Table.php @@ -17,8 +17,6 @@ use stdClass; /** - * Class Table - * * Provides missing features for altering tables that are common * in other supported databases, but are missing from SQLite. * These are needed in order to support migrations during testing @@ -345,6 +343,8 @@ protected function createTable() * Copies data from our old table to the new one, * taking care map data correctly based on any columns * that have been renamed. + * + * @return void */ protected function copyData() { @@ -472,6 +472,8 @@ protected function formatKeys($keys) /** * Attempts to drop all indexes and constraints * from the database for this table. + * + * @return void */ protected function dropIndexes() { diff --git a/system/Database/Seeder.php b/system/Database/Seeder.php index 3530592fe671..aacc8e9905ce 100644 --- a/system/Database/Seeder.php +++ b/system/Database/Seeder.php @@ -115,6 +115,8 @@ public static function faker(): ?Generator /** * Loads the specified seeder and runs it. * + * @return void + * * @throws InvalidArgumentException */ public function call(string $class) diff --git a/system/Debug/Toolbar.php b/system/Debug/Toolbar.php index 9a884b60f5b3..7900c7c780c5 100644 --- a/system/Debug/Toolbar.php +++ b/system/Debug/Toolbar.php @@ -512,7 +512,7 @@ protected function format(string $data, string $format = 'html'): string { $data = json_decode($data, true); - if ($this->config->maxHistory !== 0 && preg_match('/\d+\.\d{6}/s', (string) service('request')->getGet('debugbar_time'), $debugbarTime)) { + if (preg_match('/\d+\.\d{6}/s', (string) service('request')->getGet('debugbar_time'), $debugbarTime)) { $history = new History(); $history->setFiles( $debugbarTime[0], diff --git a/system/Debug/Toolbar/Views/toolbar.css b/system/Debug/Toolbar/Views/toolbar.css index b9f13c51ff2e..abf61e9cca93 100644 --- a/system/Debug/Toolbar/Views/toolbar.css +++ b/system/Debug/Toolbar/Views/toolbar.css @@ -332,6 +332,26 @@ display: none !important; } } +@media screen and (max-width: 768px) { + #debug-bar table { + display: block; + overflow-x: auto; + font-size: 12px; + margin: 5px 5px 10px 5px; + } + #debug-bar table td, + #debug-bar table th { + padding: 4px 6px; + } + #debug-bar .timeline { + display: block; + white-space: nowrap; + font-size: 12px; + } + #debug-bar .toolbar { + overflow-x: auto; + } +} #debug-icon { background-color: #FFFFFF; box-shadow: 0 0 4px #DFDFDF; diff --git a/system/Debug/Toolbar/Views/toolbar.js b/system/Debug/Toolbar/Views/toolbar.js index 54fe3d69f106..ddaa2386f260 100644 --- a/system/Debug/Toolbar/Views/toolbar.js +++ b/system/Debug/Toolbar/Views/toolbar.js @@ -27,22 +27,25 @@ var ciDebugBar = { .getElementById("debug-icon-link") .addEventListener("click", ciDebugBar.toggleToolbar, true); - // Allows to highlight the row of the current history request - var btn = this.toolbar.querySelector( - 'button[data-time="' + localStorage.getItem("debugbar-time") + '"]' - ); - ciDebugBar.addClass(btn.parentNode.parentNode, "current"); - historyLoad = this.toolbar.getElementsByClassName("ci-history-load"); - for (var i = 0; i < historyLoad.length; i++) { - historyLoad[i].addEventListener( - "click", - function () { - loadDoc(this.getAttribute("data-time")); - }, - true + if (historyLoad.length) { + // Allows highlighting the row of the current history request + var btn = this.toolbar.querySelector( + 'button[data-time="' + localStorage.getItem("debugbar-time-new") + '"]' ); + ciDebugBar.addClass(btn.parentNode.parentNode, "current"); + + + for (var i = 0; i < historyLoad.length; i++) { + historyLoad[i].addEventListener( + "click", + function () { + loadDoc(this.getAttribute("data-time")); + }, + true + ); + } } // Display the active Tab on page load @@ -77,14 +80,14 @@ var ciDebugBar = { links[i].addEventListener("click", function() { ciDebugBar.toggleDataTable(datatable) }, true); - + } else if (toggleData === "childrows") { let child = links[i].getAttribute("data-child"); links[i].addEventListener("click", function() { ciDebugBar.toggleChildRows(child) }, true); - + } else { links[i].addEventListener("click", ciDebugBar.toggleRows, true); } @@ -174,10 +177,10 @@ var ciDebugBar = { ); if (target.classList.contains("debug-bar-ndisplay")) { - ciDebugBar.switchClass(target, "debug-bar-ndisplay", "debug-bar-dtableRow"); + ciDebugBar.switchClass(target, "debug-bar-ndisplay", "debug-bar-dtableRow"); } else { ciDebugBar.switchClass(target, "debug-bar-dtableRow", "debug-bar-ndisplay"); - } + } } }, @@ -261,7 +264,7 @@ var ciDebugBar = { } else { ciDebugBar.switchClass(ciDebugBar.icon, "debug-bar-dinlineBlock", "debug-bar-ndisplay"); ciDebugBar.switchClass(ciDebugBar.toolbar, "debug-bar-ndisplay", "debug-bar-dinlineBlock"); - } + } }, toggleViewsHints: function () { diff --git a/system/Debug/Toolbar/Views/toolbar.tpl.php b/system/Debug/Toolbar/Views/toolbar.tpl.php index 8179c1a8e631..0768f62915bb 100644 --- a/system/Debug/Toolbar/Views/toolbar.tpl.php +++ b/system/Debug/Toolbar/Views/toolbar.tpl.php @@ -1,21 +1,24 @@