diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 975c611a61c3..17e6b415ba0d 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -25,10 +25,10 @@ body: description: Which PHP versions did you run your code? multiple: true options: - - '7.3' - '7.4' - '8.0' - '8.1' + - '8.2' validations: required: true diff --git a/.github/scripts/deploy-userguide b/.github/scripts/deploy-userguide index abbb768a5017..b67c1da94c13 100755 --- a/.github/scripts/deploy-userguide +++ b/.github/scripts/deploy-userguide @@ -23,6 +23,12 @@ cd $TARGET git checkout master rm -rf docs +# Copy common files +cp -Rf ${SOURCE}/LICENSE ./ + +# Copy repo-specific files +cp -Rf ${SOURCE}/admin/userguide/. ./ + # Copy files cp -Rf ${SOURCE}/user_guide_src/build/html ./docs cp -Rf ${SOURCE}/user_guide_src/build/epub/CodeIgniter.epub ./CodeIgniter${VERSION}.epub diff --git a/.github/workflows/deploy-apidocs.yml b/.github/workflows/deploy-apidocs.yml index 41633244cb52..b117e3dad72b 100644 --- a/.github/workflows/deploy-apidocs.yml +++ b/.github/workflows/deploy-apidocs.yml @@ -11,9 +11,14 @@ on: - 'system/**' - '.github/workflows/deploy-apidocs.yml' +permissions: + contents: read + jobs: build: name: Deploy to api + permissions: + contents: write if: github.repository == 'codeigniter4/CodeIgniter4' runs-on: ubuntu-22.04 diff --git a/.github/workflows/deploy-distributables.yml b/.github/workflows/deploy-distributables.yml index d00ebc5a9f36..e279abad2dd7 100644 --- a/.github/workflows/deploy-distributables.yml +++ b/.github/workflows/deploy-distributables.yml @@ -6,6 +6,9 @@ on: release: types: [published] +permissions: + contents: read + jobs: check-version: name: Check for updated version @@ -31,6 +34,9 @@ jobs: framework: name: Deploy to framework + permissions: + # Allow actions/github-script to create release + contents: write if: github.repository == 'codeigniter4/CodeIgniter4' runs-on: ubuntu-22.04 needs: check-version @@ -78,6 +84,9 @@ jobs: appstarter: name: Deploy to appstarter + permissions: + # Allow actions/github-script to create release + contents: write if: github.repository == 'codeigniter4/CodeIgniter4' runs-on: ubuntu-22.04 needs: check-version @@ -125,6 +134,9 @@ jobs: userguide: name: Deploy to userguide + permissions: + # Allow actions/github-script to create release + contents: write if: github.repository == 'codeigniter4/CodeIgniter4' runs-on: ubuntu-22.04 needs: check-version diff --git a/.github/workflows/deploy-userguide-latest.yml b/.github/workflows/deploy-userguide-latest.yml index 4ba79a8b341b..58e5e731ed18 100644 --- a/.github/workflows/deploy-userguide-latest.yml +++ b/.github/workflows/deploy-userguide-latest.yml @@ -12,9 +12,15 @@ on: paths: - 'user_guide_src/**' +permissions: + contents: read + jobs: build: name: Deploy to gh-pages + permissions: + # Allow ad-m/github-push-action to push commit to branch gh-pages + contents: write if: (github.repository == 'codeigniter4/CodeIgniter4') runs-on: ubuntu-latest steps: diff --git a/.github/workflows/test-autoreview.yml b/.github/workflows/test-autoreview.yml index b78180249e73..dfe4892bdebe 100644 --- a/.github/workflows/test-autoreview.yml +++ b/.github/workflows/test-autoreview.yml @@ -18,6 +18,9 @@ concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} cancel-in-progress: true +permissions: + contents: read + jobs: auto-review-tests: uses: ./.github/workflows/reusable-serviceless-phpunit-test.yml # @TODO Extract to codeigniter4/.github repo diff --git a/.github/workflows/test-coding-standards.yml b/.github/workflows/test-coding-standards.yml index 99e6bd4a3b14..a38e3ab85cd9 100644 --- a/.github/workflows/test-coding-standards.yml +++ b/.github/workflows/test-coding-standards.yml @@ -16,6 +16,9 @@ concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} cancel-in-progress: true +permissions: + contents: read + jobs: lint: name: PHP ${{ matrix.php-version }} Lint with PHP CS Fixer diff --git a/.github/workflows/test-deptrac.yml b/.github/workflows/test-deptrac.yml index e756f83ebcd7..e6b92346972b 100644 --- a/.github/workflows/test-deptrac.yml +++ b/.github/workflows/test-deptrac.yml @@ -28,6 +28,9 @@ concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} cancel-in-progress: true +permissions: + contents: read + jobs: build: name: Architectural Inspection @@ -73,3 +76,5 @@ jobs: run: | sudo phive --no-progress install --global qossmic/deptrac --trust-gpg-keys B8F640134AB1782E deptrac analyze --cache-file=build/deptrac.cache + env: + GITHUB_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/test-phpcpd.yml b/.github/workflows/test-phpcpd.yml index 9fe3764dfcb1..c4365f7b1e63 100644 --- a/.github/workflows/test-phpcpd.yml +++ b/.github/workflows/test-phpcpd.yml @@ -27,6 +27,9 @@ concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} cancel-in-progress: true +permissions: + contents: read + jobs: build: name: Duplicate Code Detection diff --git a/.github/workflows/test-phpstan.yml b/.github/workflows/test-phpstan.yml index 796d78bdba9a..cb793c356d94 100644 --- a/.github/workflows/test-phpstan.yml +++ b/.github/workflows/test-phpstan.yml @@ -31,6 +31,9 @@ concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} cancel-in-progress: true +permissions: + contents: read + jobs: build: name: PHP ${{ matrix.php-versions }} Static Analysis diff --git a/.github/workflows/test-phpunit.yml b/.github/workflows/test-phpunit.yml index 7c6182cac13c..66dc091f7b0d 100644 --- a/.github/workflows/test-phpunit.yml +++ b/.github/workflows/test-phpunit.yml @@ -31,6 +31,9 @@ concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} cancel-in-progress: true +permissions: + contents: read + jobs: # Any environment variables set in an env context defined at the workflow level # in the caller workflow are not propagated to the called workflow. diff --git a/.github/workflows/test-rector.yml b/.github/workflows/test-rector.yml index 7d8c11d61362..89f211120d0d 100644 --- a/.github/workflows/test-rector.yml +++ b/.github/workflows/test-rector.yml @@ -33,6 +33,9 @@ concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} cancel-in-progress: true +permissions: + contents: read + jobs: build: name: PHP ${{ matrix.php-versions }} Analyze code (Rector) on ${{ matrix.paths }} diff --git a/.github/workflows/test-scss.yml b/.github/workflows/test-scss.yml index 797bd3e42ea9..26a679719156 100644 --- a/.github/workflows/test-scss.yml +++ b/.github/workflows/test-scss.yml @@ -23,6 +23,9 @@ concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} cancel-in-progress: true +permissions: + contents: read + jobs: build: name: Compilation of SCSS (Dart Sass) diff --git a/.github/workflows/test-userguide.yml b/.github/workflows/test-userguide.yml index d3cc4c9d25ea..b188080996db 100644 --- a/.github/workflows/test-userguide.yml +++ b/.github/workflows/test-userguide.yml @@ -14,6 +14,9 @@ concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} cancel-in-progress: true +permissions: + contents: read + jobs: syntax_check: name: Check User Guide syntax diff --git a/CHANGELOG.md b/CHANGELOG.md index 18f45d61580c..7837363ab950 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,31 @@ # Changelog -## [v4.2.11](https://github.com/codeigniter4/CodeIgniter4/tree/v4.2.10) (2022-12-21) +## [v4.2.12](https://github.com/codeigniter4/CodeIgniter4/tree/v4.2.12) (2023-01-09) +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.2.11...v4.2.12) + +### Fixed Bugs +* docs: fix request.rst by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7014 +* fix: `link_tag()` missing `type="application/rss+xml"` by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7022 +* fix: Request::getIPaddress() causes error on CLI by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7030 +* docs: fix upgrade_database.rst by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7036 +* fix: `spark migrate:status` shows incorrect filename when format is `Y_m_d_His_` by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7038 +* fix: Model::save() object when useAutoIncrement is disabled by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/7042 +* fix: define of STDOUT in CLI init() method by @jozefrebjak in https://github.com/codeigniter4/CodeIgniter4/pull/7052 +* fix: change `getFile()` function of \CodeIgniter\Events\Events to static. by @ping-yee in https://github.com/codeigniter4/CodeIgniter4/pull/7046 +* fix: [Email] add fallback to use gethostname() by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7053 +* Fixing bug with legacy autoRoute when testing by @baycik in https://github.com/codeigniter4/CodeIgniter4/pull/7060 + +### Refactoring +* refactor: RequestTrait by rector by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7006 +* refactor: update sass output by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7026 + +## [v4.2.11](https://github.com/codeigniter4/CodeIgniter4/tree/v4.2.11) (2022-12-21) [Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.2.10...v4.2.11) +### SECURITY +* *Attackers may spoof IP address when using proxy* was fixed. See the [Security advisory](https://github.com/codeigniter4/CodeIgniter4/security/advisories/GHSA-ghw3-5qvm-3mqc) for more information. +* *Potential Session Handlers Vulnerability* was fixed. See the [Security advisory](https://github.com/codeigniter4/CodeIgniter4/security/advisories/GHSA-6cq5-8cj7-g558) for more information. + ### Fixed Bugs * fix: Request::getIPAddress() by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6820 * fix: Model cannot insert when $useAutoIncrement is false by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6827 @@ -77,6 +100,9 @@ ## [v4.2.7](https://github.com/codeigniter4/CodeIgniter4/tree/v4.2.7) (2022-10-06) [Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.2.6...v4.2.7) +### SECURITY +* *Secure or HttpOnly flag set in Config\Cookie is not reflected in Cookies issued* was fixed. See the [Security advisory](https://github.com/codeigniter4/CodeIgniter4/security/advisories/GHSA-745p-r637-7vvp) for more information. + ### Breaking Changes * fix: make Time::__toString() database-compatible on any locale by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6461 * fix: set_cookie() does not use Config\Cookie values by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6544 diff --git a/LICENSE b/LICENSE index 31425034e8e9..0119e5f554b3 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,7 @@ The MIT License (MIT) Copyright (c) 2014-2019 British Columbia Institute of Technology -Copyright (c) 2019-2022 CodeIgniter Foundation +Copyright (c) 2019-2023 CodeIgniter Foundation Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/admin/userguide/.github/scripts/deploy b/admin/userguide/.github/scripts/deploy new file mode 100755 index 000000000000..0347c004dfff --- /dev/null +++ b/admin/userguide/.github/scripts/deploy @@ -0,0 +1,15 @@ +#!/bin/sh -e + +# Deploys the User Guide to the production +# website. Triggered by updates to the GitHub +# repo's master branch. + +REPO=/opt/userguide +SITE=/home/public_html/userguides/userguide4 + +cd "$REPO" +git switch master +git pull + +rm -rf "$SITE" +cp -R "$REPO/docs" "$SITE" diff --git a/admin/userguide/.github/workflows/deploy.yml b/admin/userguide/.github/workflows/deploy.yml new file mode 100644 index 000000000000..ac8027fc7410 --- /dev/null +++ b/admin/userguide/.github/workflows/deploy.yml @@ -0,0 +1,22 @@ +# Deploys the User Guide to the production +# website whenever master branch is updated +name: Deploy Production + +on: + push: + branches: + - master + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: executing remote ssh commands using ssh key + uses: appleboy/ssh-action@master + with: + host: ${{ secrets.HOST }} + username: ${{ secrets.USERNAME }} + key: ${{ secrets.KEY }} + port: ${{ secrets.PORT }} + script: /opt/userguide/.github/scripts/deploy diff --git a/admin/userguide/.gitignore b/admin/userguide/.gitignore new file mode 100644 index 000000000000..d0eba7f4bdaa --- /dev/null +++ b/admin/userguide/.gitignore @@ -0,0 +1,13 @@ +*/config/development +*/logs/log-*.php +!*/logs/index.html +*/cache/* +!*/cache/index.html +!*/cache/.htaccess + +user_guide_src/build/* +user_guide_src/cilexer/build/* +user_guide_src/cilexer/dist/* +user_guide_src/cilexer/pycilexer.egg-info/* + +/vendor/ diff --git a/admin/userguide/README.md b/admin/userguide/README.md index 10d85692bc38..0ea07075db61 100644 --- a/admin/userguide/README.md +++ b/admin/userguide/README.md @@ -1,20 +1,15 @@ # CodeIgniter 4 User Guide -## What is CodeIgniter? - -CodeIgniter is a PHP full-stack web framework that is light, fast, flexible and secure. +CodeIgniter 4 is a PHP web framework that is light, fast, flexible, and secure. More information can be found at the [official site](http://codeigniter.com). -This repository holds a composer-installable pre-built user guide for the framework. -It has been built from the -[development repository](https://github.com/codeigniter4/CodeIgniter4). - -More information about the plans for version 4 can be found in [the announcement](http://forum.codeigniter.com/thread-62615.html) on the forums. - -## Installation & updates +**This is a read-only repository used to publish the user guide for the current release.** +It is built automatically as part of the framework release workflow, and pull +requests are not accepted here. -`composer require codeigniter4/userguide` will install a copy -of the user guide inside your project, at -`vendor/codeigniter4/userguide`. You can then `composer update` whenever -there is a new release of the framework. +Development is done in the [main repository](https://github.com/codeigniter4/codeigniter4). +If you find problems with the user guide, please submit a correcting pull request there. +If you feel that features are missing or unclear, please comment on our +[forum](https://forum.codeigniter.com/index.php), +in the appropriate CodeIgniter4 subforum. diff --git a/admin/userguide/composer.json b/admin/userguide/composer.json deleted file mode 100644 index 007664b9b2ec..000000000000 --- a/admin/userguide/composer.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "codeigniter4/userguide", - "type": "project", - "description": "CodeIgniter4 user guide", - "homepage": "https://codeigniter.com", - "license": "MIT", - "require": { - "php": "^7.4 || ^8.0", - "codeigniter4/framework": "^4" - }, - "support": { - "forum": "https://forum.codeigniter.com/", - "source": "https://github.com/codeigniter4/CodeIgniter4", - "slack": "https://codeigniterchat.slack.com" - } -} diff --git a/app/Config/Cache.php b/app/Config/Cache.php index 2d1fea90c9d1..e76c02d8ff0b 100644 --- a/app/Config/Cache.php +++ b/app/Config/Cache.php @@ -134,7 +134,7 @@ class Cache extends BaseConfig * * @see https://codeigniter.com/user_guide/libraries/caching.html#memcached * - * @var array + * @var array */ public $memcached = [ 'host' => '127.0.0.1', diff --git a/app/Config/Migrations.php b/app/Config/Migrations.php index 91e80b4a9074..604e059a1cf7 100644 --- a/app/Config/Migrations.php +++ b/app/Config/Migrations.php @@ -42,9 +42,12 @@ class Migrations extends BaseConfig * * This is the format that will be used when creating new migrations * using the CLI command: - * > php spark migrate:create + * > php spark make:migration * - * Typical formats: + * Note: if you set an unsupported format, migration runner will not find + * your migration files. + * + * Supported formats: * - YmdHis_ * - Y-m-d-His_ * - Y_m_d_His_ diff --git a/app/Controllers/BaseController.php b/app/Controllers/BaseController.php index 122db5f9a9f2..ecea16206654 100644 --- a/app/Controllers/BaseController.php +++ b/app/Controllers/BaseController.php @@ -37,6 +37,12 @@ abstract class BaseController extends Controller */ protected $helpers = []; + /** + * Be sure to declare properties for any property fetch you initialized. + * The creation of dynamic property is deprecated in PHP 8.2. + */ + // protected $session; + /** * Constructor. */ diff --git a/composer.json b/composer.json index 3fb81f249591..af045e7968f7 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,7 @@ "phpunit/phpcov": "^8.2", "phpunit/phpunit": "^9.1", "predis/predis": "^1.1 || ^2.0", - "rector/rector": "0.15.1" + "rector/rector": "0.15.2" }, "suggest": { "ext-curl": "If you use CURLRequest class", diff --git a/contributing/documentation.rst b/contributing/documentation.rst index 34b1c90ca52c..fdffa1611a98 100644 --- a/contributing/documentation.rst +++ b/contributing/documentation.rst @@ -130,3 +130,18 @@ To a Method ============= :php:meth:`CodeIgniter\\HTTP\\Response::setCookie()` + +**************** +Other Directives +**************** + +New Feature +=========== + + .. versionadded:: 4.3.0 + +Deprecated +========== + + .. deprecated:: 4.3.0 + Use :php:meth:`CodeIgniter\\Database\\BaseBuilder::setData()` instead. diff --git a/system/CLI/CLI.php b/system/CLI/CLI.php index ff1d32995a0b..f3488f50265a 100644 --- a/system/CLI/CLI.php +++ b/system/CLI/CLI.php @@ -161,9 +161,10 @@ public static function init() static::parseCommandLine(); static::$initialized = true; - } else { + } elseif (! defined('STDOUT')) { // If the command is being called from a controller // we need to define STDOUT ourselves + // For "! defined('STDOUT')" see: https://github.com/codeigniter4/CodeIgniter4/issues/7047 define('STDOUT', 'php://output'); // @codeCoverageIgnore } } diff --git a/system/Cache/CacheInterface.php b/system/Cache/CacheInterface.php index 72b3626d94cf..d50322e778f6 100644 --- a/system/Cache/CacheInterface.php +++ b/system/Cache/CacheInterface.php @@ -26,16 +26,16 @@ public function initialize(); * * @param string $key Cache item name * - * @return mixed + * @return array|bool|float|int|object|string|null */ public function get(string $key); /** * Saves an item to the cache store. * - * @param string $key Cache item name - * @param mixed $value The data to save - * @param int $ttl Time To Live, in seconds (default 60) + * @param string $key Cache item name + * @param array|bool|float|int|object|string|null $value The data to save + * @param int $ttl Time To Live, in seconds (default 60) * * @return bool Success or failure */ @@ -56,7 +56,7 @@ public function delete(string $key); * @param string $key Cache ID * @param int $offset Step/value to increase by * - * @return mixed + * @return bool|int */ public function increment(string $key, int $offset = 1); @@ -66,7 +66,7 @@ public function increment(string $key, int $offset = 1); * @param string $key Cache ID * @param int $offset Step/value to increase by * - * @return mixed + * @return bool|int */ public function decrement(string $key, int $offset = 1); @@ -83,7 +83,7 @@ public function clean(); * The information returned and the structure of the data * varies depending on the handler. * - * @return mixed + * @return array|false|object|null */ public function getCacheInfo(); diff --git a/system/Cache/Handlers/BaseHandler.php b/system/Cache/Handlers/BaseHandler.php index 57120208ceeb..0c5ef170c9e0 100644 --- a/system/Cache/Handlers/BaseHandler.php +++ b/system/Cache/Handlers/BaseHandler.php @@ -77,7 +77,7 @@ public static function validateKey($key, $prefix = ''): string * @param int $ttl Time to live * @param Closure $callback Callback return value * - * @return mixed + * @return array|bool|float|int|object|string|null */ public function remember(string $key, int $ttl, Closure $callback) { diff --git a/system/CodeIgniter.php b/system/CodeIgniter.php index e7076c4ba167..79c9ebbf5eae 100644 --- a/system/CodeIgniter.php +++ b/system/CodeIgniter.php @@ -47,7 +47,7 @@ class CodeIgniter /** * The current version of CodeIgniter Framework */ - public const CI_VERSION = '4.2.11'; + public const CI_VERSION = '4.2.12'; /** * App startup time. diff --git a/system/Database/MigrationRunner.php b/system/Database/MigrationRunner.php index 6eb92125237f..569be22807e0 100644 --- a/system/Database/MigrationRunner.php +++ b/system/Database/MigrationRunner.php @@ -67,7 +67,7 @@ class MigrationRunner * * @var string */ - protected $regex = '/^\d{4}[_-]?\d{2}[_-]?\d{2}[_-]?\d{6}_(\w+)$/'; + protected $regex = '/\A(\d{4}[_-]?\d{2}[_-]?\d{2}[_-]?\d{6})_(\w+)\z/'; /** * The main database connection. Used to store @@ -448,6 +448,8 @@ public function findNamespaceMigrations(string $namespace): array /** * Create a migration object from a file path. * + * @param string $path Full path to a valid migration file. + * * @return false|object Returns the migration object, or false on failure */ protected function migrationFromFile(string $path, string $namespace) @@ -456,9 +458,9 @@ protected function migrationFromFile(string $path, string $namespace) return false; } - $name = basename($path, '.php'); + $filename = basename($path, '.php'); - if (! preg_match($this->regex, $name)) { + if (! preg_match($this->regex, $filename)) { return false; } @@ -466,8 +468,8 @@ protected function migrationFromFile(string $path, string $namespace) $migration = new stdClass(); - $migration->version = $this->getMigrationNumber($name); - $migration->name = $this->getMigrationName($name); + $migration->version = $this->getMigrationNumber($filename); + $migration->name = $this->getMigrationName($filename); $migration->path = $path; $migration->class = $locator->getClassname($path); $migration->namespace = $namespace; @@ -525,23 +527,29 @@ public function setSilent(bool $silent) /** * Extracts the migration number from a filename + * + * @param string $migration A migration filename w/o path. */ protected function getMigrationNumber(string $migration): string { - preg_match('/^\d{4}[_-]?\d{2}[_-]?\d{2}[_-]?\d{6}/', $migration, $matches); + preg_match($this->regex, $migration, $matches); - return count($matches) ? $matches[0] : '0'; + return count($matches) ? $matches[1] : '0'; } /** - * Extracts the migration class name from a filename + * Extracts the migration name from a filename + * + * Note: The migration name should be the classname, but maybe they are + * different. + * + * @param string $migration A migration filename w/o path. */ protected function getMigrationName(string $migration): string { - $parts = explode('_', $migration); - array_shift($parts); + preg_match($this->regex, $migration, $matches); - return implode('_', $parts); + return count($matches) ? $matches[2] : ''; } /** diff --git a/system/Debug/Toolbar/Views/toolbar.css b/system/Debug/Toolbar/Views/toolbar.css index 4cb379cbb4cc..744d9392c2be 100644 --- a/system/Debug/Toolbar/Views/toolbar.css +++ b/system/Debug/Toolbar/Views/toolbar.css @@ -427,8 +427,8 @@ -webkit-box-shadow: 0 0 4px #DFDFDF; } #debug-icon a:active, -#debug-icon a:link, -#debug-icon a:visited { + #debug-icon a:link, + #debug-icon a:visited { color: #DD8615; } #debug-bar { @@ -436,17 +436,17 @@ color: #DFDFDF; } #debug-bar h1, -#debug-bar h2, -#debug-bar h3, -#debug-bar p, -#debug-bar a, -#debug-bar button, -#debug-bar table, -#debug-bar thead, -#debug-bar tr, -#debug-bar td, -#debug-bar button, -#debug-bar .toolbar { + #debug-bar h2, + #debug-bar h3, + #debug-bar p, + #debug-bar a, + #debug-bar button, + #debug-bar table, + #debug-bar thead, + #debug-bar tr, + #debug-bar td, + #debug-bar button, + #debug-bar .toolbar { background-color: transparent; color: #DFDFDF; } @@ -498,7 +498,7 @@ color: #DFDFDF; } #debug-bar #toolbar-position, -#debug-bar #toolbar-theme { + #debug-bar #toolbar-theme { filter: brightness(0) invert(0.6); } #debug-bar .ci-label.active { @@ -518,7 +518,7 @@ -webkit-box-shadow: 0 -1px 4px #434343; } #debug-bar .timeline th, -#debug-bar .timeline td { + #debug-bar .timeline td { border-color: #434343; } #debug-bar .timeline .timer { diff --git a/system/Email/Email.php b/system/Email/Email.php index 384d22fcc63b..dffe4e0e6803 100644 --- a/system/Email/Email.php +++ b/system/Email/Email.php @@ -2101,6 +2101,11 @@ protected function getHostname() return '[' . $_SERVER['SERVER_ADDR'] . ']'; } + $hostname = gethostname(); + if ($hostname !== false) { + return $hostname; + } + return '[127.0.0.1]'; } diff --git a/system/Encryption/Encryption.php b/system/Encryption/Encryption.php index 35eb323b0469..81b25eeff5f5 100644 --- a/system/Encryption/Encryption.php +++ b/system/Encryption/Encryption.php @@ -71,7 +71,7 @@ class Encryption /** * Handlers that are to be installed * - * @var array + * @var array */ protected $handlers = []; diff --git a/system/Events/Events.php b/system/Events/Events.php index 3117969882df..064f01ab4104 100644 --- a/system/Events/Events.php +++ b/system/Events/Events.php @@ -246,7 +246,7 @@ public static function setFiles(array $files) * * @return string[] */ - public function getFiles() + public static function getFiles() { return static::$files; } diff --git a/system/HTTP/RequestTrait.php b/system/HTTP/RequestTrait.php index a9d55224c557..5dc0359b3488 100644 --- a/system/HTTP/RequestTrait.php +++ b/system/HTTP/RequestTrait.php @@ -44,7 +44,7 @@ trait RequestTrait /** * Gets the user's IP address. * - * @return string IP address if it can be detected, or empty string. + * @return string IP address if it can be detected. * If the IP address is not a valid IP address, * then will return '0.0.0.0'. */ @@ -64,17 +64,20 @@ public function getIPAddress(): string */ // @phpstan-ignore-next-line $proxyIPs = $this->proxyIPs ?? config('App')->proxyIPs; - if (! empty($proxyIPs)) { - // @phpstan-ignore-next-line - if (! is_array($proxyIPs) || is_int(array_key_first($proxyIPs))) { - throw new ConfigException( - 'You must set an array with Proxy IP address key and HTTP header name value in Config\App::$proxyIPs.' - ); - } + // @phpstan-ignore-next-line + if (! empty($proxyIPs) && (! is_array($proxyIPs) || is_int(array_key_first($proxyIPs)))) { + throw new ConfigException( + 'You must set an array with Proxy IP address key and HTTP header name value in Config\App::$proxyIPs.' + ); } $this->ipAddress = $this->getServer('REMOTE_ADDR'); + // If this is a CLI request, $this->ipAddress is null. + if ($this->ipAddress === null) { + return $this->ipAddress = '0.0.0.0'; + } + if ($proxyIPs) { // @TODO Extract all this IP address logic to another class. foreach ($proxyIPs as $proxyIP => $header) { @@ -153,7 +156,7 @@ public function getIPAddress(): string return $this->ipAddress = '0.0.0.0'; } - return empty($this->ipAddress) ? '' : $this->ipAddress; + return $this->ipAddress; } /** diff --git a/system/Helpers/html_helper.php b/system/Helpers/html_helper.php index 6c04a86a56b6..c87c700256ff 100755 --- a/system/Helpers/html_helper.php +++ b/system/Helpers/html_helper.php @@ -222,13 +222,20 @@ function script_tag($src = '', bool $indexPage = false): string /** * Link * - * Generates link to a CSS file + * Generates link tag * - * @param array|string $href Stylesheet href or an array - * @param bool $indexPage should indexPage be added to the CSS path. + * @param array|string $href Stylesheet href or an array + * @param bool $indexPage should indexPage be added to the CSS path. */ - function link_tag($href = '', string $rel = 'stylesheet', string $type = 'text/css', string $title = '', string $media = '', bool $indexPage = false, string $hreflang = ''): string - { + function link_tag( + $href = '', + string $rel = 'stylesheet', + string $type = 'text/css', + string $title = '', + string $media = '', + bool $indexPage = false, + string $hreflang = '' + ): string { $link = 'useAutoIncrement === false && isset($data[$this->primaryKey])) { - $this->tempPrimaryKeyValue = $data[$this->primaryKey]; + if ($this->useAutoIncrement === false) { + if (is_array($data) && isset($data[$this->primaryKey])) { + $this->tempPrimaryKeyValue = $data[$this->primaryKey]; + } elseif (is_object($data) && isset($data->{$this->primaryKey})) { + $this->tempPrimaryKeyValue = $data->{$this->primaryKey}; + } } $this->escape = $this->tempData['escape'] ?? []; diff --git a/system/Router/Router.php b/system/Router/Router.php index 0be0e4383032..8593cdfd0266 100644 --- a/system/Router/Router.php +++ b/system/Router/Router.php @@ -574,8 +574,6 @@ public function setDirectory(?string $dir = null, bool $append = false, bool $va { if (empty($dir)) { $this->directory = null; - - return; } if ($this->autoRouter instanceof AutoRouter) { diff --git a/system/Test/Interfaces/FabricatorModel.php b/system/Test/Interfaces/FabricatorModel.php index 04976a07d24c..7173838a6a2e 100644 --- a/system/Test/Interfaces/FabricatorModel.php +++ b/system/Test/Interfaces/FabricatorModel.php @@ -67,7 +67,7 @@ public function insert($data = null, bool $returnID = true); * Sets $useSoftDeletes value so that we can temporarily override * the softdeletes settings. Can be used for all find* methods. * - * @param boolean $val + * @param bool $val * * @return Model */ diff --git a/tests/_support/Database/Seeds/CITestSeeder.php b/tests/_support/Database/Seeds/CITestSeeder.php index fb43d37a9ff4..9353578246b5 100644 --- a/tests/_support/Database/Seeds/CITestSeeder.php +++ b/tests/_support/Database/Seeds/CITestSeeder.php @@ -150,7 +150,7 @@ public function run() if ($this->db->DBDriver === 'MySQLi') { $data['ci_sessions'][] = [ - 'id' => '1f5o06b43phsnnf8if6bo33b635e4p2o', + 'id' => 'ci_session:1f5o06b43phsnnf8if6bo33b635e4p2o', 'ip_address' => '127.0.0.1', 'timestamp' => '2021-06-25 21:54:14', 'data' => '__ci_last_regenerate|i:1624650854;_ci_previous_url|s:40:\"http://localhost/index.php/home/index\";', @@ -159,7 +159,7 @@ public function run() if ($this->db->DBDriver === 'Postgre') { $data['ci_sessions'][] = [ - 'id' => '1f5o06b43phsnnf8if6bo33b635e4p2o', + 'id' => 'ci_session:1f5o06b43phsnnf8if6bo33b635e4p2o', 'ip_address' => '127.0.0.1', 'timestamp' => '2021-06-25 21:54:14.991403+02', 'data' => '\x' . bin2hex('__ci_last_regenerate|i:1624650854;_ci_previous_url|s:40:\"http://localhost/index.php/home/index\";'), diff --git a/tests/system/Database/Migrations/MigrationRunnerTest.php b/tests/system/Database/Migrations/MigrationRunnerTest.php index 28d9a2301fe5..9872080b8719 100644 --- a/tests/system/Database/Migrations/MigrationRunnerTest.php +++ b/tests/system/Database/Migrations/MigrationRunnerTest.php @@ -186,6 +186,24 @@ public function testGetMigrationNumberReturnsZeroIfNoneFound() $this->assertSame('0', $method('Foo')); } + public function testGetMigrationNameDashes() + { + $runner = new MigrationRunner($this->config); + + $method = $this->getPrivateMethodInvoker($runner, 'getMigrationName'); + + $this->assertSame('Foo_bar', $method('2019-08-06-235100_Foo_bar')); + } + + public function testGetMigrationNameUnderscores() + { + $runner = new MigrationRunner($this->config); + + $method = $this->getPrivateMethodInvoker($runner, 'getMigrationName'); + + $this->assertSame('Foo_bar', $method('2019_08_06_235100_Foo_bar')); + } + public function testSetSilentStoresValue() { $runner = new MigrationRunner($this->config); diff --git a/tests/system/Events/EventsTest.php b/tests/system/Events/EventsTest.php index b6a8aa1a4cb7..1a106aca3548 100644 --- a/tests/system/Events/EventsTest.php +++ b/tests/system/Events/EventsTest.php @@ -55,21 +55,21 @@ public function testInitialize() // it should start out empty MockEvents::setFiles([]); - $this->assertEmpty($this->manager->getFiles()); + $this->assertEmpty(Events::getFiles()); // make sure we have a default events file $default = [APPPATH . 'Config' . DIRECTORY_SEPARATOR . 'Events.php']; $this->manager->unInitialize(); MockEvents::initialize(); - $this->assertSame($default, $this->manager->getFiles()); + $this->assertSame($default, Events::getFiles()); // but we should be able to change it through the backdoor MockEvents::setFiles(['/peanuts']); - $this->assertSame(['/peanuts'], $this->manager->getFiles()); + $this->assertSame(['/peanuts'], Events::getFiles()); // re-initializing should have no effect MockEvents::initialize(); - $this->assertSame(['/peanuts'], $this->manager->getFiles()); + $this->assertSame(['/peanuts'], Events::getFiles()); } public function testPerformance() diff --git a/tests/system/Helpers/HTMLHelperTest.php b/tests/system/Helpers/HTMLHelperTest.php index 6c59b8ad1522..de141881107a 100755 --- a/tests/system/Helpers/HTMLHelperTest.php +++ b/tests/system/Helpers/HTMLHelperTest.php @@ -312,11 +312,71 @@ public function testLinkTag() $this->assertSame($expected, link_tag($target)); } - public function testLinkTagComplete() + public function testLinkTagMedia() { - $target = 'https://styles.com/css/mystyles.css'; - $expected = ''; - $this->assertSame($expected, link_tag($target, 'banana', 'fruit', 'Go away', 'VHS')); + $target = 'https://styles.com/css/mystyles.css'; + $tag = link_tag($target, 'stylesheet', 'text/css', '', 'print'); + + $expected = ''; + $this->assertSame($expected, $tag); + } + + public function testLinkTagTitle() + { + $tag = link_tag('default.css', 'stylesheet', 'text/css', 'Default Style'); + + $expected = ''; + $this->assertSame($expected, $tag); + } + + public function testLinkTagFavicon() + { + $tag = link_tag('favicon.ico', 'shortcut icon', 'image/ico'); + + $expected = ''; + $this->assertSame($expected, $tag); + } + + public function testLinkTagRss() + { + $tag = link_tag('feed', 'alternate', 'application/rss+xml', 'My RSS Feed'); + + $expected = ''; + $this->assertSame($expected, $tag); + } + + public function testLinkTagAlternate() + { + $tag = link_tag( + 'http://sp.example.com/', + 'alternate', + '', + '', + 'only screen and (max-width: 640px)' + ); + + $expected = ''; + $this->assertSame($expected, $tag); + } + + public function testLinkTagArrayAlternate() + { + $tag = link_tag([ + 'href' => 'http://sp.example.com/', + 'rel' => 'alternate', + 'media' => 'only screen and (max-width: 640px)', + ]); + + $expected = ''; + $this->assertSame($expected, $tag); + } + + public function testLinkTagCanonical() + { + $tag = link_tag('http://www.example.com/', 'canonical'); + + $expected = ''; + $this->assertSame($expected, $tag); } public function testLinkTagArray() @@ -329,6 +389,18 @@ public function testLinkTagArray() $this->assertSame($expected, link_tag($parms)); } + public function testLinkTagArrayHreflang() + { + $tag = link_tag([ + 'href' => 'https://example.com/en', + 'rel' => 'alternate', + 'hreflang' => 'x-default', + ]); + + $expected = ''; + $this->assertSame($expected, $tag); + } + public function testDocType() { $target = 'html4-strict'; diff --git a/tests/system/Models/SaveModelTest.php b/tests/system/Models/SaveModelTest.php index 287e96bec275..ad9311cec0e9 100644 --- a/tests/system/Models/SaveModelTest.php +++ b/tests/system/Models/SaveModelTest.php @@ -322,4 +322,27 @@ public function testUseAutoIncrementSetToFalseSave(): void $this->assertSame($insert['key'], $this->model->getInsertID()); $this->seeInDatabase('without_auto_increment', $update); } + + public function testUseAutoIncrementSetToFalseSaveObject(): void + { + $this->createModel(WithoutAutoIncrementModel::class); + + $insert = [ + 'key' => 'some_random_key', + 'value' => 'some value', + ]; + $this->model->save((object) $insert); + + $this->assertSame($insert['key'], $this->model->getInsertID()); + $this->seeInDatabase('without_auto_increment', $insert); + + $update = [ + 'key' => 'some_random_key', + 'value' => 'some different value', + ]; + $this->model->save((object) $update); + + $this->assertSame($insert['key'], $this->model->getInsertID()); + $this->seeInDatabase('without_auto_increment', $update); + } } diff --git a/tests/system/Session/Handlers/Database/AbstractHandlerTestCase.php b/tests/system/Session/Handlers/Database/AbstractHandlerTestCase.php index c6d20f4267a9..cc71321b15f7 100644 --- a/tests/system/Session/Handlers/Database/AbstractHandlerTestCase.php +++ b/tests/system/Session/Handlers/Database/AbstractHandlerTestCase.php @@ -76,7 +76,7 @@ public function testWriteInsert() $this->setPrivateProperty($handler, 'lock', false); $row = $this->db->table('ci_sessions') - ->getWhere(['id' => '555556b43phsnnf8if6bo33b635e4444']) + ->getWhere(['id' => 'ci_session:555556b43phsnnf8if6bo33b635e4444']) ->getRow(); $this->assertGreaterThan(time() - 100, strtotime($row->timestamp)); @@ -100,7 +100,7 @@ public function testWriteUpdate() $releaseLock(); $row = $this->db->table('ci_sessions') - ->getWhere(['id' => '1f5o06b43phsnnf8if6bo33b635e4p2o']) + ->getWhere(['id' => 'ci_session:1f5o06b43phsnnf8if6bo33b635e4p2o']) ->getRow(); $this->assertGreaterThan(time() - 100, strtotime($row->timestamp)); diff --git a/user_guide_src/source/changelogs/index.rst b/user_guide_src/source/changelogs/index.rst index 27896bd83847..a9539b9a4649 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.2.12 v4.2.11 v4.2.10 v4.2.9 diff --git a/user_guide_src/source/changelogs/v4.2.12.rst b/user_guide_src/source/changelogs/v4.2.12.rst new file mode 100644 index 000000000000..d3239378f431 --- /dev/null +++ b/user_guide_src/source/changelogs/v4.2.12.rst @@ -0,0 +1,20 @@ +Version 4.2.12 +############## + +Release Date: January 9, 2023 + +**4.2.12 release of CodeIgniter4** + +.. contents:: + :local: + :depth: 2 + +Bugs Fixed +********** + +- Fixed ``Request::getIPaddress()`` causes an error on CLI. +- Fixed ``link_tag()`` missing ``type="application/rss+xml"``. +- Fixed ``spark migrate:status`` shows incorrect filenames when format is ``Y_m_d_His_``. +- Fixed an error when ``Model::save()`` saves an object if ``$useAutoIncrement`` is false. + +See the repo's `CHANGELOG.md `_ for a complete list of bugs fixed. diff --git a/user_guide_src/source/cli/cli_commands.rst b/user_guide_src/source/cli/cli_commands.rst index 7e00cb340e4c..e14a7063cf93 100644 --- a/user_guide_src/source/cli/cli_commands.rst +++ b/user_guide_src/source/cli/cli_commands.rst @@ -22,11 +22,11 @@ and must extend ``CodeIgniter\CLI\BaseCommand``, and implement the ``run()`` met The following properties should be used in order to get listed in CLI commands and to add help functionality to your command: * ``$group``: a string to describe the group the command is lumped under when listing commands. For example: ``Database`` -* ``$name``: a string to describe the command's name. For example: ``migrate:create`` -* ``$description``: a string to describe the command. For example: ``Creates a new migration file.`` -* ``$usage``: a string to describe the command usage. For example: ``migrate:create [name] [options]`` -* ``$arguments``: an array of strings to describe each command argument. For example: ``'name' => 'The migration file name'`` -* ``$options``: an array of strings to describe each command option. For example: ``'-n' => 'Set migration namespace'`` +* ``$name``: a string to describe the command's name. For example: ``make:controller`` +* ``$description``: a string to describe the command. For example: ``Generates a new controller file.`` +* ``$usage``: a string to describe the command usage. For example: ``make:controller [options]`` +* ``$arguments``: an array of strings to describe each command argument. For example: ``'name' => 'The controller class name.'`` +* ``$options``: an array of strings to describe each command option. For example: ``'--force' => 'Force overwrite existing file.'`` **Help description will be automatically generated according to the above parameters.** diff --git a/user_guide_src/source/cli/cli_commands/001.php b/user_guide_src/source/cli/cli_commands/001.php index 0069c7247d43..fb2640fcf01f 100644 --- a/user_guide_src/source/cli/cli_commands/001.php +++ b/user_guide_src/source/cli/cli_commands/001.php @@ -1,3 +1,3 @@ `` will be replaced with the command to check. namespace defined in your ``$psr4`` array in ``Config\Autoload`` or defined in your composer autoload file. Otherwise, code generation will be interrupted. -.. important:: Use of ``migrate:create`` to create migration files is now deprecated. It will be removed in +.. important:: Since v4.0.5, use of ``migrate:create`` to create migration files has been deprecated. It will be removed in future releases. Please use ``make:migration`` as replacement. Also, please use ``make:migration --session`` to use instead of the deprecated ``session:migration``. diff --git a/user_guide_src/source/conf.py b/user_guide_src/source/conf.py index b5c4bcbc8c12..89b2532c015d 100644 --- a/user_guide_src/source/conf.py +++ b/user_guide_src/source/conf.py @@ -12,19 +12,21 @@ # # import os # import sys +import datetime # sys.path.insert(0, os.path.abspath('.')) # -- Project information ----------------------------------------------------- project = 'CodeIgniter' author = 'CodeIgniter Foundation' -copyright = '2019-2022 CodeIgniter Foundation' +year_now = datetime.date.today().year +copyright = '2019-' + str(year_now) + ' CodeIgniter Foundation' # The short X.Y version. version = '4.2' # The full version, including alpha/beta/rc tags. -release = '4.2.11' +release = '4.2.12' # -- General configuration --------------------------------------------------- @@ -137,4 +139,4 @@ epub_title = 'CodeIgniter4' epub_author = 'CodeIgniter Foundation' epub_publisher = 'CodeIgniter Foundation' -epub_copyright = '2019-2022 CodeIgniter Foundation' +epub_copyright = copyright diff --git a/user_guide_src/source/general/helpers.rst b/user_guide_src/source/general/helpers.rst index 684ca021f70e..8bdd95fe2ec2 100644 --- a/user_guide_src/source/general/helpers.rst +++ b/user_guide_src/source/general/helpers.rst @@ -2,6 +2,14 @@ Helper Functions ################ +.. contents:: + :local: + :depth: 2 + +***************** +What are Helpers? +***************** + Helpers, as the name suggests, help you with tasks. Each helper file is simply a collection of functions in a particular category. There are **URL Helpers**, that assist in creating links, there are **Form Helpers** that help @@ -9,10 +17,6 @@ you create form elements, **Text Helpers** perform various text formatting routines, **Cookie Helpers** set and read cookies, **File Helpers** help you deal with files, etc. -.. contents:: - :local: - :depth: 2 - Unlike most other systems in CodeIgniter, Helpers are not written in an Object Oriented format. They are simple, procedural functions. Each helper function performs one specific task, with no dependence on other @@ -29,8 +33,9 @@ Helpers are typically stored in your **system/Helpers**, or specified helper is not located there CI will instead look in your global **system/Helpers** directory. +**************** Loading a Helper -================ +**************** .. note:: The URL helper is always loaded so you do not need to load it yourself. @@ -41,6 +46,10 @@ Loading a helper file is quite simple using the following method: Where ``name`` is the file name of the helper, without the "**.php**" file extension or the "**_helper**" part. +.. important:: CodeIgniter helper file names are all lowercase. + Therefore, ``helper('Name')`` will not work on case-sensitive file systems + such as Linux. + For example, to load the **Cookie Helper** file, which is named **cookie_helper.php**, you would do this: @@ -50,7 +59,7 @@ For example, to load the **Cookie Helper** file, which is named don't try to assign it to a variable. Just use it as shown. Loading Multiple Helpers ------------------------- +======================== If you need to load more than one helper at a time, you can pass an array of file names in and all of them will be loaded: @@ -58,7 +67,7 @@ an array of file names in and all of them will be loaded: .. literalinclude:: helpers/003.php Loading in a Controller ------------------------ +======================= A helper can be loaded anywhere within your controller methods (or even within your View files, although that's not a good practice), as @@ -75,7 +84,7 @@ property in Controller instead. See :ref:`Controllers `. .. _helpers-loading-from-non-standard-locations: Loading from Non-standard Locations ------------------------------------ +=================================== Helpers can be loaded from directories outside of **app/Helpers** and **system/Helpers**, as long as that path can be found through a namespace that @@ -100,8 +109,9 @@ You can also use the following way: .. note:: The functions within files loaded this way are not truly namespaced. The namespace is simply used as a convenient way to locate the files. +************** Using a Helper -============== +************** Once you've loaded the Helper File containing the function you intend to use, you'll call it the way you would a standard PHP function. @@ -114,8 +124,9 @@ your view files you would do this: Where ``Click Here`` is the name of the link, and ``blog/comments`` is the URI to the controller/method you wish to link to. +******************* "Extending" Helpers -=================== +******************* To "extend" Helpers, create a file in your **app/Helpers/** folder with an identical name to the existing Helper. @@ -136,7 +147,9 @@ functions: .. literalinclude:: helpers/006.php -The ``helper()`` function will scan through all PSR-4 namespaces defined in **app/Config/Autoload.php** +.. important:: Do not specify the namespace ``App\Helpers``. + +The :php:func:`helper()` function will scan through all PSR-4 namespaces defined in **app/Config/Autoload.php** and load in ALL matching helpers of the same name. This allows any module's helpers to be loaded, as well as any helpers you've created specifically for this application. The load order is as follows: @@ -145,8 +158,9 @@ is as follows: 2. {namespace}/Helpers - All namespaces are looped through in the order they are defined. 3. system/Helpers - The base file is loaded last +********* Now What? -========= +********* In the Table of Contents, you'll find a list of all the available :doc:`Helpers <../helpers/index>`. Browse each one to see what they do. diff --git a/user_guide_src/source/general/urls.rst b/user_guide_src/source/general/urls.rst index ec8ce76c15af..25d7b577d977 100644 --- a/user_guide_src/source/general/urls.rst +++ b/user_guide_src/source/general/urls.rst @@ -50,6 +50,8 @@ Route /blog/news/2022/10 Query page=2 ======== ==================================== +.. _urls-remove-index-php: + Removing the index.php file =========================== diff --git a/user_guide_src/source/incoming/controllers.rst b/user_guide_src/source/incoming/controllers.rst index b9eaf738575c..cc9482384089 100644 --- a/user_guide_src/source/incoming/controllers.rst +++ b/user_guide_src/source/incoming/controllers.rst @@ -16,6 +16,8 @@ A Controller is simply a class file that handles a HTTP request. :doc:`URI Routi Every controller you create should extend ``BaseController`` class. This class provides several features that are available to all of your controllers. +.. _controller-constructor: + Constructor *********** diff --git a/user_guide_src/source/incoming/controllers/007.php b/user_guide_src/source/incoming/controllers/007.php index 7c2e9b7091ea..e943c78bfcca 100644 --- a/user_guide_src/source/incoming/controllers/007.php +++ b/user_guide_src/source/incoming/controllers/007.php @@ -2,7 +2,7 @@ namespace App\Controllers; -class Products extends BaseController +class Helloworld extends BaseController { protected function utility() { diff --git a/user_guide_src/source/incoming/incomingrequest.rst b/user_guide_src/source/incoming/incomingrequest.rst index d665167f824c..8373f15ceea2 100644 --- a/user_guide_src/source/incoming/incomingrequest.rst +++ b/user_guide_src/source/incoming/incomingrequest.rst @@ -40,13 +40,13 @@ be checked with the ``isAJAX()`` and ``isCLI()`` methods: which in some cases is not sent by default in XHR requests via JavaScript (i.e., fetch). See the :doc:`AJAX Requests ` section on how to avoid this problem. -You can check the HTTP method that this request represents with the ``method()`` method: +You can check the HTTP method that this request represents with the ``getMethod()`` method: .. literalinclude:: incomingrequest/005.php By default, the method is returned as a lower-case string (i.e., ``'get'``, ``'post'``, etc). -.. note:: The functionality to convert the return value to lower case is deprecated. +.. important:: The functionality to convert the return value to lower case is deprecated. It will be removed in the future version, and this method will be PSR-7 equivalent. You can get an diff --git a/user_guide_src/source/incoming/request.rst b/user_guide_src/source/incoming/request.rst index 0b6128a4bd19..c68045d6aad2 100644 --- a/user_guide_src/source/incoming/request.rst +++ b/user_guide_src/source/incoming/request.rst @@ -1,17 +1,20 @@ +############# Request Class -************* +############# The request class is an object-oriented representation of an HTTP request. This is meant to work for both incoming, such as a request to the application from a browser, and outgoing requests, -like would be used to send a request from the application to a third-party application. This class +like would be used to send a request from the application to a third-party application. + +This class provides the common functionality they both need, but both cases have custom classes that extend -from the Request class to add specific functionality. +from the Request class to add specific functionality. In practice, you will need to use these classes. See the documentation for the :doc:`IncomingRequest Class ` and :doc:`CURLRequest Class ` for more usage details. Class Reference -=============== +*************** .. php:namespace:: CodeIgniter\HTTP @@ -19,8 +22,8 @@ Class Reference .. php:method:: getIPAddress() - :returns: The user's IP Address, if it can be detected, or null. If the IP address - is not a valid IP address, then will return 0.0.0.0 + :returns: The user's IP Address, if it can be detected. If the IP address + is not a valid IP address, then will return 0.0.0.0. :rtype: string Returns the IP address for the current user. If the IP address is not valid, the method @@ -33,6 +36,9 @@ Class Reference .. php:method:: isValidIP($ip[, $which = '']) + .. deprecated:: 4.0.5 + Use :doc:`../libraries/validation` instead. + .. important:: This method is deprecated. It will be removed in future releases. :param string $ip: IP address @@ -65,19 +71,30 @@ Class Reference .. php:method:: setMethod($method) - :param string $upper: Sets the request method. Used when spoofing the request. - :returns: HTTP request method + .. deprecated:: 4.0.5 + Use :php:meth:`CodeIgniter\\HTTP\\Request::withMethod()` instead. + + :param string $method: Sets the request method. Used when spoofing the request. + :returns: This request + :rtype: Request + + .. php:method:: withMethod($method) + + .. versionadded:: 4.0.5 + + :param string $method: Sets the request method. + :returns: New request instance :rtype: Request .. php:method:: getServer([$index = null[, $filter = null[, $flags = null]]]) :param mixed $index: Value name - :param int $filter: The type of filter to apply. A list of filters can be found `here `__. - :param int|array $flags: Flags to apply. A list of flags can be found `here `__. - :returns: $_SERVER item value if found, null if not + :param int $filter: The type of filter to apply. A list of filters can be found in `PHP manual `__. + :param int|array $flags: Flags to apply. A list of flags can be found in `PHP manual `__. + :returns: ``$_SERVER`` item value if found, null if not :rtype: mixed - This method is identical to the ``post()``, ``get()`` and ``cookie()`` methods from the + This method is identical to the ``getPost()``, ``getGet()`` and ``getCookie()`` methods from the :doc:`IncomingRequest Class `, only it fetches server data (``$_SERVER``): .. literalinclude:: request/004.php @@ -90,13 +107,13 @@ Class Reference .. php:method:: getEnv([$index = null[, $filter = null[, $flags = null]]]) :param mixed $index: Value name - :param int $filter: The type of filter to apply. A list of filters can be found `here `__. - :param int|array $flags: Flags to apply. A list of flags can be found `here `__. - :returns: $_ENV item value if found, null if not + :param int $filter: The type of filter to apply. A list of filters can be found in `PHP manual `__. + :param int|array $flags: Flags to apply. A list of flags can be found in `PHP manual `__. + :returns: ``$_ENV`` item value if found, null if not :rtype: mixed - This method is identical to the ``post()``, ``get()`` and ``cookie()`` methods from the - :doc:`IncomingRequest Class `, only it fetches getEnv data (``$_ENV``): + This method is identical to the ``getPost()``, ``getGet()`` and ``getCookie()`` methods from the + :doc:`IncomingRequest Class `, only it fetches env data (``$_ENV``): .. literalinclude:: request/006.php @@ -109,17 +126,17 @@ Class Reference :param string $method: Method name :param mixed $value: Data to be added - :returns: HTTP request method - :rtype: Request + :returns: This request + :rtype: Request - Allows manually setting the value of PHP global, like $_GET, $_POST, etc. + Allows manually setting the value of PHP global, like ``$_GET``, ``$_POST``, etc. .. php:method:: fetchGlobal($method [, $index = null[, $filter = null[, $flags = null]]]) :param string $method: Input filter constant :param mixed $index: Value name - :param int $filter: The type of filter to apply. A list of filters can be found `here `__. - :param int|array $flags: Flags to apply. A list of flags can be found `here `__. + :param int $filter: The type of filter to apply. A list of filters can be found in `PHP manual `__. + :param int|array $flags: Flags to apply. A list of flags can be found in `PHP manual `__. :rtype: mixed Fetches one or more items from a global, like cookies, get, post, etc. diff --git a/user_guide_src/source/incoming/request/003.php b/user_guide_src/source/incoming/request/003.php index fbfda408e272..4b0b537b801f 100644 --- a/user_guide_src/source/incoming/request/003.php +++ b/user_guide_src/source/incoming/request/003.php @@ -1,5 +1,5 @@ getMethod(true); // Outputs: POST +echo $request->getMethod(true); // Outputs: POST echo $request->getMethod(false); // Outputs: post -echo $request->getMethod(); // Outputs: post +echo $request->getMethod(); // Outputs: post diff --git a/user_guide_src/source/installation/troubleshooting.rst b/user_guide_src/source/installation/troubleshooting.rst index 426769938a49..30a8d0f55184 100644 --- a/user_guide_src/source/installation/troubleshooting.rst +++ b/user_guide_src/source/installation/troubleshooting.rst @@ -27,6 +27,7 @@ If a URL like ``/mypage/find/apple`` doesn't work, but the similar URL ``/index.php/mypage/find/apple`` does, that sounds like your **.htaccess** rules (for Apache) are not set up properly, or the ``mod_rewrite`` extension in Apache's **httpd.conf** is commented out. +See :ref:`urls-remove-index-php`. Only the default page loads --------------------------- diff --git a/user_guide_src/source/installation/upgrade_404.rst b/user_guide_src/source/installation/upgrade_404.rst index 899dfb49a35a..fcae336e07f8 100644 --- a/user_guide_src/source/installation/upgrade_404.rst +++ b/user_guide_src/source/installation/upgrade_404.rst @@ -15,7 +15,11 @@ Please refer to the upgrade instructions corresponding to your installation meth CodeIgniter 4.0.4 fixes a bug in the implementation of :doc:`Controller Filters `, breaking code implementing the ``FilterInterface``. -**Update FilterInterface declarations** +Breaking Changes +**************** + +Update FilterInterface Declarations +=================================== The method signatures for ``after()`` and ``before()`` must be updated to include ``$arguments``. The function definitions should be changed from:: diff --git a/user_guide_src/source/installation/upgrade_405.rst b/user_guide_src/source/installation/upgrade_405.rst index d83f461b3218..e3c07c13a816 100644 --- a/user_guide_src/source/installation/upgrade_405.rst +++ b/user_guide_src/source/installation/upgrade_405.rst @@ -12,18 +12,23 @@ Please refer to the upgrade instructions corresponding to your installation meth :local: :depth: 2 -**Cookie SameSite support** +Breaking Enhancements +********************* + +Cookie SameSite Support +======================= CodeIgniter 4.0.5 introduces a setting for the cookie SameSite attribute. Prior versions did not set this attribute at all. The default setting for cookies is now `Lax`. This will affect how cookies are handled in -cross-domain contexts and you may need to adjust this setting in your projects. Separate settings in `app/Config/App.php` +cross-domain contexts and you may need to adjust this setting in your projects. Separate settings in **app/Config/App.php** exists for Response cookies and for CSRF cookies. For additional information, see `MDN Web Docs `_. The SameSite specifications are described in `RFC 6265 `_ and the `RFC 6265bis revision `_. -**Message::getHeader(s)** +Message::getHeader(s) +===================== The HTTP layer is moving towards `PSR-7 compliance `_. Towards this end ``Message::getHeader()`` and ``Message::getHeaders()`` are deprecated and should be replaced @@ -32,8 +37,8 @@ to all classes that extend ``Message`` as well: ``Request``, ``Response`` and th Additional related deprecations from the HTTP layer: -* ``Message::isJSON``: Check the "Content-Type" header directly -* ``Request[Interface]::isValidIP``: Use the Validation class with ``valid_ip`` +* ``Message::isJSON()``: Check the "Content-Type" header directly +* ``Request[Interface]::isValidIP()``: Use the Validation class with ``valid_ip`` * ``Request[Interface]::getMethod()``: The ``$upper`` parameter will be removed, use str_to_upper() * ``Request[Trait]::$ipAddress``: This property will become private * ``Request::$proxyIPs``: This property will be removed; access ``config('App')->proxyIPs`` directly @@ -41,40 +46,42 @@ Additional related deprecations from the HTTP layer: * ``Response[Interface]::getReason()``: Use ``getReasonPhrase()`` instead * ``Response[Interface]::getStatusCode()``: The explicit ``int`` return type will be removed (no action required) -**ResponseInterface** +ResponseInterface +================= This interface intends to include the necessary methods for any framework-compatible response class. -A number of methods expected by the framework were missing and have noe been added. If you use any +A number of methods expected by the framework were missing and have now been added. If you use any classes the implement ``ResponseInterface`` directly they will need to be compatible with the updated requirements. These methods are as follows: -* ``setLastModified($date);`` -* ``setLink(PagerInterface $pager);`` -* ``setJSON($body, bool $unencoded = false);`` -* ``getJSON();`` -* ``setXML($body);`` -* ``getXML();`` -* ``send();`` -* ``sendHeaders();`` -* ``sendBody();`` -* ``setCookie($name, $value = '', $expire = '', $domain = '', $path = '/', $prefix = '', $secure = false, $httponly = false, $samesite = null);`` -* ``hasCookie(string $name, string $value = null, string $prefix = ''): bool;`` -* ``getCookie(string $name = null, string $prefix = '');`` -* ``deleteCookie(string $name = '', string $domain = '', string $path = '/', string $prefix = '');`` -* ``getCookies();`` -* ``redirect(string $uri, string $method = 'auto', int $code = null);`` -* ``download(string $filename = '', $data = '', bool $setMime = false);`` +* ``setLastModified($date)`` +* ``setLink(PagerInterface $pager)`` +* ``setJSON($body, bool $unencoded = false)`` +* ``getJSON()`` +* ``setXML($body)`` +* ``getXML()`` +* ``send()`` +* ``sendHeaders()`` +* ``sendBody()`` +* ``setCookie($name, $value = '', $expire = '', $domain = '', $path = '/', $prefix = '', $secure = false, $httponly = false, $samesite = null)`` +* ``hasCookie(string $name, string $value = null, string $prefix = ''): bool`` +* ``getCookie(string $name = null, string $prefix = '')`` +* ``deleteCookie(string $name = '', string $domain = '', string $path = '/', string $prefix = '')`` +* ``getCookies()`` +* ``redirect(string $uri, string $method = 'auto', int $code = null)`` +* ``download(string $filename = '', $data = '', bool $setMime = false)`` To facilitate use of this interface these methods have been moved from the framework's ``Response`` into a ``ResponseTrait`` which you may use, and ``DownloadResponse`` now extends ``Response`` directly to ensure maximum compatibility. -**Config\Services** +Config\\Services +================ Service discovery has been updated to allow third-party services (when enabled via Modules) to take precedence over core services. Update **app/Config/Services.php** so the class extends ``CodeIgniter\Config\BaseService`` to allow proper discovery of third-party services. Project Files -============= +************* Numerous 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. @@ -86,7 +93,7 @@ the project space: `Explore on Packagist `_ in ``current_url()``, the resulting URIs could be incorrect for a project's configuration, most importantly: ``indexPage`` @@ -20,7 +24,8 @@ would *not* be included. Projects using ``App::$indexPage`` should expect altere ``current_url()`` and all its dependencies (including Response Testing, Pager, Form Helper, Pager, and View Parser). Update your projects accordingly. -**Cache Keys** +Cache Keys +========== Cache handlers had wildly different compatibility for keys. The updated cache drivers now pass all keys through validation, roughly matching PSR-6's recommendations: @@ -35,22 +40,28 @@ all keys through validation, roughly matching PSR-6's recommendations: Update your projects to remove any invalid cache keys. -**BaseConnection::query() return values** +BaseConnection::query() Return Values +===================================== ``BaseConnection::query()`` method in prior versions was incorrectly returning BaseResult objects even if the query failed. This method will now return ``false`` for failed queries (or throw an -Exception if ``DBDebug==true``) and will return booleans for write-type queries. Review any use +Exception if ``DBDebug`` is ``true``) and will return booleans for write-type queries. Review any use of ``query()`` method and be assess whether the value might be boolean instead of Result object. For a better idea of what queries are write-type queries, check ``BaseConnection::isWriteType()`` and any DBMS-specific override ``isWriteType()`` in the relevant Connection class. -**ConnectionInterface::isWriteType() declaration added** +Breaking Enhancements +********************* + +ConnectionInterface::isWriteType() Declaration Added +==================================================== If you have written any classes that implement ConnectionInterface, these must now implement the ``isWriteType()`` method, declared as ``public function isWriteType($sql): bool``. If your class extends BaseConnection, then that class will provide a basic ``isWriteType()`` method which you might want to override. -**Test Traits** +Test Traits +=========== The ``CodeIgniter\Test`` namespace has had significant improvements to help developers with their own test cases. Most notably test extensions have moved to Traits to make them easier to @@ -68,7 +79,8 @@ and use any traits you need. For example: Finally, ``ControllerTester`` has been superseded by ``ControllerTestTrait`` to standardize approach and take advantage of the updated response testing (below). -**Test Responses** +Test Responses +============== The tools for testing responses have been consolidated and improved. A new ``TestResponse`` replaces ``ControllerResponse`` and ``FeatureResponse`` with a complete @@ -80,7 +92,7 @@ changes to be aware of: * ``TestResponse`` does not have ``getBody()`` and ``setBody()`` methods, but rather uses the Response methods directly, e.g.: ``$body = $result->response()->getBody();`` Project Files -============= +************* Numerous 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. @@ -92,7 +104,7 @@ the project space: `Explore on Packagist `_ (based on PSR-12). -**Method Scope** +Breaking Changes +**************** -The following methods were changed from "public" to "protected" to match their parent class methods and better align with their uses. +Method Scope +============ + +The following methods were changed from ``public`` to ``protected`` to match their parent class methods and better align with their uses. If you relied on any of these methods being public (highly unlikely) adjust your code accordingly: * ``CodeIgniter\Database\MySQLi\Connection::execute()`` @@ -45,7 +49,7 @@ If you relied on any of these methods being public (highly unlikely) adjust your * ``CodeIgniter\Test\Mock\MockSecurity.php::sendCookie()`` Project Files -============= +************* All files in the project space were reformatted with the new coding style. This will not affect existing code but you may want to apply the updated coding style to your own projects to keep diff --git a/user_guide_src/source/installation/upgrade_415.rst b/user_guide_src/source/installation/upgrade_415.rst index ae10e90f6874..d23c21a362a3 100644 --- a/user_guide_src/source/installation/upgrade_415.rst +++ b/user_guide_src/source/installation/upgrade_415.rst @@ -24,7 +24,7 @@ and modified the ``set()`` method, then you need to change its definition from ``public function set($key, ?string $value = '', ?bool $escape = null)`` to ``public function set($key, $value = '', ?bool $escape = null)``. -Session DatabaseHandler's database table change +Session DatabaseHandler's Database Table Change ----------------------------------------------- The types of the following columns in the session table have been changed for optimization. @@ -60,7 +60,7 @@ Protecting **GET** method needs only when you use ``form_open()`` auto-generatio because :ref:`auto-routing-legacy` permits any HTTP method to access a controller. Accessing the controller with a method you don't expect could bypass the filter. -CURLRequest header change +CURLRequest Header Change ------------------------- In the previous version, if you didn't provide your own headers, ``CURLRequest`` would send the request-headers from the browser. @@ -68,7 +68,7 @@ The bug was fixed. If your requests depend on the headers, your requests might f In this case, add the necessary headers manually. See :ref:`CURLRequest Class ` for how to add. -Query Builder changes +Query Builder Changes --------------------- For optimization and a bug fix, the following behaviors, mostly used in testing, have been changed. @@ -81,7 +81,7 @@ Breaking Enhancements .. _upgrade-415-multiple-filters-for-a-route: -Multiple filters for a route +Multiple Filters for a Route ---------------------------- A new feature to set multiple filters for a route. diff --git a/user_guide_src/source/installation/upgrade_416.rst b/user_guide_src/source/installation/upgrade_416.rst index ce88d59582b7..3a5b31d1c5e8 100644 --- a/user_guide_src/source/installation/upgrade_416.rst +++ b/user_guide_src/source/installation/upgrade_416.rst @@ -15,7 +15,7 @@ Please refer to the upgrade instructions corresponding to your installation meth Breaking Changes **************** -Validation result changes +Validation Result Changes ========================= Due to a bug fix, the Validation now might change the validation results when you validate an array item (see :ref:`Changelog `). So check the validation results for all the code that validates the array. Validating multiple fields like ``contacts.*.name`` is not affected. diff --git a/user_guide_src/source/installation/upgrade_418.rst b/user_guide_src/source/installation/upgrade_418.rst index ee3973ba37bc..24f08dde1cca 100644 --- a/user_guide_src/source/installation/upgrade_418.rst +++ b/user_guide_src/source/installation/upgrade_418.rst @@ -15,4 +15,4 @@ Please refer to the upgrade instructions corresponding to your installation meth Breaking Changes **************** -- Due to a security issue in the ``API\ResponseTrait`` all trait methods are now scoped to ``protected``. See the `Security advisory` ` for more information. +- Due to a security issue in the ``API\ResponseTrait`` all trait methods are now scoped to ``protected``. See the `Security advisory GHSA-7528-7jg5-6g62 `_ for more information. diff --git a/user_guide_src/source/installation/upgrade_4212.rst b/user_guide_src/source/installation/upgrade_4212.rst new file mode 100644 index 000000000000..ff6005d641d3 --- /dev/null +++ b/user_guide_src/source/installation/upgrade_4212.rst @@ -0,0 +1,29 @@ +############################### +Upgrading from 4.2.11 to 4.2.12 +############################### + +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 + +Project Files +************* + +Version ``4.2.12`` did not alter any executable code in project files. + +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/Cache.php +* app/Config/Migrations.php +* app/Controllers/BaseController.php +* composer.json diff --git a/user_guide_src/source/installation/upgrade_423.rst b/user_guide_src/source/installation/upgrade_423.rst index 980c8b3ed3ed..c78ddb38d77b 100644 --- a/user_guide_src/source/installation/upgrade_423.rst +++ b/user_guide_src/source/installation/upgrade_423.rst @@ -2,8 +2,17 @@ Upgrading from 4.2.2 to 4.2.3 ############################# +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 +Project Files +************* + Version ``4.2.3`` is an internal change for security measures and requires no intervention in projects. diff --git a/user_guide_src/source/installation/upgrade_4xx.rst b/user_guide_src/source/installation/upgrade_4xx.rst index 6eadd080a02c..a87886545994 100644 --- a/user_guide_src/source/installation/upgrade_4xx.rst +++ b/user_guide_src/source/installation/upgrade_4xx.rst @@ -35,8 +35,8 @@ General Adjustments Downloads ========= -- CI4 is still available as a ready-to-run zip or tarball. -- It can also be installed using Composer. +- CI4 is still available as a :doc:`ready-to-run zip or tarball <../installation/installing_manual>`. +- It can also be installed using :doc:`Composer <../installation/installing_composer>`. Namespaces ========== @@ -90,10 +90,10 @@ Class Loading - There is no longer a CodeIgniter "superobject", with framework component references magically injected as properties of your controller. -- Classes are instantiated where needed, and components are managed - by ``Services``. -- The class loader automatically handles PSR-4 style class locating, - within the ``App`` (**app**) and ``CodeIgniter`` (i.e., **system**) top level +- Classes are instantiated where needed, and framework components are managed + by :doc:`../concepts/services`. +- The :doc:`Autoloader <../concepts/autoloader>` automatically handles PSR-4 style class locating, + within the ``App`` (**app** folder) and ``CodeIgniter`` (i.e., **system** folder) top level namespaces; with Composer autoloading support. - You can configure the class loading to support whatever application structure you are most comfortable with, including the "HMVC" style. @@ -102,13 +102,31 @@ Libraries ========= - Your app classes can still go inside **app/Libraries**, but they don't have to. -- Instead of CI3's ``$this->load->library(x);`` you can now use +- Instead of CI3's ``$this->load->library('x');`` you can now use ``$this->x = new X();``, following namespaced conventions for your component. Helpers ======= -- Helpers are pretty much the same as before, though some have been simplified. +- :doc:`Helpers <../general/helpers>` are pretty much the same as before, though some have been simplified. +- Some helpers from CodeIgniter 3 no longer exists in Version 4. For all these + helpers, you have to find a new way to implement your functions. These + helpers are `CAPTCHA Helper `_, + `Email Helper `_. + `Path Helper `_. + and `Smiley Helper `_. +- `Download Helper `_ + in CI3 was removed. You need to use Response object where you are using ``force_download()``. + See :ref:`force-file-download`. +- `Language Helper `_ + in CI3 was removed. But ``lang()`` is always available in CI4. See :php:func:`lang()`. +- `Typography Helper `_ + in CI3 wll be :doc:`Typography Library <../libraries/typography>` in CI4. +- `Directory Helper `_ + and `File Helper `_ in CI3 + will be :doc:`../helpers/filesystem_helper` in CI4. +- `String Helper `_ functions + in CI3 are included in :doc:`../helpers/text_helper` in CI4. - In CI4, ``redirect()`` returns a ``RedirectResponse`` instance instead of redirecting and terminating script execution. You must return it. - `redirect() Documentation CodeIgniter 3.X `_ - `redirect() Documentation CodeIgniter 4.X <../general/common_functions.html#redirect>`_ @@ -116,7 +134,8 @@ Helpers Events ====== -- Hooks have been replaced by Events. +- `Hooks `_ have been + replaced by :doc:`../extending/events`. - Instead of CI3's ``$hook['post_controller_constructor']`` you now use ``Events::on('post_controller_constructor', ['MyClass', 'MyFunction']);``, with the namespace ``CodeIgniter\Events\Events;``. - Events are always enabled, and are available globally. @@ -126,17 +145,18 @@ Extending the Framework - You don't need a **core** folder to hold ``MY_...`` framework component extensions or replacements. -- You don't need ``MY_x`` classes inside your libraries folder +- You don't need ``MY_X`` classes inside your libraries folder to extend or replace CI4 pieces. - Make any such classes where you like, and add appropriate service methods in **app/Config/Services.php** to load your components instead of the default ones. +- See :doc:`../extending/core_classes` for details. Upgrading Libraries ******************* - Your app classes can still go inside **app/Libraries**, but they don't have to. -- Instead of CI3's ``$this->load->library(x);`` you can now use ``$this->x = new X();``, +- Instead of CI3's ``$this->load->library('x');`` you can now use ``$this->x = new X();``, following namespaced conventions for your component. - Some libraries from CodeIgniter 3 no longer exists in Version 4. For all these libraries, you have to find a new way to implement your functions. These diff --git a/user_guide_src/source/installation/upgrade_configuration.rst b/user_guide_src/source/installation/upgrade_configuration.rst index aca4af05d4a7..6150b9a01ff6 100644 --- a/user_guide_src/source/installation/upgrade_configuration.rst +++ b/user_guide_src/source/installation/upgrade_configuration.rst @@ -15,6 +15,8 @@ What has been changed ===================== - In CI4, the configurations are now stored in classes which extend ``CodeIgniter\Config\BaseConfig``. +- The **application/config/config.php** in CI3 will be **app/Config/App.php** + and some other files like **app/Config/Security.php** for the specific classes. - Within the configuration class, the config values are stored in public class properties. - The method to fetch config values has been changed. @@ -30,7 +32,7 @@ Upgrade Guide from the CI3 config into the new CI4 config class as public class properties. 4. Now, you have to change the config fetching syntax everywhere you fetch config values. The CI3 syntax is something like ``$this->config->item('item_name');``. - You have to change this into ``config('MyConfigFile')->item_name;``. + You have to change this into ``config('MyConfig')->item_name;``. Code Example ============ @@ -38,13 +40,13 @@ Code Example CodeIgniter Version 3.x ------------------------ -Path: **application/config**: +Path: **application/config/site.php**: .. literalinclude:: upgrade_configuration/ci3sample/001.php CodeIgniter Version 4.x ----------------------- -Path: **app/Config**: +Path: **app/Config/Site.php**: .. literalinclude:: upgrade_configuration/001.php diff --git a/user_guide_src/source/installation/upgrade_configuration/001.php b/user_guide_src/source/installation/upgrade_configuration/001.php index f62f19b7c5ad..fdbbfe4eff0a 100644 --- a/user_guide_src/source/installation/upgrade_configuration/001.php +++ b/user_guide_src/source/installation/upgrade_configuration/001.php @@ -4,7 +4,7 @@ use CodeIgniter\Config\BaseConfig; -class CustomClass extends BaseConfig +class Site extends BaseConfig { public $siteName = 'My Great Site'; public $siteEmail = 'webmaster@example.com'; diff --git a/user_guide_src/source/installation/upgrade_controllers.rst b/user_guide_src/source/installation/upgrade_controllers.rst index 1810ea615506..ca5555de85bb 100644 --- a/user_guide_src/source/installation/upgrade_controllers.rst +++ b/user_guide_src/source/installation/upgrade_controllers.rst @@ -15,10 +15,11 @@ What has been changed ===================== - Since namespaces have been added to CodeIgniter 4, the controllers must be changed to support namespaces. -- Controllers don't use constructors any more (to invoke CI 'magic') unless those are part of base controllers you make. -- CI provides Request and Response objects for you to work with - more powerful than the CI3-way. -- If you want a base controller (``MY_Controller`` in CI3), make it where you like, - e.g., BaseController extends Controller, and then have your controllers extend it +- The constructor of CI4 Controller does not automatically load core classes into the properties. +- CI4's Controller has a special constructor :ref:`initController() `. +- CI4 provides :doc:`Request ` and :doc:`Responses ` + objects for you to work with - more powerful than the CI3-way. +- If you want a base controller (``MY_Controller`` in CI3), use **app/Controllers/BaseController.php**. Upgrade Guide ============= diff --git a/user_guide_src/source/installation/upgrade_controllers/ci3sample/001.php b/user_guide_src/source/installation/upgrade_controllers/ci3sample/001.php index d6a01bdea8b1..a0b22b6bb952 100644 --- a/user_guide_src/source/installation/upgrade_controllers/ci3sample/001.php +++ b/user_guide_src/source/installation/upgrade_controllers/ci3sample/001.php @@ -4,6 +4,6 @@ class Helloworld extends CI_Controller { public function index($name) { - echo "Hello $name! "; + echo 'Hello ' . html_escape($name) . '!'; } } diff --git a/user_guide_src/source/installation/upgrade_database.rst b/user_guide_src/source/installation/upgrade_database.rst index f62044d8a302..5dc22ddda820 100644 --- a/user_guide_src/source/installation/upgrade_database.rst +++ b/user_guide_src/source/installation/upgrade_database.rst @@ -14,7 +14,9 @@ Documentations What has been changed ===================== - The functionality in CI3 is basically the same as in CI4. -- The method names have changed to camelCase and the query builder now needs to be initialized before you can run queries on it. +- `Database Caching `_ functionality known from CI3 was removed. +- The method names have changed to camelCase and the :doc:`Query Builder <../database/query_builder>` + now needs to be initialized before you can run queries on it. Upgrade Guide ============= @@ -39,6 +41,8 @@ Upgrade Guide - ``$this->db->select_max('age');`` to ``$builder->selectMax('age');`` - ``$this->db->join('comments', 'comments.id = blogs.id');`` to ``$builder->join('comments', 'comments.id = blogs.id');`` - ``$this->db->having('user_id', 45);`` to ``$builder->having('user_id', 45);`` +6. CI4 does not provide `Database Caching `_ + layer known from CI3, so if you need to cache the result, use :doc:`../libraries/caching` instead. Code Example ============ diff --git a/user_guide_src/source/installation/upgrade_file_upload/001.php b/user_guide_src/source/installation/upgrade_file_upload/001.php index a8b727c29745..c4c6d50db7b4 100644 --- a/user_guide_src/source/installation/upgrade_file_upload/001.php +++ b/user_guide_src/source/installation/upgrade_file_upload/001.php @@ -12,9 +12,13 @@ public function index() public function do_upload() { $this->validate([ - 'userfile' => 'uploaded[userfile]|max_size[userfile,100]' - . '|mime_in[userfile,image/png,image/jpg,image/gif]' - . '|ext_in[userfile,png,jpg,gif]|max_dims[userfile,1024,768]', + 'userfile' => [ + 'uploaded[userfile]', + 'max_size[userfile,100]', + 'mime_in[userfile,image/png,image/jpg,image/gif]', + 'ext_in[userfile,png,jpg,gif]', + 'max_dims[userfile,1024,768]', + ], ]); $file = $this->request->getFile('userfile'); diff --git a/user_guide_src/source/installation/upgrade_models.rst b/user_guide_src/source/installation/upgrade_models.rst index 278a283c0343..bc5fbf08292f 100644 --- a/user_guide_src/source/installation/upgrade_models.rst +++ b/user_guide_src/source/installation/upgrade_models.rst @@ -24,7 +24,7 @@ Upgrade Guide 2. Add this line just after the opening php tag: ``namespace App\Models;``. 3. Below the ``namespace App\Models;`` line add this line: ``use CodeIgniter\Model;``. 4. Replace ``extends CI_Model`` with ``extends Model``. -5. Instead of CI3's ``$this->load->model(x);``, you would now use ``$this->x = new X();``, following namespaced conventions for your component. Alternatively, you can use the :php:func:`model()` function: ``$this->x = model('X');``. +5. Instead of CI3's ``$this->load->model('x');``, you would now use ``$this->x = new X();``, following namespaced conventions for your component. Alternatively, you can use the :php:func:`model()` function: ``$this->x = model('X');``. If you use sub-directories in your model structure you have to change the namespace according to that. Example: You have a version 3 model located in **application/models/users/user_contact.php** the namespace has to be ``namespace App\Models\Users;`` and the model path in the version 4 should look like this: **app/Models/Users/UserContact.php** @@ -32,7 +32,7 @@ Example: You have a version 3 model located in **application/models/users/user_c The new Model in CI4 has a lot of built-in methods. For example, the ``find($id)`` method. With this you can find data where the primary key is equal to ``$id``. Inserting data is also easier than before. In CI4 there is an ``insert($data)`` method. You can optionally make use of all those built-in methods and migrate your code to the new methods. -You can find more information to those methods :doc:`here `. +You can find more information to those methods in :doc:`../models/model`. Code Example ============ diff --git a/user_guide_src/source/installation/upgrade_responses.rst b/user_guide_src/source/installation/upgrade_responses.rst index 6788468c0b57..2059647b230d 100644 --- a/user_guide_src/source/installation/upgrade_responses.rst +++ b/user_guide_src/source/installation/upgrade_responses.rst @@ -17,7 +17,7 @@ What has been changed Upgrade Guide ============= 1. The methods in the HTTP Responses class are named slightly different. The most important change in the naming is the switch from underscored method names to camelCase. The method ``set_content_type()`` from version 3 is now named ``setContentType()`` and so on. -2. In the most cases you have to change ``$this->output`` to ``$this->response`` followed by the method. You can find all methods :doc:`here `. +2. In the most cases you have to change ``$this->output`` to ``$this->response`` followed by the method. You can find all methods in :doc:`../outgoing/response`. Code Example ============ diff --git a/user_guide_src/source/installation/upgrade_security.rst b/user_guide_src/source/installation/upgrade_security.rst index c520b9c50350..81bf5e290d56 100644 --- a/user_guide_src/source/installation/upgrade_security.rst +++ b/user_guide_src/source/installation/upgrade_security.rst @@ -12,11 +12,11 @@ Documentations - :doc:`Security Documentation CodeIgniter 4.X ` .. note:: - If you use the :doc:`form helper ` and enable the CSRF filter globally, then ``form_open()`` will automatically insert a hidden CSRF field in your forms. So you do not have to upgrade this by yourself. + 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. What has been changed ===================== -- The method to implement CSRF tokens to html forms has been changed. +- The method to implement CSRF tokens to HTML forms has been changed. Upgrade Guide ============= diff --git a/user_guide_src/source/installation/upgrade_security/001.php b/user_guide_src/source/installation/upgrade_security/001.php index 38536bd67cce..c7c907966f0b 100644 --- a/user_guide_src/source/installation/upgrade_security/001.php +++ b/user_guide_src/source/installation/upgrade_security/001.php @@ -14,5 +14,6 @@ class Filters extends BaseConfig 'csrf', ], ]; + // ... } diff --git a/user_guide_src/source/installation/upgrade_sessions/001.php b/user_guide_src/source/installation/upgrade_sessions/001.php index 0bfeeb7f9487..9ac867e4e814 100644 --- a/user_guide_src/source/installation/upgrade_sessions/001.php +++ b/user_guide_src/source/installation/upgrade_sessions/001.php @@ -2,7 +2,7 @@ $session = session(); -$_SESSION['item']; +$_SESSION['item']; // But we do not recommend to use superglobal directly. $session->get('item'); $session->item; session('item'); diff --git a/user_guide_src/source/installation/upgrade_validations.rst b/user_guide_src/source/installation/upgrade_validations.rst index 5bd6a2a1d514..45325b3ae327 100644 --- a/user_guide_src/source/installation/upgrade_validations.rst +++ b/user_guide_src/source/installation/upgrade_validations.rst @@ -13,7 +13,7 @@ Documentations of Library What has been changed ===================== -- If you want to change validation error display, you have to set CI4 validation View templates. +- If you want to change validation error display, you have to set CI4 :ref:`validation View templates `. - CI4 validation has no Callbacks nor Callable in CI3. - CI4 validation format rules do not permit empty string. - CI4 validation never changes your data. @@ -29,7 +29,7 @@ Upgrade Guide - ``$this->load->helper(array('form', 'url'));`` to ``helper(['form', 'url']);`` - remove the line ``$this->load->library('form_validation');`` - ``if ($this->form_validation->run() == FALSE)`` to ``if (! $this->validate([]))`` - - ``$this->load->view('myform');`` to ``echo view('myform', ['validation' => $this->validator,]);`` + - ``$this->load->view('myform');`` to ``return view('myform', ['validation' => $this->validator,]);`` 3. You have to change the validation rules. The new syntax is to set the rules as array in the controller: diff --git a/user_guide_src/source/installation/upgrade_validations/002.php b/user_guide_src/source/installation/upgrade_validations/002.php index ddbed1ede5fc..916fae1484d4 100644 --- a/user_guide_src/source/installation/upgrade_validations/002.php +++ b/user_guide_src/source/installation/upgrade_validations/002.php @@ -13,11 +13,11 @@ public function index() if (! $this->validate([ // Validation rules ])) { - echo view('myform', [ + return view('myform', [ 'validation' => $this->validator, ]); - } else { - echo view('formsuccess'); } + + return view('formsuccess'); } } diff --git a/user_guide_src/source/installation/upgrade_views.rst b/user_guide_src/source/installation/upgrade_views.rst index 29dcace6ee62..3e56735e3102 100644 --- a/user_guide_src/source/installation/upgrade_views.rst +++ b/user_guide_src/source/installation/upgrade_views.rst @@ -15,9 +15,10 @@ What has been changed ===================== - Your views look much like before, but they are invoked differently ... instead of CI3's - ``$this->load->view(x);``, you can use ``return view(x);``. -- CI4 supports *View Cells* to build your response in pieces, and *View Layouts* for page layout. -- The template parser is still there, and substantially enhanced. + ``$this->load->view('x');``, you can use ``return view('x');``. +- CI4 supports :doc:`../outgoing/view_cells` to build your response in pieces, + and :doc:`../outgoing/view_layouts` for page layout. +- The :doc:`Template Parser <../outgoing/view_parser>` is still there, and substantially enhanced. Upgrade Guide ============= diff --git a/user_guide_src/source/installation/upgrade_views/ci3sample/001.php b/user_guide_src/source/installation/upgrade_views/ci3sample/001.php index f0b7c348666d..bbabef2d1819 100644 --- a/user_guide_src/source/installation/upgrade_views/ci3sample/001.php +++ b/user_guide_src/source/installation/upgrade_views/ci3sample/001.php @@ -1,15 +1,15 @@ - <?php echo $title; ?> + <?php echo html_escape($title); ?> -

+

My Todo List

    -
  • +
diff --git a/user_guide_src/source/installation/upgrading.rst b/user_guide_src/source/installation/upgrading.rst index 6a759f93a232..242ac80d1b16 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_4212 upgrade_4211 upgrade_4210 upgrade_428 diff --git a/user_guide_src/source/libraries/uploaded_files/002.php b/user_guide_src/source/libraries/uploaded_files/002.php index 11c6b18698d4..44b33a854a61 100644 --- a/user_guide_src/source/libraries/uploaded_files/002.php +++ b/user_guide_src/source/libraries/uploaded_files/002.php @@ -18,11 +18,13 @@ public function upload() $validationRule = [ 'userfile' => [ 'label' => 'Image File', - 'rules' => 'uploaded[userfile]' - . '|is_image[userfile]' - . '|mime_in[userfile,image/jpg,image/jpeg,image/gif,image/png,image/webp]' - . '|max_size[userfile,100]' - . '|max_dims[userfile,1024,768]', + 'rules' => [ + 'uploaded[userfile]', + 'is_image[userfile]', + 'mime_in[userfile,image/jpg,image/jpeg,image/gif,image/png,image/webp]', + 'max_size[userfile,100]', + 'max_dims[userfile,1024,768]', + ], ], ]; if (! $this->validate($validationRule)) { @@ -36,10 +38,11 @@ public function upload() if (! $img->hasMoved()) { $filepath = WRITEPATH . 'uploads/' . $img->store(); - $data = ['uploaded_flleinfo' => new File($filepath)]; + $data = ['uploaded_fileinfo' => new File($filepath)]; return view('upload_success', $data); } + $data = ['errors' => 'The file has already been moved.']; return view('upload_form', $data); diff --git a/user_guide_src/source/libraries/validation.rst b/user_guide_src/source/libraries/validation.rst index ec7f237d7172..773cf16f6749 100644 --- a/user_guide_src/source/libraries/validation.rst +++ b/user_guide_src/source/libraries/validation.rst @@ -516,6 +516,8 @@ When specifying a field with a wildcard, all errors matching the mask will be ch .. literalinclude:: validation/029.php +.. _validation-customizing-error-display: + Customizing Error Display ************************* @@ -666,8 +668,8 @@ is_not_unique Yes Checks the database to see if the given value filter (currently accept only one filter). is_unique Yes Checks if this field value exists in the is_unique[table.field,ignore_field,ignore_value] database. Optionally set a column and value - value to ignore, useful when updating records - to ignore itself. + to ignore, useful when updating records to + ignore itself. less_than Yes Fails if field is greater than or equal to less_than[8] the parameter value or not numeric. less_than_equal_to Yes Fails if field is greater than the parameter less_than_equal_to[8] diff --git a/user_guide_src/source/license.rst b/user_guide_src/source/license.rst index 40505f1b73ba..4320bd96ccae 100644 --- a/user_guide_src/source/license.rst +++ b/user_guide_src/source/license.rst @@ -3,7 +3,7 @@ The MIT License (MIT) ##################### | Copyright (c) 2014-2019 British Columbia Institute of Technology -| Copyright (c) 2019-2022 CodeIgniter Foundation +| Copyright (c) 2019-2023 CodeIgniter Foundation Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/user_guide_src/source/outgoing/response.rst b/user_guide_src/source/outgoing/response.rst index 553acba21ea4..f3e0909487a4 100644 --- a/user_guide_src/source/outgoing/response.rst +++ b/user_guide_src/source/outgoing/response.rst @@ -59,6 +59,8 @@ parameter. This is not case-sensitive. .. literalinclude:: response/006.php +.. _force-file-download: + Force File Download ===================