Skip to main content

Cloudsoft Zone Build, deploy and scale with practical guides to PHP, Node.js, React, DevOps and AI automation. Explore the guides

Cloudsoft Zone
CodeIgniterDevelopmentPHPIndustry update

What's new in CodeIgniter 4.7 and how to upgrade from 4.6

CodeIgniter 4.7 requires PHP 8.2 and adds experimental FrankenPHP worker mode, controller attributes, API transformers and API pagination. Here is what changed, what can break, the 4.7.4 security fixes and an upgrade checklist.

Cloudsoft Zone

Updated 8 min read

Computer monitor filled with colourful lines of source code photographed at an angle
Photograph: Markus Spiske markusspiske / CC0, via Wikimedia Commons
In this article

CodeIgniter 4.7.0 was released on 1 February 2026, and the 4.7 branch has since reached 4.7.4 (7 July 2026), which included four security fixes. It is the largest feature release the framework has had in some time: a new minimum PHP version, an experimental worker mode that keeps the application in memory between requests, PHP attributes on controllers, and API transformers with built-in pagination.

It also contains breaking changes that can surface quietly in existing applications, for example in validation rules and model primary-key handling. This article summarises what changed, explains who benefits from each feature, and ends with an upgrade checklist for teams moving from 4.6. Every item is taken from the official change logs and upgrade guide.

The CodeIgniter team maintains only the latest version, so staying on an old minor release means going without security fixes. That alone makes 4.7 worth planning for.

The headline changes at a glance

AreaWhat changed in 4.7Who should care
PHP requirementMinimum raised to PHP 8.2; PHP 8.5 supported from 4.7.0Everyone, especially shared hosting users
Worker ModeExperimental FrankenPHP support with php spark worker:installHigh-traffic, database-heavy apps
Controller Attributes#[Filter], #[Restrict] and #[Cache] on controllers and methodsTeams that prefer configuration next to code
API TransformersBaseTransformer, make:transformer, fields and include query parametersAnyone building JSON APIs
Pagination for APIsResponseTrait::paginate() returns data, meta and linksAPI developers
MigrationsOptional distributed lock with Config\Migrations::$lockMulti-server deployments
EncryptionKey rotation with Config\Encryption::$previousKeysApps that store encrypted data
CacheNew APCu driver and a remember() method on the cache interfacePerformance work
Purple plush elephant toy, the PHP elePHPant mascot, standing on a white background
The elePHPant, PHP's community mascot. CodeIgniter 4.7 supports PHP 8.2 through 8.5.

Worker Mode: what it is and when to use it

Traditional PHP-FPM uses a "shared nothing" model: every request loads files, boots the framework, opens database connections and then throws it all away. Worker Mode keeps one PHP process alive. The framework boots once, then the worker handles request after request, resetting only request-specific state such as superglobals and the request and response objects.

The user guide describes typical gains of two to three times for database-driven endpoints, 10-30% for simple JSON endpoints with little bootstrap work, and the largest improvements for applications with many services and routes. Treat those as the project's own figures; measure your application before and after.

Setup is short. Install FrankenPHP (the only officially supported worker implementation), then:

Shell
php spark worker:install   # creates Caddyfile and public/frankenphp-worker.php
frankenphp run             # or 'frankenphp start' to run in the background

The feature is marked experimental, and long-lived processes change how you must write code:

  • Do not keep request-specific data in static properties or singletons; it will leak into the next request.
  • Do not enable config caching or file-locator caching in app/Config/Optimize.php, and do not run spark optimize. The user guide is explicit that these break in worker mode.
  • Restart workers after deploying code. They do not reload changed files.
  • Prefer Redis or Memcached for sessions and cache; file handlers can suffer lock contention between workers.
  • Only add services to $persistentServices in app/Config/WorkerMode.php if they are truly stateless.

Database connections are checked at the start of each request and re-established if they have dropped, and uncommitted transactions are rolled back with a warning. Version 4.7.1 added $resetEventListeners for applications that register event listeners inside other event callbacks.

Should you adopt it now? For most small business applications on shared hosting or a modest VPS, the honest answer is not yet: PHP-FPM with OPcache is well understood and fast enough, and moving to FrankenPHP changes your web server as well as your PHP runtime. Worker Mode is worth a trial if you run an API with steady traffic, a profiler shows that framework bootstrap and connection setup take a large share of each request, and your team can review the code for shared state. Run the trial on staging with production-like data, compare response times and memory use, and keep the PHP-FPM setup ready to switch back.

Controller Attributes

Filters were previously assigned in app/Config/Filters.php or in route options. In 4.7 you can declare them on the controller itself, and this works with every routing style, including auto-routing:

PHP
<?php

namespace App\Controllers;

use CodeIgniter\Router\Attributes\Filter;
use CodeIgniter\Router\Attributes\Restrict;

#[Filter(by: 'group', having: ['admin', 'superadmin'])]
class Reports extends BaseController
{
    #[Filter(by: 'permission', having: ['reports.export'])]
    public function export()
    {
        // ...
    }

    #[Restrict(environment: ['development'])]
    public function debugDump()
    {
        // Only reachable in development
    }
}

The advantage is that a reviewer sees the access rules next to the code they protect. The risk is duplication: if the same filter is also applied in the configuration file, both run. Choose one convention per project. If you do not use attributes at all, you can turn the feature off with $useControllerAttributes = false in app/Config/Routing.php.

API Transformers and pagination

Transformers formalise a pattern many teams wrote by hand: a class that converts a model row or entity into the public JSON shape. Generate one with php spark make:transformer User --suffix, implement toArray(), and call transform() or transformMany(). Clients can request a subset of fields with ?fields=id,name and related resources with ?include=posts, and you can restrict both with getAllowedFields() and getAllowedIncludes().

Pagination joins in through ResponseTrait::paginate($model, 20, UserTransformer::class), which reads the page from the query string and returns consistent meta and links blocks. Note that 4.7.4 fixed a bug where fields and include parameters leaked into nested transformers, sometimes causing infinite recursion, so use the latest patch release if you adopt this feature.

Technician crouching with a laptop while working on cabling inside an open server rack
Worker mode keeps PHP processes alive between requests, so restart workers after every deploy.

Smaller additions worth knowing

  • Encryption key rotation: list old keys in $previousKeys and decryption falls back to them, so you can rotate keys without losing access to existing data.
  • Content Security Policy Level 3: new keywords such as 'strict-dynamic' and directives such as script-src-elem and worker-src, plus hash sources for scripts and styles.
  • Database: a ping() method on connections, and uniform exception logging across drivers.
  • Page cache: Config\Cache::$cacheStatusCodes controls which status codes are cached; the docs recommend [200].
  • Views: override a module's namespaced views by placing files under app/Views/overrides.
  • Time: isPast(), isFuture(), addCalendarMonths() and subCalendarMonths().
  • Images: the ImageMagick handler now uses only the imagick extension and can strip metadata with clearMetadata().

Breaking changes that can bite

Most applications upgrade without trouble, but these changes alter behaviour without necessarily causing an obvious error:

regex_match placeholders

Placeholders inside the regex_match validation rule now need double braces, to avoid confusion with regex quantifiers like {1,3}. Search your rules for regex_match and change {placeholder} to {{placeholder}}.

Primary key validation in models

insert() (without auto-increment), update() and delete() now reject values such as null, 0, '0', '', booleans and empty arrays before any query runs. They throw InvalidArgumentException instead of DatabaseException. If you catch database exceptions around these calls, catch the new type too. Also, insertBatch() and updateBatch() now honour updateOnlyChanged and allowEmptyInserts.

Deep change detection in entities

Entity::hasChanged() now compares arrays, objects, enums and dates by value. Code that modified a nested object and relied on the entity not noticing will now trigger saves it did not before.

Encryption handler state

Passing a key in $params to encrypt() or decrypt() no longer changes the handler's stored key. If you passed a key once and expected it to stick, configure it in Config\Encryption instead.

Removed deprecated items

Among others: Entity::setAttributes() (use injectRawData()), Session::stop() and several session properties, and the basic, md5 and sha1 types in random_string().

Security fixes in 4.7.4

The July 2026 patch release fixed four issues, each with a GitHub security advisory. If you are on any earlier 4.7 release, these alone justify updating:

  • IncomingRequest::isSecure() now trusts X-Forwarded-Proto and Front-End-Https only from proxies listed in Config\App::$proxyIPs.
  • An SQL injection in Query Builder's deleteBatch() when combined with where() conditions.
  • UploadedFile::move() now sanitises the client filename when no name is given, closing a path traversal route.
  • The is_image and mime_in rules now also check the client file extension, so a PHP file with image magic bytes no longer passes.

Upgrade checklist: 4.6 to 4.7

  1. Upgrade PHP to 8.2 or newer first (PHP 8.2 itself reaches end of life on 31 December 2026, so 8.3 or later is the better target).
  2. Run composer update so codeigniter4/framework moves to 4.7.x. Check that composer.json does not pin an exact older version.
  3. Merge project-space changes. The upgrade guide lists the changed files; new ones include app/Config/Hostnames.php and app/Config/WorkerMode.php, and app/Config/Migrations.php gains $lock. Also review public/index.php and spark.
  4. Search for regex_match rules and update placeholders.
  5. Review code that catches DatabaseException around model writes.
  6. Review entity save flows that depend on change detection.
  7. If you implement framework interfaces (cache handlers, query classes, image handlers), add the new methods and native types.
  8. Run your test suite in the testing environment, then in a staging environment with production settings.
  9. Deploy, then confirm with php spark phpini:check and your health checks.

Summary

CodeIgniter 4.7 brings modern PHP features into the framework without changing its character: it is still small, explicit and quick to learn. For most teams the immediate value lies in transformers, API pagination and controller attributes. Worker Mode is promising but experimental; try it on a staging copy of a database-heavy service and measure. Whatever you adopt, upgrade to the latest 4.7 patch release for the security fixes, and read the breaking-changes list before you run composer update in production.

Frequently asked questions

  • When was CodeIgniter 4.7 released?

    Version 4.7.0 was released on 1 February 2026. The latest patch at the time of writing is 4.7.4, released on 7 July 2026.

  • Is CodeIgniter Worker Mode ready for production?

    The user guide marks it as experimental, with FrankenPHP as the only officially supported implementation. Test it on staging, measure, and review your code for state that persists between requests before relying on it.

  • Do I have to use controller attributes?

    No. Filters in app/Config/Filters.php and route options still work. Attributes are optional and can be disabled with $useControllerAttributes = false in app/Config/Routing.php.

  • Can I run CodeIgniter 4.7 on PHP 8.1?

    No. CodeIgniter 4.7 requires PHP 8.2 or newer. Upgrade PHP before upgrading the framework.

Advertise on CloudSoftZone

Your next customer could start right here.

Introduce your tool, product or business to readers exploring technology.

Advertise with us

Explore placements and request a private quote

Sources

  1. Version 4.7.0 change log — CodeIgniter Foundation, 2026-02-01
  2. Version 4.7.4 change log — CodeIgniter Foundation, 2026-07-07
  3. Upgrading from 4.6.x to 4.7.0 — CodeIgniter Foundation
  4. Worker Mode — CodeIgniter Foundation
  5. Controller Attributes — CodeIgniter Foundation
  6. API Resources (Transformers) — CodeIgniter Foundation
  7. Server Requirements — CodeIgniter Foundation
  8. CodeIgniter4 releases — GitHub

Written with substantial AI assistance. Facts checked against cited sources. Editorial policy.

Comments

No comments yet. Be the first to join the conversation.

Leave a comment

Every comment is read by a moderator before it appears. Your email address is never published; it is used only to contact you about this comment.

Never published. Used only to contact you about this comment.

Loading the security check…

Comments are moderated before publication.