Version 4.8.0¶
Release Date: Unreleased
4.8.0 release of CodeIgniter4
Highlights¶
- TBD
BREAKING¶
Behavior Changes¶
- The static
Boot::initializeConsole()method no longer handles the display of the console header. This is now handled withinConsole::run(). If you have overriddenBoot::initializeConsole(), you should remove any code related to displaying the console header, as this is now the responsibility of theConsoleclass. - CLI: Uncaught exceptions in
spark, and errors shown withBaseCommand::showError()orAbstractCommand::renderThrowable()(asmigrateanddb:seeddo), are now printed to STDERR instead of STDOUT. The exit code is unchanged. See CLI Error Output. - CLI:
CLI::beep()now writes through theInputOutputtransport instead ofecho, so it is discarded bycallSilently(), captured byMockInputOutput, and no longer seen by PHP output buffering on the command line. - CLI:
CLI::print()of text ending in a line feed, and a progress bar drawn withCLI::showProgress(), no longer cause a blank line before the nextCLI::write(), since the cursor already sits at the start of a line.CLI::init()now also resets that cursor state, the progress bar, and the cached terminal dimensions. - CLI:
CLI::showProgress()now draws a full bar at 100% for a step beyond the total, instead of throwing aValueError. - CLI:
CLI::error()no longer leaves color support disabled for STDOUT when it is given an invalid color name, andCLI::write(),CLI::print(), andCLI::color()now accept an empty foreground with a background instead of throwing. - CLI: An empty
NO_COLORno longer turns color off, following https://no-color.org/. Color detection now turns color off whenTERMisdumb, and on Windows when the stream is not a terminal (outside MSYS2'sMINGW32andMINGW64). On Windows without VT100 support, anyTERMstarting withxterm,screen,vt100,vt220,putty,rxvt,ansi,cygwinorlinux, or ending in-256color, now enables color, where previously onlyxtermdid. Coloring text that already contains a colored segment now keeps that segment whatever its color, where previously only the dim colors (\033[0;...m) were kept and a bright or background-only segment lost the color of the text after it. - CLI: The output state moved out of the static
CLIproperties. A subclass ofCLIthat overridesfwrite()orcolor(), or reads$lastWrite,$isColored,$io,$height,$width, or the color tables, no longer affects or observes the output. See CLI Output Classes. - CLI: The prompt methods moved to
Prompt. A subclass ofCLIthat overridesvalidate()no longer affects the prompts, and an override ofprompt()no longer affectspromptByKey()orpromptByMultipleKeys(). Passingprompt()options or rules, or apromptByKey()text, of another type thanstring,array, ornullnow throws aTypeErrorinstead of anInvalidArgumentException. See CLI Prompt. - Commands: The
-hoption of theroutescommand, which was previously mapped to--sort-by-handler, is now removed. Use--sort-by-handlerinstead to sort the routes by handler when running theroutescommand. - Commands: The
filter:checkcommand now requires the HTTP method argument to be uppercase (e.g.,spark filter:check GET /instead ofspark filter:check get /). - Commands: Several built-in commands have been migrated from
BaseCommandto the modernAbstractCommandstyle. Applications that extend a built-in command to override behavior may need to re-implement against the modern API (configure()+execute()and the#[Command]attribute) once the class it extends is migrated, or, preferably, compose instead of extending. Invocations on the command line are unaffected. - Commands: Generator commands migrated to
AbstractGeneratorCommandnow returnEXIT_ERROR(previouslyEXIT_SUCCESS) when the target file exists without--force, when the namespace is not defined, or when the name does not form a valid class name. TheCodeIgniternamespace confirmation is no longer prompted on non-interactive runs, andmake:cellandmake:model --return entitynow ask it only once. - Commands: The
make:commandcommand now generates modern commands: abasiccommand extendsAbstractCommandwith the#[Command]attribute, and ageneratorcommand extendsAbstractGeneratorCommandwith the#[GeneratorCommand]attribute. - Commands: The
make:migrationcommand now rejects--forceas an unknown option. It was previously accepted but ignored, since the timestamped file name only collides with an existing file when the same name is generated twice within one second. - Commands: The success and error messages from
debugbar:clear,cache:clear, andcache:infonow include the affected path or cache driver/handler so the user can see which resource was acted on (or rejected). Scripts asserting on the prior literal text will need to be updated. - Commands: Declining the
key:generateoverwrite prompt interactively now returnsEXIT_SUCCESSinstead ofEXIT_ERROR. Output messages were also reworded; CI/automation that branches on the exit code or greps the previous wording will need updating. - Commands: The
migrate:rollbackcommand no longer accepts the undocumented-g(database group) option. It never had any effect, sinceMigrationRunner::regress()ignores the group, and the modern command pipeline now rejects unknown options. Remove-gfrom anymigrate:rollbackinvocation. - Commands: The
logs:clearcommand now returnsEXIT_SUCCESS(previouslyEXIT_ERROR) when the user declines the interactive confirmation prompt, since user-initiated cancellation is not a failure. Output messages have also been reworded to distinguish cancellation (interactiven) from abort (non-interactive without--force), and the resolved log directory path is now included in the prompt, success, and failure messages. - Commands: The
db:tablecommand now returnsEXIT_SUCCESSfromdb:table --show(previouslyEXIT_ERROR), and reports an error instead of prompting when run non-interactively (--no-interactionor piped input) without a valid table. CI/automation that branches on the--showexit code will need updating. - Commands: The
db:tableoutput was reworded: the data and metadata column headers are now capitalized (e.g.Id,Created_at), the connection summary headers readHostname,Database,Username,DB Driver,DB Prefix, andPort, and the section titles changed (e.g.Data of "users" table:). Scripts that grep the previous output will need updating. - Commands: The
worker:uninstallcommand now returnsEXIT_SUCCESS(previouslyEXIT_ERROR) when the user declines the interactive confirmation prompt, since user-initiated cancellation is not a failure. In non-interactive mode without--forceit now aborts withEXIT_ERRORinstead of prompting, and the redundantUninstalling FrankenPHP Worker Modeheader was dropped. Scripts that branch on the previous exit code or wording will need updating. - Commands: The
phpini:checkcommand's invalid-argument error now prints onlyYou must specify a correct argument.. The usage example and the argument listing moved tophpini:check --help, where the argument is now namedsection. Scripts that grep the previous output will need updating. - Commands: The prompts that pick from fixed options (
db:table,db:create,key:generate,key:rotate,make:command,make:controller, andmake:model) now usePrompt::select(), so where the arrow-key menu is unavailable, as on Windows, the answer is the option's number instead of its value. Option values such as--prefixstill take the value. - Config:
CodeIgniter\Config\BaseServiceis nowabstract. It is intended to be extended (asConfig\Servicesdoes) and accessed statically; code that instantiated it directly withnew BaseService()will now throw anError. - Config:
BaseConfignow scans the environment once instead of checking every property against it, which makes building config classes several times faster without config caching and in Worker Mode. As a result, a bareputenv()call (adding, changing, or removing a variable) made after the first config class was built may not be seen untilBaseConfig::reset()runs, including in later requests in Worker Mode. See Config Environment Lookups. - Database:
BaseBuilder::getCompiledSelect(),getCompiledInsert(),getCompiledUpdate(),getCompiledDelete(),delete()andcountAllResults()now clear bindings and binding counters after compilation or execution when their reset parameter is enabled (the default). This prevents accumulation when a builder is reused, butgetBinds()now returns an empty array after these calls. This also alignsgetCompiledSelect()with its documented reset behavior, which should matchget(). Passfalsefor the reset parameter to retain the query and its bindings. Test mode retains bindings needed by its returned raw SQL. See Query Builder Bindings. - Database: The Postgre driver's
$db->error()['code']previously always returned''. It now returns the 5-character SQLSTATE string for query and transaction failures (e.g.,'42P01'), or'08006'for connection-level failures. Code that relied on$db->error()['code'] === ''will need updating. - Debug:
CodeIgniter\Debug\Toolbar\Collectors\BaseCollectoris nowabstract. Custom toolbar collectors must extend it; code that instantiated it directly withnew BaseCollector()will now throw anError. - Filters: HTTP method matching for method-based filters is now case-sensitive. The keys in
Config\Filters::$methodsmust exactly match the request method (e.g.,GET,POST). Lowercase method names (e.g.,post) will no longer match. - HTTP: Routes defined with
$routes->add()now also match HTTPQUERYrequests. If the route has CSRF protection, remember that CSRF verification does not protect safe methods such asGETandQUERY. - HTTP:
CodeIgniter\HTTP\Exceptions\RedirectExceptionno longer redeclares the inherited$codeproperty. The status applied to aResponsepassed to the constructor whose status is outside the 301-308 range now comes from the newprotected int $defaultStatusCode = 302property. Subclasses that overrode$codeto change that status must override$defaultStatusCodeinstead. - Router:
RouteCollectionno longer appliesesc(strip_tags())to namespace, controller, or method values, including controller options passed toresource()andpresenter(). Applications relying on this implicit transformation must preprocess these values before passing them to the router. - Testing: Tests using the
FeatureTestTraitmust now use uppercase HTTP method names when they call thecall()method directly (e.g.,$this->call('GET', '/path')instead of$this->call('get', '/path')). Additionally, method-based routes set withwithRoutes()must also use uppercase method names (e.g.,$this->withRoutes([['GET', 'home', 'Home::index']])).
Interface Changes¶
NOTE: If you've implemented your own classes that implement these interfaces from scratch, you will need to update your implementations to include the new methods or method changes to ensure compatibility.
- Cache:
CodeIgniter\Cache\CacheInterface::remember()now accepts a TTL callable. Custom implementations ofCacheInterfacemust update the$ttlparameter type frominttocallable|int. - Database:
CodeIgniter\Database\ConnectionInterfacenow requires theafterCommit(),afterRollback(),inTransaction(), andtransaction()methods. - HTTP:
CodeIgniter\HTTP\ResponseInterfacenow requires thestream()andeventStream()methods, which create streaming and SSE responses. See Streaming Responses. - HTTP:
CodeIgniter\HTTP\Files\UploadedFileInterface::move()now returnsstaticinstead ofbool. The previousboolreturn was incompatible withCodeIgniter\Files\File::move(), whichUploadedFileextends, so no implementation could satisfy both. See Upgrading from 4.7.x to 4.8.0 for the migration. - Logging:
CodeIgniter\Log\Handlers\HandlerInterface::handle()now requires a third parameterarray $context = []. Any custom log handler that overrideshandle()- whether implementingHandlerInterfacedirectly or extending a built-in handler class - must add the parameter to itshandle()method signature. - Security: The
SecurityInterface'sverify()method now has a native return type ofstatic. - Validation:
CodeIgniter\Validation\ValidationInterfacenow requires thegetValidatedInput()method, which returns aCodeIgniter\Input\ValidatedInputinstance.
Method Signature Changes¶
-
Database:
BaseBuilder::join()andBaseBuilder::compileJoinTable()now acceptRawSql|stringfor the table argument. Custom builders overriding either method must accept both types. See Upgrading from 4.7.x to 4.8.0. -
CLI: The
Console::run()method now accepts an optionalarray $tokensparameter. This allows you to pass an array of command tokens directly to the console runner, which is useful for testing or programmatically running commands. If not provided, it will default to using the global$argv. -
CodeIgniter: The deprecated parameters in methods have been removed:
-
CodeIgniter\CodeIgniter::handleRequest()no longer accepts the deprecated$cacheConfigand$returnResponseparameters.$cacheConfigis no longer used and is now hard deprecated. A deprecation notice will be triggered if this is passed to the method.$returnResponsehas been removed, since it was already deprecated and unused.- The updated method signature is now
handleRequest(?RouteCollectionInterface $routes, ?Cache $cacheConfig = null).
-
CodeIgniter\CodeIgniter::gatherOutput()no longer accepts the deprecated$cacheConfigparameter. As this is the first parameter, custom uses of this method will need to be updated to remove the parameter.
-
-
Config:
CodeIgniter\Config\Services::request()no longer accepts any parameter. -
Database: The following methods have had their signatures updated to remove deprecated parameters:
CodeIgniter\Database\Forge::_createTable()no longer accepts the deprecated$ifNotExistsparameter. The method signature is now_createTable(string $table, array $attributes).
-
HTTP:
CodeIgniter\HTTP\OutgoingRequest::__construct()now requires the$uriparameter, which was previously?URI $uri = null. Passingnullonly worked when aHostheader was supplied in the same call, since the constructor otherwise dereferences the URI to set that header. ConsequentlyOutgoingRequest::getUri()now returnsURIinstead ofURI|null, matchingOutgoingRequestInterface. See Upgrading from 4.7.x to 4.8.0 for the migration. -
Model:
CodeIgniter\BaseModelnow requires thechunkRows(),chunkById(), andchunkRowsById()methods. Custom classes extendingBaseModeldirectly must implement them. -
Session: The
$max_lifetimeparameter of the followinggc()methods now has the nativeinttype, matchingSessionHandlerInterface:ArrayHandler::gc(),DatabaseHandler::gc(),FileHandler::gc(),MemcachedHandler::gc(),PostgreHandler::gc(),RedisHandler::gc().
Property Scope Changes¶
- HTTP: The following properties have changed their scope (visibility):
CodeIgniter\HTTP\RequestTrait::$ipAddressis now private (previously protected).
Method Scope Changes¶
-
CLI:
CodeIgniter\CLI\Commands::getCommandAlternatives()is now public (previously protected). -
HTTP: The following methods have changed their scope (visibility):
CodeIgniter\HTTP\URI::setUri()is now private (previously public).CodeIgniter\HTTP\URI::refreshPath()is now protected (previously public).CodeIgniter\HTTP\SiteURI::refreshPath()is now protected (previously public).
Removed Deprecated Items¶
-
Autoloader: Removed the following deprecated methods:
CodeIgniter\Autoloader\Autoloader::sanitizeFileName()CodeIgniter\Autoloader\Autoloader::discoverComposerNamespaces()
-
Bootstrap: The deprecated system/bootstrap.php file has been removed.
-
Cache: Removed the following deprecated methods and constant:
CodeIgniter\Cache\Handlers\BaseHandler::RESERVED_CHARACTERS(deprecated since v4.1.5)CodeIgniter\Cache\Handlers\FileHandler::writeFile()(deprecated since v4.6.0)CodeIgniter\Cache\Handlers\FileHandler::deleteFile()(deprecated since v4.6.0)CodeIgniter\Cache\Handlers\FileHandler::getDirFileInfo()(deprecated since v4.6.0)CodeIgniter\Cache\Handlers\FileHandler::getFileInfo()(deprecated since v4.6.0)
-
CLI: Removed the following deprecated properties and methods:
CodeIgniter\CLI\BaseCommand::getPad()CodeIgniter\CLI\CLI::$readline_supportCodeIgniter\CLI\CLI::$wait_msgCodeIgniter\CLI\CLI::isWindows()CodeIgniter\CLI\GeneratorTrait::execute()
-
CodeIgniter: Removed the following deprecated properties and methods:
CodeIgniter\CodeIgniter::$cacheTTLCodeIgniter\CodeIgniter::$returnResponseCodeIgniter\CodeIgniter::initializeKint()CodeIgniter\CodeIgniter::detectEnvironment()CodeIgniter\CodeIgniter::bootstrapEnvironment()CodeIgniter\CodeIgniter::forceSecureAccess()CodeIgniter\CodeIgniter::displayCache()CodeIgniter\CodeIgniter::cache()CodeIgniter\CodeIgniter::cachePage()CodeIgniter\CodeIgniter::generateCacheName()CodeIgniter\CodeIgniter::displayPerformanceMetrics()CodeIgniter\CodeIgniter::determinePath()CodeIgniter\CodeIgniter::callExit()CodeIgniter\Test\MockCodeIgniter::callExit()
-
Config: Removed the following deprecated property:
CodeIgniter\Config\BaseService::$services(deprecated since v4.5.0)
-
Database: Removed the following deprecated properties and methods:
CodeIgniter\Database\BaseBuilder::setInsertBatch()CodeIgniter\Database\BaseBuilder::setUpdateBatch()CodeIgniter\Database\BaseBuilder::cleanClone()CodeIgniter\Database\BaseConnection::$strictOnCodeIgniter\Database\Forge::$createTableIfStrCodeIgniter\Database\Seeder::$fakerCodeIgniter\Database\Seeder::faker()CodeIgniter\Database\OCI8\Forge::$createTableIfStrCodeIgniter\Database\OCI8\Forge::getError()CodeIgniter\Database\SQLSRV\Forge::$createTableIfStr
-
Debug: Removed the following deprecated properties and methods:
CodeIgniter\Debug\Toolbar\Collectors\BaseCollector::cleanPath()(deprecated since v4.2.0)CodeIgniter\Debug\Exceptions::$ob_level(deprecated since v4.4.0)CodeIgniter\Debug\Exceptions::$viewPath(deprecated since v4.4.0)CodeIgniter\Debug\Exceptions::determineView()(deprecated since v4.4.0)CodeIgniter\Debug\Exceptions::render()(deprecated since v4.4.0)CodeIgniter\Debug\Exceptions::collectVars()(deprecated since v4.4.0)CodeIgniter\Debug\Exceptions::maskSensitiveData()(deprecated since v4.4.0)CodeIgniter\Debug\Exceptions::maskData()(deprecated since v4.4.0)CodeIgniter\Debug\Exceptions::cleanPath()(deprecated since v4.2.0)CodeIgniter\Debug\Exceptions::describeMemory()(deprecated since v4.4.0)CodeIgniter\Debug\Exceptions::highlightFile()(deprecated since v4.4.0)
-
Exceptions: Removed the following static constructors of
FrameworkExceptionand its child classes:CodeIgniter\Exceptions\DownloadException::forCannotSetCache()CodeIgniter\Exceptions\FrameworkException::forMissingExtension()CodeIgniter\Honeypot\Exceptions\HoneypotException::forNoHiddenValue()CodeIgniter\HTTP\Exceptions\HTTPException::forInvalidSameSiteSetting()CodeIgniter\Security\Exceptions\SecurityException::forInvalidSameSite()CodeIgniter\Session\Exceptions\SessionException::forInvalidSameSiteSetting()
-
Filters: Removed the following deprecated properties and methods:
CodeIgniter\Filters\Filters::$arguments(deprecated since v4.6.0)CodeIgniter\Filters\Filters::$argumentsClass(deprecated since v4.6.0)CodeIgniter\Filters\Filters::getArguments()(deprecated since v4.6.0)
-
HTTP: Removed the following deprecated properties and methods:
CodeIgniter\HTTP\Message::getHeaders()(deprecated since v4.0.5)CodeIgniter\HTTP\Message::getHeader()(deprecated since v4.0.5)CodeIgniter\HTTP\RequestTrait::getEnv()(deprecated since v4.4.4)CodeIgniter\HTTP\URI::$uriString(deprecated since v4.4.0)CodeIgniter\HTTP\URI::$baseURL(deprecated since v4.4.0)CodeIgniter\HTTP\URI::setBaseURL()(deprecated since v4.4.0)CodeIgniter\HTTP\URI::getBaseURL()(deprecated since v4.4.0)CodeIgniter\HTTP\URI::setScheme()(deprecated since v4.4.0)CodeIgniter\HTTP\SiteURI::$segments(deprecated since v4.4.0)CodeIgniter\HTTP\SiteURI::setBaseURL()(deprecated since v4.4.0)CodeIgniter\HTTP\SiteURI::setURI()(deprecated since v4.4.0)
-
Security: Removed the following deprecated properties and methods:
CodeIgniter\Security\SecurityInterface::sanitizeFilename()(deprecated since v4.6.2)CodeIgniter\Security\Security::sanitizeFilename()(deprecated since v4.6.2)CodeIgniter\Security\Security::$csrfProtection(deprecated since v4.4.0)CodeIgniter\Security\Security::$tokenRandomize(deprecated since v4.4.0)CodeIgniter\Security\Security::$tokenName(deprecated since v4.4.0)CodeIgniter\Security\Security::$headerName(deprecated since v4.4.0)CodeIgniter\Security\Security::$expires(deprecated since v4.4.0)CodeIgniter\Security\Security::$regenerate(deprecated since v4.4.0)CodeIgniter\Security\Security::$redirect(deprecated since v4.4.0)CodeIgniter\Security\Security::$sameSite(deprecated since v4.4.0)
Enhancements¶
API¶
- Added per-type sparse fieldsets to API transformers. A transformer may declare a
$typeso clients can request?fields[<type>]=...to filter the fields of that resource type independently, including for nested/related resources, while the flat?fields=...continues to apply to the root resource only. See API Resources.
Commands¶
- Added a new attribute-based command style built on
AbstractCommandand the#[Command]attribute, withconfigure()/isAvailable()/initialize()/interact()/execute()hooks and typedArgument/Optiondefinitions. The legacyBaseCommandstyle continues to work. See Modern Spark Commands. - Modern commands can now declare command aliases through an
aliaseslist on the#[Command]attribute. Aliases resolve to the command at dispatch (php spark <alias>andhelp <alias>), are listed as their own rows inspark list, and appear in anAliases:section ofhelp <command>. An alias that collides with an existing command name or another alias is rejected at discovery. See Modern Spark Commands. - Modern commands can now be hidden with
hidden: trueon the#[Command]attribute. A hidden command and its aliases are left out ofspark listand of the suggestions for a mistyped command name, but still run by exact name or alias and still have ahelppage.AbstractCommand::isHidden()andCommands::isHiddenCommand()report the flag. See Hidden Commands. - Modern commands can now opt out of the spark header with
headerless: trueon the#[Command]attribute, with the same effect as always passing--no-header.help <command>and<command> --helpstill print the header.AbstractCommand::isHeaderless()andCommands::isHeaderlessCommand()report the flag. See Commands Without the Header. - Every modern command now ships with a
--no-interaction/-Nflag that skips theinteract()hook, plus publicisInteractive()/setInteractive()methods onAbstractCommand.isInteractive()also auto-detects piped or CI environments by probing STDIN for a TTY, and the state cascades to sub-commands invoked via$this->call(...). See Non-Interactive Mode. spark help <command>now shows the default value of each option that accepts a value (e.g.,[default: "file"]), the same way argument defaults are already displayed. Boolean flags and negatable toggles, which have no meaningful default to show, are unaffected.- You can now retrieve the last executed command in the console using the new
Console::getCommand()method. This is useful for logging, debugging, or any situation where you need to know which command was run. CLInow supports the--separator to mean that what follows are arguments, not options. This allows you to have arguments that start with-without them being treated as options. For example:spark my:command -- --myargwill pass--myargas an argument instead of an option.CLInow supports options with values specified using an equals sign (e.g.,--option=value) in addition to the existing space-separated syntax (e.g.,--option value). This provides more flexibility in how you can pass options to commands.CLInow supports parsing array options written multiple times (e.g.,--option=value1 --option=value2) into an array of values. This allows you to easily pass multiple values for the same option without needing to use a comma-separated string. When used withCLI::getOption(), an array option will return its last value (for example, in this case,value2). To retrieve all values for an array option, useCLI::getRawOption().- Likewise, the
command()function now also supports the above enhancements for command-line option parsing when using the function to run commands from code. - Added
make:requestgenerator command to scaffold Form Request classes. - Added
key:rotatecommand to demote the currentencryption.keytoencryption.previousKeysin .env and generate a new key. See Rotating with thekey:rotateCommand. - Added
AbstractCommand::callSilently()to invoke another command with its output discarded, restoring the prior output afterwards. See Calling Silently. - The output side of the
CLIclass now lives in instance classes, withCLIdelegating to them:Output(print(),write(),error(),newLine(),deferNewLine(),beep(),clearScreen(),color(),wrap()),Terminal(dimensions and color support),ProgressBar,Table, andColor(ANSI codes andColor::width()).CLI::getOutput()andCLI::getTerminal()return the shared instances. See The Classes Behind the Facade. - Modern commands can write through
$this->getOutput(), the sameOutputused by the staticCLImethods.AbstractCommand::run()andCommands::runCommand()accept anOutputto run against, whichConsolepasses down and sub-commands inherit. See Writing Output. - The prompt methods of
CLInow delegate toPrompt(ask(),askByKey(),askByMultipleKeys()), which reads through anOutputand accepts the validation that checks the answers.CLI::getPrompt()andAbstractCommand::getPrompt()return one on the shared output and on the output of the run. See The Classes Behind the Facade. - Added
Prompt::select()andPrompt::multiselect(), which pick options with the arrow keys and the space bar, and ask for numbers where keys cannot be read. See select() and multiselect(). - Added
--silent,--quiet/-q, and--verbose/-v,-vv,-vvvto every command, withSHELL_VERBOSITYas the fallback. Modern commands can write at aVerbositythrough$this->getOutput().lang:finduses the global--verbosein place of its own. See Verbosity and Writing at a Verbosity. - Color detection now honors
FORCE_COLOR(see https://force-color.org/), which turns color on even when the output is piped, andCOLORTERM.NO_COLORstill wins over both. The color variables can also be set in the .env file. - Added
AbstractGeneratorCommandand the#[GeneratorCommand]attribute, the modern counterpart ofGeneratorTraitfor commands that generate files from templates. Generator commands built on it accept the-n,-s, and-fshortcuts for--namespace,--suffix, and--force. See Modern Generator Commands. - Legacy
BaseCommand::call()can now invoke modern commands: integer-keyed params are passed as arguments and string-keyed params as options, which the target command validates like any other input. - The
make:controllercommand now accepts-band-ras shortcuts for--bareand--restful, and rejects an invalid--restfulvalue on non-interactive runs instead of prompting. - The
make:modelcommand now accepts-t,-g, and-ras shortcuts for--table,--dbgroup, and--return, and rejects an invalid--returnvalue on non-interactive runs instead of prompting. - The
make:commandcommand now accepts-c,-t, and-gas shortcuts for--command,--type, and--group, and rejects an invalid--typevalue on non-interactive runs instead of prompting. - The
make:migrationcommand now accepts-tand-gas shortcuts for--tableand--dbgroup. - The
make:scaffoldcommand now accepts-b,-t, and-gas shortcuts for--bare,--table, and--dbgroup, and asks for a missing class name once instead of once per generated file. - spark now offers to run the closest matching command when a command name is mistyped on an interactive run. See Correcting a Mistyped Command.
- Modern commands can now opt in to prompting for missing required arguments on interactive runs by implementing the new
PromptsForMissingInputInterfacemarker interface. Prompt labels can be customized viagetArgumentPromptLabels(), and anafterPrompting()hook runs when prompting occurred. Non-interactive runs keep failing fast with the missing-arguments error. See Prompting for Missing Arguments. - The
migrate,migrate:rollback,migrate:refresh, andmigrate:statuscommands now accept long option names (--namespace,--group,--batch,--force) alongside their existing short forms (-n,-g,-b,-f). - The
namespacescommand now accepts long option names (--config-only,--raw,--max-length) alongside its existing short forms (-c,-r,-m). - The
worker:installandworker:uninstallcommands now accept the-fshort option alongside--force. - Added
NullInputOutput, anInputOutputsink that discards all writes and returns an empty string frominput(). - The
spark routescommand now resolves the Before/After Filters columns for routes that use custom placeholders registered via$routes->addPlaceholder(). Sample URIs for custom placeholders are derived from the placeholder regex when possible, and can be overridden through the new$placeholderSamplesproperty inConfig\Routing. See Sample Values forspark routes.
Testing¶
- Added
assertSameSql()toCIUnitTestCaseto compare generated SQL while ignoring newlines in the actual SQL. - Added
FeatureTestTrait::withFiles()andMockUploadedFilefor testing routes that validate and move uploaded files.
Database¶
- Added
afterCommit()andafterRollback()transaction callbacks to database connections. These callbacks run after the outermost transaction commits or rolls back. See Running Code after Commit or Rollback. - Added
ConstraintViolationExceptionwithUniqueConstraintViolationException,ForeignKeyConstraintViolationException,NotNullConstraintViolationException, andCheckConstraintViolationExceptionsubclasses for typed database integrity errors. See Constraint Violation Exceptions. - Added
inTransaction()to database connections to check whether the connection is inside an active CodeIgniter-managed transaction. See Checking Transaction State. - Added support for PHP
BackedEnumvalues in database escaping, query bindings, and Query Builder bound values. - Prepared query execution failures now throw or store typed database exceptions such as
ConstraintViolationExceptionandRetryableTransactionExceptionwhen applicable, matching normal query failures. - Added
RetryableTransactionExceptionfor driver-specific retryable transaction failures such as deadlocks and serialization failures. See Classifying Retryable Transaction Failures. - Added the
transaction()method to database connections to run a callback inside a transaction, with optional retry attempts for retryable transaction failures and scopedtransExceptionandresetTransStatusoptions. See Running Transactions with a Closure. - Added
trustServerCertificateoption toSQLSRVdatabase connections inConfig\Database. Set it totrueto trust the server certificate without CA validation when using encrypted connections.
Query Builder¶
BaseBuilder::join()now acceptsRawSqlas the table argument, allowing raw JOIN targets such as subqueries. See RawSql.- Added
exists()anddoesntExist()to Query Builder to check whether the current Query Builder query would return at least one row. See $builder->exists(). - Added
explain()to Query Builder to run execution-plan queries for the currentSELECTquery. See $builder->explain(). - Added
havingBetween(),orHavingBetween(),havingNotBetween(), andorHavingNotBetween()to Query Builder. See $builder->havingBetween(). - Added
likeAny()andorLikeAny()to Query Builder to search one value across multiple fields with groupedORLIKEconditions. See $builder->likeAny(). - Added
nowait()andskipLocked()lock wait modifiers for pessimisticSELECTlocks on supported drivers. See No Wait and Skip Locked. - Added
sharedLock()to add pessimistic read locks toSELECTqueries on supported drivers. See Shared Lock. - Added
whereBetween(),orWhereBetween(),whereNotBetween(), andorWhereNotBetween()to Query Builder. See $builder->whereBetween(). - Added
whereColumn()andorWhereColumn()to compare one column to another column while protecting identifiers by default. See $builder->whereColumn(). - Added
whereExists(),orWhereExists(),whereNotExists(), andorWhereNotExists()to addEXISTSandNOT EXISTSsubquery conditions. See $builder->whereExists(). - Added new
incrementMany()anddecrementMany()methods toCodeIgniter\Database\BaseBuilderfor performing bulk increment/decrement operations. - Added
lockForUpdate()to add pessimistic write locks toSELECTqueries on supported drivers. See Lock for Update.
Forge¶
Others¶
- Added new
timezoneoption to connection array inConfig\Databaseconfig. This ensures consistent timestamps between model operations and database functions likeNOW(). Supported drivers: MySQLi, Postgre, and OCI8. See timezone for details. - Added
$db->getLastException()which returns the typed exception even whenDBDebugisfalse. See $db->getLastException(). - Added
DatabaseException::getDatabaseCode()returning the native driver error code asint|string;getCode()is constrained tointby PHP'sThrowableinterface and cannot carry string SQLSTATE codes.
Debug¶
- Added a Copy Details button to detailed HTML exception pages.
Model¶
- Added
chunkRows(),chunkById(), andchunkRowsById()methods toCodeIgniter\Modelfor processing large datasets in chunks. - Added
encrypted,encrypted-jsonandencrypted-json-arraycasting for Entity properties and Model fields using the Encryption service. See encrypted. - Added
firstOrInsert()method toCodeIgniter\Modelthat finds the first row matching the given attributes or inserts a new one. See firstOrInsert(). - Added
$insertOnlyFieldsandsetInsertOnlyFields()toCodeIgniter\Modelto remove configured fields from Model update operations while allowing them during inserts. See $insertOnlyFields. - Added
$throwOnDisallowedFieldsandthrowOnDisallowedFields()toCodeIgniter\Modelto throw aDataExceptionwhen write data contains fields that would otherwise be discarded by$allowedFields. See $throwOnDisallowedFields. - Added
withLockedRow()toCodeIgniter\Modelto run a callback with one row reloaded inside a transaction usinglockForUpdate(). See withLockedRow().
Libraries¶
- Cache: Added support for TTL callables in the
remember()method of cache handlers. This allows you to specify a callable that returns a TTL value, which can be useful for dynamic TTL. - Context: This new feature allows you to easily set and retrieve normal or hidden contextual data for the current request. See Context for details.
- Images: Added support for the AVIF file format.
- Locks: Added Atomic Locks for owner-aware, cross-process mutual exclusion backed by supported cache handlers: File, Redis, Predis, and Memcached. Memcached support has driver-specific release limitations because Memcached has no atomic compare-and-delete command.
- Logging: Log handlers now receive the full context array as a third argument to
handle(). When$logGlobalContextis enabled, the CI global context is available under theHandlerInterface::GLOBAL_CONTEXT_KEYkey. Built-in handlers append it to the log output; custom handlers can use it for structured logging. - Logging: Added per-call context logging with three new
Config\Loggeroptions ($logContext,$logContextTrace,$logContextUsedKeys). Per PSR-3, aThrowablein theexceptioncontext key is automatically normalized to a meaningful array. All options default tofalse. - Logging: Added the
stopChainOnFailurehandler option forFileHandlerandErrorlogHandler. Set it tofalseto let the remaining handlers run when the handler fails to write the log. It defaults totrue, which keeps the existing behavior. See Stopping the Handler Chain on Failure. - Security: Added Fetch Metadata based CSRF protection with token fallback.
Helpers and Functions¶
- Array Helper gained five new dot-path functions:
dot_array_has(),dot_array_set(),dot_array_unset(),dot_array_only(), anddot_array_except(). - Array Helper dot-path read operations now support object rows (including
Entity) indot_array_search(),dot_array_has(),dot_array_only(),dot_array_except(), andarray_group_by().dot_array_only()anddot_array_except()still return arrays. If the source itself is an object, it is read as an array-like value. Object values inside the source are kept unchanged when selected as a whole or left untouched. Partial object paths are returned as arrays.
HTTP¶
- Added support for the HTTP
QUERYmethod in routing, feature tests,CURLRequest, andFormRequestinput source selection. - Added the
retryoption toCURLRequestfor retrying failed responses with configurable delays, retryable status codes, optional transient cURL error retries, andRetry-Aftersupport. See retry. - Added Form Requests - a new
FormRequestbase class that encapsulates validation rules, custom error messages, and authorization logic for a single HTTP request. - Added
IncomingRequest::input()to read GET, POST, JSON, and raw request data throughInputData. - Added support for streaming responses. The
StreamResponseclass streams a body that is generated while it is being sent, such as large dataset exports or AI/LLM token streams, and theSSEResponseclass extends it to stream Server-Sent Events (SSE) over HTTP. Both can be created via the new$this->response->stream()and$this->response->eventStream()factory methods. See Streaming Responses and Server-Sent Events (SSE). Responseand its child classes no longer requireConfig\Appto be passed to their constructors. Consequently,CURLRequest's$configparameter is unused and will be removed in a future release.Responsenow lazily loads theContentSecurityPolicy,Cookie, andCookieStoreclasses only when used, instead of instantiating them on every request in the constructor. Requests that do not touch CSP or cookies no longer incur the cost of loading these classes.CLIRequestnow supports the--separator to mean that what follows are arguments, not options. This allows you to have arguments that start with-without them being treated as options. For example:php index.php command -- --myargwill pass--myargas an argument instead of an option.CLIRequestnow supports options with values specified using an equals sign (e.g.,--option=value) in addition to the existing space-separated syntax (e.g.,--option value). This provides more flexibility in how you can pass options to CLI requests.- Added
enable()anddisable()methods toContentSecurityPolicyto enable or disable the CSP header for the current request. See Enable or Disable CSP at Runtime. - Added
$enableStyleNonceand$enableScriptNonceoptions toConfig\Appto control whether nonces are automatically added to thestyle-*andscript-*directives in the Content Security Policy (CSP) header when CSP is enabled. See Control Nonce Generation for details. - Added
URI::withQuery(),URI::withQueryArray(),URI::withoutQueryVars(), andURI::withOnlyQueryVars()to return cloned URIs with replaced or filtered query variables. - Added
URI::withQueryVar()andURI::withQueryVars()to return a cloned URI with query variables added or replaced. URInow accepts an optional boolean second parameter in the constructor, defaulting tofalse, to control how the query string is parsed on instantiation. This is the behavior of->useRawQueryString()brought into the constructor for convenience. Previously, you had to call$uri->useRawQueryString(true)->setURI($uri)to get this behavior. Now you can simply donew URI($uri, true).CLIRequestnow supports parsing array options written multiple times (e.g.,--option=value1 --option=value2) into an array of values. This allows you to easily pass multiple values for the same option without needing to use a comma-separated string. When used withCLIRequest::getOption(), an array option will return its last value (for example, in this case,value2). To retrieve all values for an array option, useCLIRequest::getRawOption().
Validation¶
- Custom rule methods that set an error via the
&$errorreference parameter now support the{field},{param}, and{value}placeholders, consistent with language-file andsetRule()/setRules()error messages. - Added
Validation::getValidatedInput()to access validated data through aValidatedInputobject.
Others¶
- Config: Added optional
CodeIgniter\Config\Mergedirectives for Registrars to control replacing, deep merging, and ordered list additions. Existing registrar merge behavior is unchanged. See Controlling how values are merged. - Float and Double Casting: Added support for precision and rounding mode when casting to float or double in entities.
- Added
CodeIgniter\Input\InputData,ValidatedInput, andInputDataFactoryfor reusable typed input data objects. - Float and Double casting now throws
CastException::forInvalidFloatRoundingMode()if a rounding mode other than up, down, even, or odd is provided. - Filters: Added
RequestIdfilter for request tracing and correlation logging. The filter stores the request ID in the request context and automatically adds theX-Request-IDresponse header. IncomingX-Request-IDheaders are used when valid. See Request ID for details. - Environment: Added
CodeIgniter\EnvironmentDetectorclass and correspondingenvironmentservice as a mockable wrapper around theENVIRONMENTconstant. Framework internals that previously comparedENVIRONMENTdirectly now go through this service, making environment-specific branches reachable in tests viaServices::injectMock(). See Theenvironmentservice. - Time: Added
Time::between(),Time::min(), andTime::max()comparison helpers. See between, min, and max.
Message Changes¶
-
Added new language keys:
Cache.unsupportedLockStore(CacheException::forUnsupportedLockStore())CLI.commandAliasandCLI.helpAliases(command alias rendering inlistandhelp)CLI.altCommandNone,CLI.altCommandRun, andCLI.altCommandSelect(correcting a mistyped command)CLI.generator.invalidCommandType(make:commandrejecting an invalid--type)CLI.generator.invalidReturnType(make:modelrejecting an invalid--return)Commands.invalidCommandAlias,Commands.commandAliasSameAsName,Commands.duplicateCommandAlias,Commands.aliasClashesWithCommandName, andCommands.aliasClashesWithAlias(command alias validation)
-
Removed deprecated language keys tied to removed exception constructors:
Core.missingExtension(FrameworkException::forMissingExtension())HTTP.cannotSetCache(DownloadException::forCannotSetCache())HTTP.disallowedActionHTTP.invalidSameSiteSetting(HTTPException::forInvalidSameSiteSetting())Security.invalidSameSite(SecurityException::forInvalidSameSite())Session.invalidSameSiteSetting(SessionException::forInvalidSameSiteSetting())
Changes¶
- Config: Added the
mdkey forConfig\Mimes::$mimesfor Markdown files. - Router: Reduced route registration overhead in
RouteCollection::create()by skipping placeholder replacement for route keys without:and callinghelper('array')only once per collection.
Deprecations¶
- CLI: The
CLI::parseCommandLine()method is now deprecated and will be removed in a future release. TheCLIclass now uses the newCommandLineParserclass to handle command-line argument parsing. - CLI: Returning a non-integer exit code from a command is now deprecated and will trigger a deprecation notice. Command methods should return an integer exit code (e.g.,
0for success, non-zero for errors) to ensure proper behavior across all platforms. - CLI:
Commands::run()is now deprecated in favor ofCommands::runLegacy()for legacyBaseCommandcommands, andCommands::runCommand()for modernAbstractCommandcommands. - CLI: The
$commandsparameter ofCommands::verifyCommand()and the$collectionparameter ofCommands::getCommandAlternatives()are no longer used. Passing a non-empty array for either will trigger a deprecation notice. - CLI:
BaseCommandis now deprecated in favor ofAbstractCommand. Running a command that extends it triggers a deprecation notice. See Modern Spark Commands. - CLI:
GeneratorTraitis now deprecated in favor ofAbstractGeneratorCommand. See Modern Generator Commands. - CLI: The
CLI.generator.viewName.celllanguage string is deprecated. It was never displayed, sincemake:cellonly prompts for the class name. - CLI: The
CLI::$foreground_colors,CLI::$background_colors,CLI::$lastWrite,CLI::$height,CLI::$width,CLI::$isColored,CLI::$io, andCLI::$stdoutToStderrproperties are deprecated and no longer used. Their state lives inColor,Output, andTerminal. See CLI Output Classes. - CLI:
CLI::validate()is deprecated and no longer used by the prompts. Calling it triggers a deprecation notice. OverridePrompt::validate()instead. See CLI Prompt. - HTTP: The
CLIRequest::parseCommand()method is now deprecated and will be removed in a future release. TheCLIRequestclass now uses the newCommandLineParserclass to handle command-line argument parsing. - HTTP:
URI::setSilent()is now hard deprecated. This method was previously only marked as deprecated. It will now trigger a deprecation notice when used.
Bugs Fixed¶
See the repo's CHANGELOG.md for a complete list of bugs fixed.