Web Development

Migrating From CodeIgniter 3 to CodeIgniter 4: What Changes

CodeIgniter 4 is a rewrite, so moving from version 3 is a conversion. Here is what changes in structure, controllers, models, routing and libraries, and how to convert without stopping the business.

Migrating From CodeIgniter 3 to CodeIgniter 4: What Changes

There is no update button for this one. The CodeIgniter user guide is blunt about it: version 4 is a rewrite, old code does not run on it, and the work is a conversion of your application. A CodeIgniter 3 to 4 migration means starting a fresh CodeIgniter 4 project and moving your controllers, models, views and libraries into it, changing each as it goes.

That sounds worse than it is. The ideas are the same, the database can stay as it is, and much of the work is repetitive. What you need is a clear picture of what changes and an order of work, and that is what follows.

Why CodeIgniter 3 code will not run in version 4

CodeIgniter 3 is built around one global object. Controllers extend CI_Controller, and everything you load (models, libraries, the database, the session) appears as a property on $this. File names and class names follow the framework's own loading rules.

CodeIgniter 4 drops that object. Every class has a namespace and is found by PSR-4 autoloading. Framework components are requested as services. Requests and responses are objects that controllers receive and return. None of this is exotic, since it is how current PHP is written, but it touches almost every file. The official guide on upgrading from 3.x to 4.x is the reference to keep open while you work.

The project layout and the web root move

  • application/ becomes app/, with Controllers, Models, Views, Config, Libraries and Filters inside it.
  • index.php moves into a new public/ folder, and the web server's document root has to point there. Nothing outside public/ is reachable from a browser, so the defined('BASEPATH') OR exit(...) line at the top of every file goes away.
  • Cache, logs, session files and uploads live in writable/.
  • Configuration files are classes in app/Config with public properties. Values that differ per server go into a .env file, including CI_ENVIRONMENT.
  • There is no application/core folder and no MY_ prefix. A shared parent controller is app/Controllers/BaseController.php, and replacements for framework classes are registered in app/Config/Services.php.

Controllers, before and after

The same action in both versions shows most of the pattern. First CodeIgniter 3:

<?php
defined('BASEPATH') OR exit('No direct script access allowed');

class Orders extends CI_Controller
{
    public function show($id)
    {
        $this->load->model('order_model');
        $data['order'] = $this->order_model->find($id);

        $this->load->view('orders/show', $data);
    }
}

And CodeIgniter 4:

<?php

namespace App\Controllers;

use App\Models\OrderModel;
use CodeIgniter\Exceptions\PageNotFoundException;

class Orders extends BaseController
{
    public function show($id = null): string
    {
        $order = model(OrderModel::class)->find($id);

        if ($order === null) {
            throw PageNotFoundException::forPageNotFound();
        }

        return view('orders/show', ['order' => $order]);
    }
}

The controller returns its output where the old one echoed it through the loader. Redirects follow the same rule: redirect('login') becomes return redirect()->to('login'), and forgetting the return is the most common bug in a freshly converted controller, because the redirect then never happens.

A map of the calls you will change most

CodeIgniter 3CodeIgniter 4
$this->input->post('email')$this->request->getPost('email')
$this->load->view('page', $data)return view('page', $data)
$this->load->library('pdf')new \App\Libraries\Pdf()
$this->session->set_userdata($data)session()->set($data)
$this->form_validation->run()$this->validateData($data, $rules)
$this->config->item('per_page')config('Site')->perPage
$query->result_array()$query->getResultArray()
Hooks in config/hooks.phpEvents, or controller filters for code that ran before and after controllers

Database method names follow one rule: snake_case becomes camelCase, so affected_rows() is now affectedRows().

Models and the database layer

Your tables do not change. How the code reaches them does. A CodeIgniter 3 model is an empty class in which you write Query Builder calls on $this->db. A CodeIgniter 4 model knows its table and has find(), findAll(), insert(), update() and delete() built in:

<?php

namespace App\Models;

use CodeIgniter\Model;

class OrderModel extends Model
{
    protected $table         = 'orders';
    protected $primaryKey    = 'id';
    protected $returnType    = 'array';
    protected $allowedFields = ['customer_id', 'status', 'total'];
}

Three details catch people out:

  • $allowedFields is required for inserts and updates through the model. A field that is not listed is silently left out, which looks like a save that half worked.
  • The Query Builder is started from a table: $db->table('orders')->where(...)->get(). Custom queries from old models can be carried over this way before you rewrite them to use the model's own methods.
  • Database migrations must use timestamp names. Sequential names such as 001_create_users are no longer supported, the migrations table has a new structure, and migrations run from the command line with php spark migrate.

The database result cache from version 3 is gone. If you relied on it, use the Cache library around the queries that need it. If you stored sessions in the database, the session table definition changed and has to be recreated, which means every user signs in again after the switch.

Routing is explicit now

In CodeIgniter 3 any public controller method was reachable by its address. CodeIgniter 4 turns automatic routing off by default, and every route is declared in app/Config/Routes.php with its HTTP verb:

// CodeIgniter 3, application/config/routes.php
$route['orders/(:num)'] = 'orders/show/$1';

// CodeIgniter 4, app/Config/Routes.php
$routes->get('orders/(:num)', 'Orders::show/$1');

One wildcard changed meaning. (:any) in version 3 matched a single segment. In version 4 that is (:segment), and (:any) matches across several segments. A legacy auto-routing mode exists for the transition, but an application with a large number of controllers is better served by writing the routes out. The list doubles as an inventory of what the application exposes, and php spark routes prints it.

What has no direct replacement

Some version 3 components were not carried over: the Shopping Cart, FTP, Zip, XML-RPC, Calendar, Javascript and Trackback libraries, and the CAPTCHA, Email, Path and Smiley helpers. For each one you use, pick a Composer package or write the small part you need.

Third-party code is the larger risk. Authentication libraries, REST server libraries and modular extensions written for CodeIgniter 3 depend on the old global object and do not load in version 4. Authentication is usually the biggest single item. CodeIgniter Shield is the official library for version 4, and existing password hashes created with PHP's password_hash() can be moved across. Hashes made with older methods need a rehash-on-login step. Data encrypted with the version 3 Encryption library can still be read if you configure the version 4 encrypter for compatibility, as the upgrade guide describes.

A CodeIgniter 3 to 4 migration strategy that keeps the business running

  1. Bring the old application up to date first. Move it to the last CodeIgniter 3 release and a current PHP version it can run on. The details are in keeping a legacy CodeIgniter application safe and maintainable.
  2. Take inventory. List controllers, models, libraries, helpers, hooks, third-party code, cron jobs and every place the code touches the session or the file system. This list is the estimate.
  3. Freeze features, or keep the freeze short. Every change made to the old code during the conversion has to be made twice.
  4. Set up the new project next to the old one, pointing at a copy of the same database. Convert configuration, the base controller, authentication and the layout views first, because everything else depends on them.
  5. Convert one module at a time: its model, its controllers, its views, its routes. Write a test or a written checklist for each module as it is converted, and compare output with the old application on the same data.
  6. Cut over at a quiet time. Point the document root at the new public/ folder, keep the old application ready behind a second host name for a few days, and watch the logs.

For large systems the two applications can run side by side for a while, with the web server sending converted sections to the new code. That works only if login is solved across both, because sessions are not shared between the versions. On small and mid-sized applications one planned cutover is usually simpler.

Convert, rebuild or stay a while longer

If the application is well structured and mostly uses the framework's own features, converting to CodeIgniter 4 is the shortest route, and a developer who knows the code can do it. If it leans on abandoned libraries, mixes logic into views, or needs queues and a richer data layer anyway, compare the conversion with a rebuild on another framework before you commit, and our Laravel vs CodeIgniter comparison can help with that. Staying on version 3 for another year is defensible only with the maintenance work done properly.

We handle these projects as a CodeIgniter 3 to 4 upgrade, starting with the inventory and a module-by-module plan. If the old system has to stay in service while a decision is made, CodeIgniter 3 support covers that, and new work on the converted application continues under CodeIgniter 4 development.

Frequently asked questions

Is there a tool that converts CodeIgniter 3 code automatically?

There is no official converter, and the upgrade guide says plainly that there is no step-by-step checklist. Search and replace handles many of the renamed methods, and a tool such as Rector can modernize the PHP syntax. Structure, namespaces, routing and third-party libraries are manual work.

Do I have to change the database?

No. Tables and data stay as they are, and CodeIgniter 4 connects to the same database. The exceptions are the migrations table and the session table, which have new definitions, and any password hashes stored with outdated methods.

How long does a CodeIgniter 3 to 4 conversion take?

It depends on the number of controllers and models, how much third-party code is involved, and whether tests exist. The inventory step gives a count of each, and that count is the basis for a realistic estimate. Authentication and custom libraries usually take longer than the controllers.

Which PHP version does CodeIgniter 4 need?

CodeIgniter 4 requires a current PHP 8 release, and the minimum rises over time. Check the server requirements page for the version you install, and plan the PHP upgrade as part of the same project if your server is behind.

Add WorldWin Coder to your preferred sources

Add worldwincoder.com as a preferred source on Google, or open this article in your AI assistant to use it as a source.

Swati Priyadrshni
· Founder & CEO at WorldWin Coder

I'm Swati Priyadarshani, Founder & CEO of LMS Advisor and Co-Founder of WorldWin Coder Pvt. Ltd. (est. 2019). Over the last 10+ years, I've helped enterprises build learning platforms that actually get used - not just deployed.

Keep reading

Have something to build, fix or move?

Tell us where you are. A developer reads every inquiry, and you get next steps and a free quote within one business day.