Jay GajjarSenior Software Engineer

CodeIgniter 3 on PHP 8: errors on PHP 8.1 to 8.5 and how to fix them

Running CodeIgniter 3 on PHP 8? The errors you will see on 8.1, 8.2, 8.4 and 8.5, tested fixes for each, and when patching stops being worth it.

By Jay Gajjar9 min read

CodeIgniter 3 on PHP 8 works up to PHP 8.1 if you are on CodeIgniter 3.1.12 or 3.1.13, and from PHP 8.2 onward it needs patches. On 8.2 and 8.3 the main symptom is a flood of "Creation of dynamic property ... is deprecated" notices, PHP 8.4 adds deprecations around E_STRICT, session ID length and mysqli::ping(), and PHP 8.5 adds a few more in the image and upload libraries. All of these have small, known fixes. The real question is how long you want to keep patching a framework that has not had an official release since March 2022.

This guide lists the errors you will actually see on each PHP version, the fix for each, and the point where patching stops being sensible. I checked the CodeIgniter 3.1.13 source and the PHP migration notes for each fix, and ran a stock 3.1.13 install on PHP 8.3 to confirm the dynamic property fix.

Quick answer

  1. Upgrade the system/ folder to CodeIgniter 3.1.13 first. It fixes most PHP 8.0 and 8.1 problems.
  2. For PHP 8.2 and 8.3, add #[\AllowDynamicProperties] through MY_ subclasses of CI_Controller, CI_Loader, CI_Router and CI_URI, and declare properties in your own classes.
  3. For PHP 8.4, remove E_STRICT from index.php and system/core/Exceptions.php, guard the session ID length code, and stop calling $this->db->reconnect() with MySQLi.
  4. For PHP 8.5, expect deprecations from imagedestroy() in Image_lib and finfo_close() in Upload.
  5. Fix your own code for PHP 8 behavior changes (null arguments, count() on null, string comparisons).
  6. Plan a move to CodeIgniter 4 or Laravel. Patches keep you running. They do not give you support.

Which PHP versions does CodeIgniter 3 support?

Officially, CodeIgniter 3.1.13 recommends PHP 5.6 or newer and its changelog (opens in a new tab) covers PHP 8.0 and 8.1 compatibility work. Nothing newer. The repository describes CI3 as "in maintenance, receiving mostly just security updates", and the develop branch (3.2.0-dev) has never been released. Here is what each PHP version means in practice:

PHP version Security support (php.net) CodeIgniter 3.1.13 status
8.0, 8.1 Ended Works. 3.1.12 added PHP 8.0/8.1 fixes and a session handler wrapper for 8.1
8.2 Until Dec 31, 2026 Runs, but raises dynamic property deprecations on every request
8.3 Until Dec 31, 2027 Same as 8.2 in the core; no new core failures in my testing
8.4 Until Dec 31, 2028 Adds E_STRICT, session ID length and mysqli::ping() deprecations
8.5 Until Dec 31, 2029 Adds imagedestroy(), finfo_close() and xml_parser_free() deprecations

Deprecations are not fatal errors. With CI3's default production settings, index.php excludes E_DEPRECATED from error reporting and turns off display_errors, so visitors see nothing. You see them in development and in logs, and they matter because each one marks code that a future PHP version will break. The PHP RFC that deprecated dynamic properties, for example, plans to turn them into an Error exception in PHP 9.

Step 0: check your CodeIgniter 3 version

Many "CodeIgniter 3 PHP 8" problems disappear on the latest 3.1.x. Check the constant:

grep -n "CI_VERSION" system/core/CodeIgniter.php

If it says anything below 3.1.13, replace the system/ folder with the one from 3.1.13 (keep a backup and read the upgrade notes for each version you skip). Two PHP 8.1 issues in particular are fixed there:

  • Session handler deprecations. Older versions log "Return type of CI_Session_files_driver::open(...) should either be compatible with SessionHandlerInterface::open(string $path, string $name): bool". 3.1.12 added a wrapper class that handles this.
  • MySQLi exceptions. PHP 8.1 changed MySQLi's default error mode to throw exceptions. The 3.1.13 MySQLi driver switches it back to MYSQLI_REPORT_OFF on connect, so CodeIgniter's own database error handling keeps working. On older versions a failed query can throw an uncaught mysqli_sql_exception instead.

Never edit files in system/ without a record of what you changed. I keep a short PATCHES.md in the repository and prefer MY_ overrides in application/core wherever possible, as below.

CodeIgniter 3 PHP 8.2 and 8.3 errors: "Creation of dynamic property is deprecated"

This is the error everyone hits. PHP 8.2 deprecated creating a property (opens in a new tab) that a class does not declare, and CodeIgniter 3 does that constantly: the controller gets $this->load, $this->config, $this->uri and every loaded model or library assigned at runtime. On a fresh 3.1.13 install running on PHP 8.3, the default welcome page alone produced 27 notices like these:

Message: Creation of dynamic property CI_URI::$config is deprecated
Message: Creation of dynamic property CI_Router::$uri is deprecated
Message: Creation of dynamic property CI_Loader::$benchmark is deprecated
Message: Creation of dynamic property Welcome::$benchmark is deprecated

The fix without touching system/

PHP 8.2 added the #[\AllowDynamicProperties] attribute, and child classes inherit it. CodeIgniter already looks for MY_ subclasses of core classes in application/core, so you can apply the attribute there. Create four small files:

<?php
// application/core/MY_URI.php
defined('BASEPATH') OR exit('No direct script access allowed');

#[\AllowDynamicProperties]
class MY_URI extends CI_URI {}

Repeat the same pattern for MY_Router extends CI_Router, MY_Loader extends CI_Loader and MY_Controller extends CI_Controller, then make your controllers extend MY_Controller. If the app already has these classes (HMVC setups usually ship their own MY_Loader and MY_Router), add the attribute line to the existing class instead of creating a new one.

I tested this on PHP 8.3 with a controller that loads the database, a model, the form validation library and the URL and form helpers. With the four MY_ classes in place, it ran with zero notices. The attribute line is safe on PHP 7 too: before PHP 8.0, # starts a comment, so older PHP simply ignores it.

Your own classes

The same notice appears for your models and libraries when they set properties they never declared:

class Invoice_lib
{
    public function __construct()
    {
        $this->CI =& get_instance(); // dynamic property on PHP 8.2+
    }
}

The clean fix is to declare the property (protected $CI;). Add the attribute only where a class genuinely stores arbitrary properties. If a notice names a class you do not recognize, such as a database driver or third-party library, the same two options apply.

PHP 8.1 and later: errors in your own code

Most errors after a PHP 8 upgrade come from application code, not from the framework. These are the ones I see most in CodeIgniter 3 apps.

Passing null to built-in functions (8.1, deprecated). $this->input->post() and $this->input->get() return NULL for missing fields, and that null goes straight into string functions:

// Deprecated: trim(): Passing null to parameter #1 ($string) of type string is deprecated
$name = trim($this->input->post('name'));

// Fix
$name = trim((string) $this->input->post('name'));
// or
$name = trim($this->input->post('name') ?? '');

count() on null or a non-array (8.0, fatal). This one is not a deprecation. It throws a TypeError and stops the request:

// TypeError: count(): Argument #1 ($value) must be of type Countable|array, null given
if (count($this->db->get_where('users', ['email' => $email])->row()) > 0) { ... }

// Fix: check what you actually mean
if ($this->db->get_where('users', ['email' => $email])->num_rows() > 0) { ... }

String to number comparisons (8.0, silent behavior change). 0 == 'abc' was true in PHP 7 and is false in PHP 8. Login checks, status flags and "is this field empty" logic built on loose comparisons can change behavior without any error. Search for == 0 and == '0' and use strict comparisons.

Removed functions (8.0, fatal). each(), create_function() and money_format() are gone. Old third-party libraries in application/third_party are the usual source.

For a wider checklist of PHP 8 changes outside CodeIgniter, see my PHP 8 upgrade checklist.

CodeIgniter 3 PHP 8.4 errors: E_STRICT, session ID length and mysqli ping

The PHP 8.4 deprecations (opens in a new tab) hit three places in the CodeIgniter 3.1.13 core.

"Constant E_STRICT is deprecated"

PHP removed the E_STRICT error level and deprecated the constant in 8.4. CodeIgniter 3 references it in two places: the production branch of index.php and the $levels array in system/core/Exceptions.php. The PHP RFC behind the change notes that the level no longer has any meaning, so removing the references changes nothing functionally.

// index.php, testing/production branch
error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED & ~E_USER_NOTICE & ~E_USER_DEPRECATED);

In system/core/Exceptions.php, delete the E_STRICT => 'Runtime Notice' line from $levels, or override the class with an application/core/MY_Exceptions.php that redefines $levels without it.

"session.sid_length INI setting is deprecated"

PHP 8.4 deprecated changing session.sid_length and session.sid_bits_per_character. CodeIgniter's Session library has a _configure_sid_length() method that calls ini_set('session.sid_length', ...) whenever the session ID carries fewer than 160 bits, which is the case with PHP's built-in defaults of 32 characters at 4 bits each. The method also builds the regular expression that validates session cookies, so the fix has to keep the two in sync. In system/libraries/Session/Session.php, change the condition so PHP 8.4+ keeps its own length:

// before
if (($bits = $sid_length * $bits_per_character) < 160)

// after
if (($bits = $sid_length * $bits_per_character) < 160 && PHP_VERSION_ID < 80400)

Do not simply delete the ini_set() line. The code would then expect 40-character IDs while PHP issues 32-character ones, and every session would be rejected. Expect existing users to be logged out once after deploying this change, because their old cookies have the longer ID.

mysqli::ping() is deprecated

PHP 8.4 deprecated mysqli::ping() because the reconnect feature it relied on was removed in PHP 8.2. CodeIgniter's MySQLi driver calls it inside $this->db->reconnect(). Most web requests never call that method, but long-running CLI scripts often do. Replace the call with an explicit close and reconnect:

// instead of $this->db->reconnect();
$this->db->close();
$this->db->initialize();

Implicitly nullable parameters

Also new in 8.4: function find(Customer $c = null) is deprecated. Write ?Customer $c = null. The CI3 core does not use typed parameters, so this only affects your own code and libraries.

PHP 8.5: what changes for CodeIgniter 3

PHP 8.5 deprecated several cleanup functions that no longer do anything because the objects free themselves. In CodeIgniter 3.1.13 that means imagedestroy() calls in system/libraries/Image_lib.php, finfo_close() in system/libraries/Upload.php and xml_parser_free() in the XML-RPC library. They produce deprecation notices, not failures, when you resize images or validate uploads. Wrapping them in if (PHP_VERSION_ID < 80500) silences them.

PHP 8.5 also deprecated the cast names (boolean), (integer), (double) and (binary), the backtick operator, and curl_close(). I found none of these in the 3.1.13 system/ folder, but older application code and third-party libraries use them often. A quick search finds them:

grep -rnE "\((boolean|integer|double|binary)\)|curl_close\(" application/

Or use a maintained CodeIgniter 3 fork

If you would rather not maintain your own patches, the community fork pocketarc/codeigniter (opens in a new tab) states support for PHP 8.2 to 8.5 and was last published on Packagist in September 2026. You install it with Composer and point $system_path in index.php at vendor/pocketarc/codeigniter/system. It is not an official CodeIgniter project, so review its changes and test the whole app before you trust it in production. It is a reasonable bridge. It is still CI3.

When patching stops being sensible

Patching for CodeIgniter 3 PHP 8 compatibility is cheap when the app is stable and small. It stops making sense when:

  • You are patching every PHP release. Each new version adds deprecations, and PHP 9 is expected to turn dynamic properties into hard errors.
  • The app is still growing. New features built on CI3 add to the code you will eventually migrate.
  • Security matters. Card data, personal data or financial records deserve a framework that gets regular security releases.
  • Libraries are stuck. When the payment SDK or PDF library you need requires PHP 8.3+ and modern Composer packages, CI3 starts to block you.
  • Nobody wants to touch it. Developers who know CI3 internals are getting harder to find.

At that point the choice is between CodeIgniter 4 and Laravel. My CodeIgniter 3 to 4 migration guide walks through both routes, and my CodeIgniter 4 migration service covers doing it for you, from audit to cutover.

Frequently asked questions

Does CodeIgniter 3 support PHP 8.2?

Not officially. CodeIgniter 3.1.13 runs on PHP 8.2, but it raises "Creation of dynamic property" deprecation notices that you fix with the #[\AllowDynamicProperties] attribute or by declaring properties. No official CI3 release has added PHP 8.2 support.

Can CodeIgniter 3 run on PHP 8.4 or 8.5?

Yes, with the patches above for dynamic properties, E_STRICT, the session ID length and mysqli::ping(), plus the PHP 8.5 library cleanups. Test file uploads, image resizing and sessions carefully after each PHP upgrade, because those paths are where the framework's deprecations sit.

Is it safe to hide deprecation warnings instead of fixing them?

As a short-term measure, yes. CI3's production settings already exclude E_DEPRECATED. But deprecations mark code that a future PHP version will break, so log them and fix them rather than switching them off and forgetting.

What PHP version does CodeIgniter 4 need?

CodeIgniter 4.7 requires PHP 8.2 or newer with the intl and mbstring extensions. It supports current PHP versions without patches.

If your host is about to force a PHP upgrade and your CodeIgniter 3 app is not ready, I can patch it safely and plan the next step with you through my PHP and Laravel maintenance service.