• Skip to secondary menu
  • Skip to main content
  • Skip to primary sidebar
  • Home
  • Projects
  • Products
  • Themes
  • Tools
  • Request for Quote

Vengala Vinay

Having 12+ Years of Experience in Software Development

  • Home
  • WordPress
  • PHP
    • Codeigniter
  • Django
  • Magento
  • Selenium
  • Server
Home » Upgrading legacy CodeIgniter 3 application directory structures to CodeIgniter 4 PSR-4 autoloading on Debian 12

Upgrading legacy CodeIgniter 3 application directory structures to CodeIgniter 4 PSR-4 autoloading on Debian 12

Prerequisites and Environment Setup

This guide assumes a Debian 12 (Bookworm) server with a functional LAMP or LEMP stack. Specifically, you’ll need PHP 8.1 or later, Composer installed globally, and root or sudo privileges. We’ll be working within a hypothetical application directory, /var/www/legacy_app. Ensure your existing CodeIgniter 3 application is functional before proceeding with the upgrade.

First, let’s verify our PHP and Composer versions:

sudo apt update && sudo apt upgrade -y
php -v
composer --version

If Composer is not installed, you can install it via:

php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php composer-setup.php
sudo mv composer.phar /usr/local/bin/composer
php -r "unlink('composer-setup.php');"
composer --version

Analyzing the Legacy CodeIgniter 3 Directory Structure

A typical CodeIgniter 3 application has a flat structure within the application/ directory. Key subdirectories include config/, controllers/, models/, views/, and libraries/. CodeIgniter 3 relies on its own autoloader, which is file-based and often requires manual inclusion or the use of APPPATH constants.

Consider a simplified legacy structure:

/var/www/legacy_app/
├── application/
│   ├── config/
│   │   ├── config.php
│   │   └── database.php
│   ├── controllers/
│   │   └── Welcome.php
│   ├── models/
│   │   └── User_model.php
│   ├── views/
│   │   └── welcome_message.php
│   └── libraries/
│       └── My_library.php
├── system/
│   └── (CodeIgniter 3 core files)
└── index.php

Introducing CodeIgniter 4 and PSR-4 Autoloading

CodeIgniter 4 embraces modern PHP standards, most notably PSR-4 autoloading. This means your application’s classes will be organized into namespaces, and Composer will handle their loading automatically based on directory structure. The typical CI4 structure is flatter and more organized, with a dedicated app/ directory for your application code and a public/ directory for web-accessible files.

The goal is to migrate the contents of application/ into a new app/ directory, adopting namespaces and leveraging Composer’s autoloader. The public/ directory will contain the entry point (index.php) and front-controller related assets.

Step-by-Step Migration Strategy

We will perform this migration in stages, ensuring minimal downtime and allowing for iterative testing.

1. Initializing CodeIgniter 4 Project

Navigate to your application’s root directory and create a new CodeIgniter 4 project using Composer. It’s crucial to do this in a separate directory initially to avoid overwriting your existing application. We’ll then selectively copy over files.

cd /var/www/
composer create-project codeigniter4/appstarter legacy_app_ci4
cd legacy_app_ci4

This command downloads the latest CodeIgniter 4 skeleton into a new directory named legacy_app_ci4. The core structure will look something like this:

/var/www/legacy_app_ci4/
├── app/
│   ├── Config/
│   ├── Controllers/
│   ├── Database/
│   ├── Filters/
│   ├── Helpers/
│   ├── Language/
│   ├── Libraries/
│   ├── Models/
│   ├── ThirdParty/
│   └── Views/
├── public/
│   ├── index.php
│   ├── robots.txt
│   └── .htaccess (or nginx equivalent)
├── writable/
├── tests/
├── vendor/
├── composer.json
└── spark

2. Migrating Configuration Files

CodeIgniter 4 uses a dedicated app/Config/ directory. Configuration files are now classes extending CodeIgniter\Config\BaseConfig. We need to adapt our legacy application/config/ files to this new format.

Copy your legacy configuration files (e.g., config.php, database.php) into the new app/Config/ directory. Then, refactor them into CI4 configuration classes. For example, application/config/config.php might become app/Config/App.php.

Example: Migrating application/config/config.php to app/Config/App.php

Legacy application/config/config.php (simplified):

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

$config['base_url'] = 'http://localhost/legacy_app/';
$config['index_page'] = '';
$config['uri_protocol'] = 'REQUEST_URI';
// ... other configurations
?>

New app/Config/App.php:

<?php

namespace Config;

use CodeIgniter\Config\BaseConfig;

class App extends BaseConfig
{
    /**
     * @var string
     */
    public $baseURL = 'http://localhost/legacy_app/'; // Note: This will be overridden by environment variables or public/.htaccess

    /**
     * @var string
     */
    public $indexPage = '';

    /**
     * @var string
     */
    public $uriProtocol = 'REQUEST_URI';

    /**
     * @var string
     */
    public $defaultLocale = 'en';

    /**
     * @var bool
     */
    public $negotiateLocale = false;

    /**
     * @var array
     */
    public $supportedLocales = ['en'];

    // ... other configurations mapped to public properties
}
?>

Similarly, refactor application/config/database.php into app/Config/Database.php. Pay close attention to the structure of the $default and $backup arrays in CI4’s Database.php.

Example: Migrating application/config/database.php to app/Config/Database.php

Legacy application/config/database.php (simplified):

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

$db['default'] = array(
    'dsn'    => '',
    'hostname' => 'localhost',
    'username' => 'root',
    'password' => '',
    'database' => 'legacy_db',
    'dbdriver' => 'mysqli',
    'dbprefix' => '',
    'pconnect' => FALSE,
    'db_debug' => (ENVIRONMENT !== 'production'),
    'cache_on' => FALSE,
    'cachedir' => '',
    'char_set' => 'utf8',
    'dbcollat' => 'utf8_general_ci',
    'swap_pre' => '',
    'encrypt'  => FALSE,
    'compress' => FALSE,
    'stricton' => FALSE,
    'failover' => array(),
    'save_queries' => TRUE
);
?>

New app/Config/Database.php:

<?php

namespace Config;

use CodeIgniter\Database\Config\BaseConfig;

class Database extends BaseConfig
{
    /**
     * @var string
     */
    public $defaultGroup = 'default';

    /**
     * @var array
     */
    public $default = [
        'DSN'          => '',
        'hostname'     => 'localhost',
        'port'         => '',
        'database'     => 'legacy_db',
        'username'     => 'root',
        'password'     => '',
        'DBDriver'     => 'MySQLi', // Note: Case sensitivity and driver name
        'dbprefix'     => '',
        'pConnect'     => false,
        'DBDebug'      => (ENVIRONMENT !== 'production'),
        'cacheOn'      => false,
        'cacheDir'     => '',
        'charset'      => 'utf8',
        'DBCollat'     => 'utf8_general_ci',
        'swapPre'      => '',
        'encrypt'      => false,
        'compress'     => false,
        'strictOn'     => false,
        'failover'     => [],
        'modes'        => [],
        ' குற்றச்சாட்டுகள்' => [], // Example of a potential typo in legacy, corrected here
        'saveQueries'  => true,
    ];

    // ... other database groups if any
}
?>

3. Migrating Controllers, Models, and Libraries

This is the core of the migration. CodeIgniter 4 uses namespaces extensively. Your controllers, models, and libraries need to be placed in their respective directories within app/ and assigned appropriate namespaces.

Controllers:

Legacy application/controllers/Welcome.php:

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

class Welcome extends CI_Controller {
    public function index()
    {
        $this->load->view('welcome_message');
    }
}
?>

Migrated app/Controllers/Home.php (CI4 convention uses Home for the default controller):

<?php

namespace App\Controllers;

class Home extends BaseController
{
    public function index()
    {
        return view('welcome_message'); // CI4 uses view() helper or $this->render()
    }
}
?>

Models:

Legacy application/models/User_model.php:

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

class User_model extends CI_Model {
    public function get_user($id)
    {
        $this->db->where('id', $id);
        $query = $this->db->get('users');
        return $query->row();
    }
}
?>

Migrated app/Models/UserModel.php:

<?php

namespace App\Models;

use CodeIgniter\Model;

class UserModel extends Model
{
    protected $table      = 'users';
    protected $primaryKey = 'id';

    protected $allowedFields = ['name', 'email']; // Example

    // CI4 uses a different API for querying
    public function getUser($id)
    {
        return $this->where('id', $id)->first();
    }
}
?>

Libraries:

Legacy application/libraries/My_library.php:

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

class My_library {
    public function do_something() {
        return "Hello from legacy library!";
    }
}
?>

Migrated app/Libraries/MyLibrary.php:

<?php

namespace App\Libraries;

class MyLibrary
{
    public function doSomething() {
        return "Hello from CI4 library!";
    }
}
?>

Important Notes on Migration:

  • Namespaces: Ensure all your migrated classes have the correct namespace (e.g., App\Controllers, App\Models, App\Libraries).
  • Base Classes: Controllers should extend \App\Controllers\BaseController, and Models should extend \CodeIgniter\Model.
  • Autoloading: Composer will handle autoloading for classes within the app/ directory based on the App namespace.
  • File Naming: CI4 conventions often use CamelCase for class names (e.g., User_model.php becomes UserModel.php).
  • API Changes: Many CI3 methods have changed. For example, $this->load->model('User_model') becomes $userModel = new \App\Models\UserModel(); or using the model factory. Database queries use a fluent builder API.

4. Updating the Entry Point and Web Server Configuration

The public/ directory in CI4 is the new web root. The index.php file here is the front controller.

Copy your legacy index.php and .htaccess (if Apache) or configure your Nginx server block to point the document root to the new public/ directory.

New public/index.php (from CI4 starter):

<?php
//--------------------------------------------------------------------
// App Initialization
//--------------------------------------------------------------------
// The path to the application directory.
$appPath = __DIR__ . '/../app';

// The path to the front controller (this file).
define('FCPATH', __DIR__ . DIRECTORY_SEPARATOR);

// The path to the system directory.
define('SYSTEMPATH', __DIR__ . '/../system'); // Note: CI4 system is in vendor/codeigniter4/framework/system

// The path to the writable directory.
define('WRITEPATH', __DIR__ . '/../writable');

// The path to the public directory.
define('PUBLICPATH', __DIR__ . DIRECTORY_SEPARATOR);

/*
 *---------------------------------------------------------------
 * BOOTSTRAP THE APPLICATION
 *---------------------------------------------------------------
 * This process sets up the path constants, loads the
 * application's drivers, and sets the true root path.
 */
$bootstrap = require rtrim($appPath, '/ ') . '/bootstrap.php';

/*
 *---------------------------------------------------------------
 * LAUNCH THE APPLICATION
 *---------------------------------------------------------------
 */
$app = \Config\Services::app();

// Activate filters
$app->run();
?>

Apache Configuration (/etc/apache2/sites-available/legacy_app.conf):

<VirtualHost *:80>
    ServerAdmin webmaster@localhost
    DocumentRoot /var/www/legacy_app_ci4/public

    <Directory /var/www/legacy_app_ci4/public>
        Options Indexes FollowSymLinks
        AllowOverride All
        Require all granted
        RewriteEngine On
        RewriteCond %{REQUEST_FILENAME} !-f
        RewriteCond %{REQUEST_FILENAME} !-d
        RewriteRule ^(.*)$ index.php/$1 [L]
    </Directory>

    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>

Enable the site and rewrite module:

sudo a2ensite legacy_app.conf
sudo a2enmod rewrite
sudo systemctl reload apache2

Nginx Configuration (/etc/nginx/sites-available/legacy_app):

server {
    listen 80;
    server_name your_domain.com; # Or localhost

    root /var/www/legacy_app_ci4/public;
    index index.php index.html index.htm;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/var/run/php/php8.1-fpm.sock; # Adjust PHP version if necessary
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.ht {
        deny all;
    }
}

Enable the site and test Nginx configuration:

sudo ln -s /etc/nginx/sites-available/legacy_app /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

5. Composer Autoloading and Environment Variables

After migrating files and updating configurations, run Composer’s dump-autoload command to ensure the autoloader is up-to-date.

cd /var/www/legacy_app_ci4
composer dump-autoload

CodeIgniter 4 heavily relies on environment variables for configuration, especially for sensitive data like database credentials. The .env file in the project root is used for this. Copy your legacy database credentials and other environment-specific settings into .env.

Example .env file:

[app]
baseURL = 'http://localhost/legacy_app_ci4/'
# ... other app settings

[database]
default.database = 'legacy_db'
default.username = 'root'
default.password = ''
default.DBDriver = 'MySQLi'
# ... other database settings

[mail]
# ... mail settings

Ensure the .env file is correctly referenced in public/index.php and app/bootstrap.php. The starter project usually handles this.

6. Testing and Debugging

With the basic structure in place, it’s time for thorough testing. Enable detailed error reporting in your development environment by setting CI_ENVIRONMENT = 'development' in your .env file.

Common Issues and Debugging Steps:

  • 404 Errors: Verify your web server’s document root points to the public/ directory and that rewrite rules are correctly configured. Check app/Config/App.php for baseURL and indexPage.
  • Class Not Found Errors: Double-check namespaces and file paths. Ensure composer dump-autoload has been run. Verify the PSR-4 mapping in vendor/composer/autoload_psr4.json.
  • Database Connection Errors: Confirm credentials in .env and app/Config/Database.php are correct. Check the DBDriver name (e.g., ‘MySQLi’ vs ‘mysqli’).
  • View Rendering Issues: Ensure views are in app/Views/ and are being called correctly using the view() helper or $this->render().
  • Helper Function Not Found: CI4 has a different set of built-in helpers. You may need to load them explicitly in your controller’s constructor or in app/Config/Autoload.php.
  • Session Issues: CI4 has a new session library. Ensure it’s configured in app/Config/Session.php and loaded correctly.

Use the CodeIgniter 4 Debug Toolbar for invaluable insights into requests, database queries, and errors during development.

Post-Migration Considerations

Once the application is running on CodeIgniter 4, consider these points:

  • Security: Review CI4’s security features, such as CSRF protection, input filtering, and escaping output.
  • Performance: Optimize database queries, leverage caching, and profile your application.
  • Testing: Implement unit, feature, and integration tests using PHPUnit, which is integrated with CodeIgniter 4.
  • Deployment: Adapt your deployment scripts to handle Composer dependencies and the new directory structure.
  • Code Refactoring: Gradually refactor legacy code to take full advantage of CI4’s features and modern PHP practices.

This comprehensive approach to migrating from CodeIgniter 3’s directory structure to CodeIgniter 4’s PSR-4 autoloading on Debian 12 provides a robust foundation for modernizing your legacy application.

Reader Interactions

Leave a Reply Cancel reply

You must be logged in to post a comment.

Primary Sidebar

A little about the Author

Having 12+ Years of Experience in Software Development, Vinay is a principal software architect, senior systems engineer, and elite technical consultant. He specializes in bespoke PHP/WordPress development, high-performance Magento 2 & Shopify architectures, custom plugin/theme development from scratch, and legacy code modernization (including VB6, VB.NET, PyQt, and Crystal Reports). Known for solving complex database bottlenecks, speed optimization (Core Web Vitals), and advanced security code auditing, Vinay engineers production-ready systems designed to scale under heavy concurrent load conditions.



Chat on WhatsApp

Recent Posts

  • Debugging Guide: Diagnosing PHP-FPM child process pool exhaustion in multi-site network environments with modern tools
  • Debugging and Resolving complex namespace class loading collisions issues during heavy concurrent database traffic
  • Step-by-Step Guide: Offloading high-frequency customer support tickets metadata writes to a Redis KV store
  • How to refactor legacy event ticket registers queries using modern WP_Query and custom Transient caching
  • Step-by-Step Guide: Offloading high-frequency member profile directories metadata writes to a Redis KV store

Categories

  • apache (1)
  • Business & Monetization (390)
  • Centos (4)
  • Comparisons & Decision Making (55)
  • Debian (2)
  • Debugging & Troubleshooting (662)
  • Desktop Applications (14)
  • DevOps (7)
  • DevOps & Cloud Scaling (962)
  • Django (1)
  • Laravel (4)
  • Migration & Architecture (192)
  • Mobile Applications (24)
  • MySQL (1)
  • Performance & Optimization (873)
  • PHP (5)
  • PHP Development (49)
  • Plugins & Themes (244)
  • Programming Languages (9)
  • Python (20)
  • Ruby on Rails (1)
  • Security & Compliance (647)
  • SEO & Growth (492)
  • Server (118)
  • Ubuntu (9)
  • VB6 & VB.NET (8)
  • Web Applications & Frontend (19)
  • Web Assembly (Wasm) (2)
  • WordPress (22)
  • WordPress Plugin Development (726)
  • WordPress Theme Development (357)

Recent Posts

  • Debugging Guide: Diagnosing PHP-FPM child process pool exhaustion in multi-site network environments with modern tools
  • Debugging and Resolving complex namespace class loading collisions issues during heavy concurrent database traffic
  • Step-by-Step Guide: Offloading high-frequency customer support tickets metadata writes to a Redis KV store

Top Categories

  • DevOps & Cloud Scaling (962)
  • Performance & Optimization (873)
  • WordPress Plugin Development (726)
  • Debugging & Troubleshooting (662)
  • Security & Compliance (647)
  • SEO & Growth (492)

Our Products

  • ERP & LMS Systems (4)
  • Directories & Marketplaces (4)
  • Healthcare Portals (3)
  • Point of Sale (POS) (2)
  • E-Commerce Engines (2)

Our Services

  • E-Commerce Development (10)
  • WordPress Development (8)
  • Python & Desktop GUI (7)
  • General Consulting (7)
  • Legacy Modernization (5)
  • Mobile App Development (4)

Copyright © 2026 · Vinay Vengala