Skip to content
Back to skills

Php Codeigniter Legacy Patterns

ASecurity

Guides classic CodeIgniter 2.x/3.x applications (the application/ directory, bcit-ci/CodeIgniter, no PHP namespaces) - a fundamentally different framework generation from CodeIgniter 4. Use when working in a codebase with an application/ directory and CI_VERSION below 4, extending MY_Controller or a *_model class, adding a route, or reviewing code that assumes CI4-style namespaces or DI.

  • 2 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added October 16, 2026
ai-agentsgophpsqlapidatabasesecuritydocumentation

Works with

  • cli
  • api

Security analysis

A100/100

Pro scans all 2 files and shows the line behind each finding

Scanned October 16, 2026

npx -y skills add shennawardana23/skillme --skill php-codeigniter-legacy-patterns --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Php Codeigniter Legacy Patterns?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Php Codeigniter Legacy Patterns
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/shennawardana23-php-codeigniter-legacy-patterns/badge)](https://www.skillsdirectory.com/skills/shennawardana23-php-codeigniter-legacy-patterns)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: php-codeigniter-legacy-patterns
description: Guides classic CodeIgniter 2.x/3.x applications (the application/ directory, bcit-ci/CodeIgniter, no PHP namespaces) - a fundamentally different framework generation from CodeIgniter 4. Use when working in a codebase with an application/ directory and CI_VERSION below 4, extending MY_Controller or a *_model class, adding a route, or reviewing code that assumes CI4-style namespaces or DI.
license: Apache-2.0
metadata:
  version: "0.1.0"
  category: "php"
---

# Classic CodeIgniter (2.x/3.x) Patterns

Classic CodeIgniter (2.x/3.x — recognizable by an `application/` directory,
`bcit-ci/CodeIgniter` in `composer.json`, and no PHP namespaces anywhere in
app code) is not an older version of the same thing this catalog's
`php-codeigniter-patterns`/`-security`/`-tdd`/`-verification` skills teach —
those assume CI4's namespaced classes, `CodeIgniter\Model`, `Config\*`
classes, and the `spark` CLI, none of which exist here. Treat a codebase
with an `application/` directory (not `app/`) as needing this skill
instead, not a CI4 skill applied loosely.

## Everything is global, resolved through a service locator

There is no dependency injection. A controller pulls in what it needs via
`$this->load->`, and that call populates a property on the controller
instance at runtime — nothing is declared as a constructor dependency or
type-checkable ahead of time:

```php
class MY_Controller extends CI_Controller
{
    public function __construct()
    {
        parent::__construct();
        $this->load->model(array('User_model', 'Hotel_model'), '', TRUE);
        $this->load->library('session');
    }
}
```

Every class name here is global — no namespaces — so `User_model` must be
unique across the entire loaded application, and a helper function calling
`get_instance()` (CodeIgniter's way of reaching the current controller
instance from outside it) creates an implicit dependency on whatever
properties that controller's constructor happened to populate:

```php
function requires_admin_privilege() {
    $CI =& get_instance();
    return $CI->privilege_lib->user_is_admin(); // assumes $CI->privilege_lib exists
}
```

A helper like this silently breaks if called from a controller that
doesn't extend the same base class or load the same library — there is no
compiler or type system to catch the mismatch, only a runtime error.

## A shared base controller is usually a god class

It's common for one `MY_Controller` (extended by every controller in the
app, including API controllers) to accumulate constructor logic, loaded
libraries/models, and helper methods for the whole application over time,
because there's no lighter-weight alternative built into the framework —
there is no separate, minimal base for an API-only controller unless the
team builds one deliberately. Before adding "just one more thing" to a
shared base controller, consider whether it actually needs to run for
every controller that extends it, including ones (an API endpoint, a CLI
task) that may not need session/view-rendering setup at all.

## Configuration is a plain PHP array, not typed

```php
// application/config/database.php
$db['default'] = array(
    'hostname' => 'localhost',
    'username' => 'root',
    'password' => '',
    'database' => 'app',
    'dbdriver' => 'mysqli',
);
```

Multiple named connection groups can coexist in the same array (`$db['default']`,
`$db['reporting']`, and so on), selected explicitly per model
(`$this->load->database('reporting')`) — nothing enforces which group a
given model is supposed to use; that convention lives only in
documentation or in the developer's head, not in the type system.

## Routes are manual and exhaustive

`application/config/routes.php` is a flat associative array mapping every
individual URI pattern to a controller/method — there is no automatic
resource-routing convention (`Route::resource`-style) to lean on. A new
CRUD action means an explicit new line in this file; an unmapped route
falls through to the framework's default 404 behavior, and a typo'd
pattern here fails exactly the same silent way a CI4 filter-alias typo
does — the route just doesn't match, with no error pointing at the cause.

## Gotchas

- **No namespaces means every class name is a global collision risk** —
  two libraries or models with the same class name (even in different
  directories) will conflict; check for name collisions explicitly rather
  than assuming directory structure provides isolation the way a
  namespace would.
- **`get_instance()` inside a helper function creates an implicit runtime
  dependency on the calling controller's setup** — a helper that assumes
  `$CI->some_library` exists breaks with no compile-time warning if called
  from a controller that never loaded that library.
- **A shared base controller extended by every controller, API included,
  tends to accumulate unrelated setup logic over time** — evaluate whether
  new base-controller logic genuinely needs to run for every subclass
  before adding it, rather than defaulting to "put it in the base
  controller since everything extends it anyway."
- **Multiple database connection groups in one config file have no
  enforced usage convention** — confirm which group a given model
  actually expects before assuming `$this->db` (the default connection)
  is always the right one.
- **`enable_hooks = TRUE` in `application/config/hooks.php` does not mean
  hooks are actually in use** — check `application/hooks/` for real,
  registered hook points before assuming this is an active extension
  mechanism in a given codebase; it's commonly left enabled with nothing
  registered.
- **A manually-exhaustive `routes.php` has the same silent-typo failure
  mode as a CI4 filter alias typo** — a misspelled pattern doesn't error,
  it just never matches, and the request falls through to a 404 with
  nothing pointing at the routes file as the cause.
- **Vendored, pre-Composer third-party code (a library dropped directly
  into `application/third_party/`) can coexist with Composer-managed
  dependencies in the same app** — these are two independently-maintained
  dependency mechanisms; updating one doesn't touch the other, and it's
  easy to forget the vendored copy exists at all when auditing
  dependencies.

## Real-world grounding

Classic CodeIgniter's lack of namespaces and its `$this->load->`
service-locator style are direct artifacts of the PHP 5.2/5.3-era language
features it targeted — namespaces were only added to PHP in 5.3, and much
of CodeIgniter 2's design predates widespread namespace adoption in the PHP
ecosystem. This is exactly why CodeIgniter 4 (a full rewrite, not an
incremental upgrade) introduced namespaces, PSR-4 autoloading, and a
DI-adjacent `Config`/`Services` model — the two framework generations are
different enough in structure that "upgrade CI3 to CI4" is properly
understood as a migration project, not a version bump, which is the same
reason this catalog treats them as two separate skills rather than one.

## Verification

- [ ] New class names are checked for collisions across the whole
      `application/` tree, since there are no namespaces to isolate them
- [ ] Helper functions using `get_instance()` document which
      controller-populated properties they depend on
- [ ] New logic added to a shared base controller genuinely needs to run
      for every subclass, including any API-only controllers that extend
      it
- [ ] The correct database connection group is used for a given model,
      confirmed rather than defaulted to `$this->db`
- [ ] `application/hooks/` actually has registered hooks before treating
      hooks as an active extension mechanism in this codebase
- [ ] A new route added to `routes.php` has been tested by an actual
      request, not just read for correctness

Files in this skill

  • SKILL.md7.7 KB
  • evals/evals.json1.4 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.