shumatsumonobu / ci-extension
Extend CodeIgniter for ease of use
Requires
- php: ^8.3
- ext-openssl: *
- aws/aws-sdk-php: ^3.403
- codeigniter/framework: 3.1.*
- intervention/image: ^4.4
- mathieuviossat/arraytotexttable: ^1.0.12
- twig/twig: ^3.30
- vlucas/phpdotenv: ^5.8
Requires (Dev)
- phpunit/phpunit: ^12.5
Suggests
None
Provides
None
Conflicts
None
Replaces
None
- dev-main
- v6.0.0
- v5.0.11
- v5.0.10
- v5.0.9
- v5.0.8
- v5.0.7
- v5.0.6
- v5.0.5
- v5.0.4
- v5.0.3
- v5.0.2
- v5.0.1
- v5.0.0
- v4.20
- v4.1.9
- v4.1.8
- v4.1.7
- v4.1.6
- v4.1.5
- v4.1.4
- v4.1.3
- v4.1.2
- v4.1.1
- v4.1.0
- v4.0.25
- v4.0.24
- v4.0.23
- v4.0.22
- v4.0.21
- v4.0.20
- v4.0.19
- v4.0.18
- v4.0.17
- v4.0.16
- v4.0.15
- v4.0.14
- v4.0.13
- v4.0.12
- v4.0.11
- v4.0.10
- v4.0.9
- v4.0.8
- v4.0.7
- v4.0.6
- v4.0.5
- v4.0.4
- v4.0.3
- v4.0.2
- v4.0.1
- v4.0.0
- v3.9.9
- v3.9.8
- v3.9.7
- v3.9.6
- v3.9.5
- v3.9.4
- v3.9.3
- v3.9.2
- v3.9.1
- v3.9.0
- v3.8.9
- v3.8.8
- v3.8.7
- v3.8.6
- v3.8.5
- v3.8.4
- v3.8.3
- v3.8.2
- v3.8.1
- v3.8.0
- v3.7.9
- v3.7.8
- v3.7.7
- v3.7.6
- v3.7.5
- v3.7.4
- v3.7.3
- v3.7.2
- v3.7.1
- v3.7.0
- v3.6.9
- v3.6.8
- v3.6.7
- v3.6.6
- v3.6.5
- v3.6.4
- v3.6.3
- v3.6.2
- v3.6.1
- v3.6.0
- v3.5.9
- v3.5.8
- v3.5.7
- v3.5.6
- v3.5.5
- v3.5.4
- v3.5.3
- v3.5.2
- v3.5.1
- v3.5.0
- v3.4.9
- v3.4.8
- v3.4.7
- v3.4.6
- v3.4.5
- v3.4.4
- v3.4.3
- v3.4.2
- v3.4.1
- v3.4.0
- v3.3.9
- v3.3.8
- v3.3.7
- v3.3.6
- v3.3.5
- v3.3.4
- v3.3.3
- v3.3.2
- v3.3.1
- v3.3.0
- v3.2.9
- v3.2.8
- v3.2.7
- v3.2.6
- v3.2.5
- v3.2.4
- v3.2.3
- v3.2.2
- v3.2.1
- v3.2.0
- v3.1.7
- v3.1.6
- v3.1.5
- v3.1.4
- v3.1.3
- v3.1.2
- v3.1.1
- v3.1.0
- v3.0.9
- v3.0.8
- v3.0.7
- v3.0.6
- v3.0.5
- v3.0.4
- v3.0.3
- v3.0.2
- v3.0.1
- v3.0.0
- v2.4.2
- v2.4.1
- v2.4.0
- v2.3.9
- v2.3.8
- v2.3.7
- v2.3.6
- v2.3.5
- v2.3.4
- v2.3.3
- v2.3.2
- v2.3.1
- v2.3.0
- v2.2.9
- v2.2.8
- v2.2.7
- v2.2.6
- v2.2.5
- v2.2.4
- v2.2.3
- v2.2.2
- v2.2.1
- v2.2.0
- v2.1.9
- v2.1.8
- v2.1.7
- v2.1.6
- v2.1.5
- v2.1.4
- v2.1.3
- v2.1.2
- v2.1.1
- v2.1.0
- v2.0.9
- v2.0.8
- v2.0.7
- v2.0.6
- v2.0.5
- v2.0.4
- v2.0.3
- v2.0.2
- v2.0.1
- v2.0.0
- v1.3.7
- v1.3.6
- v1.3.5
- v1.3.4
- v1.3.3
- v1.3.2
- v1.3.1
- v1.3.0
- v1.2.9
- v1.2.8
- v1.2.7
- v1.2.6
- v1.2.5
- v1.2.4
- v1.2.3
- v1.2.2
- v1.2.1
- v1.2.0
- v1.1.9
- v1.1.8
- v1.1.7
- v1.1.6
- v1.1.5
- v1.1.4
- v1.1.3
- v1.1.2
- v1.1.1
- v1.1.0
- v1.0.9
- v1.0.8
- v1.0.7
- v1.0.6
- v1.0.5
- v1.0.4
- v1.0.3
- v1.0.2
- v1.0.1
- v1.0.0
This package is auto-updated.
Last update: 2026-11-13 10:55:46 UTC
README
CodeIgniter Extension
Modern conveniences for CodeIgniter 3, on PHP 8.3.
Response helpers, attribute-based access control, a richer model, utilities and AWS clients — plus a one-command starter app.
API Reference | Changelog | Contributing
What you get
| Area | Highlights |
|---|---|
| Controller | One-line JSON, Twig, HTML, text, image and download responses. CORS headers |
| Access control | #[Access(...)] on each action: signed-in only, signed-out only, roles, CLI only |
| Model | const TABLE binds the query builder to a table. INSERT ... ON DUPLICATE KEY UPDATE, helpers such as get_by_id() |
| Utilities | Images, files, encryption, CSV, REST client, validation, logging, email |
| AWS | Amazon Rekognition (face comparison and detection) and Amazon SES (email, including cross-account AssumeRole) |
| Starter app | composer create-project creates a working app with sign-in, user management and a Tailwind CSS UI |
Built on CodeIgniter 3 (codeigniter/framework 3.1.x).
Requirements
- PHP 8.3 or later with the
gd,mbstring,opensslandxmlextensions - Composer
imagickextension and ImageMagick, only for the GIF and PDF functions of\X\Util\ImageHelper- Node.js 20 or later, only to build the starter app's stylesheet
Getting started
Create a new app
composer create-project shumatsumonobu/ci-extension myapp
cd myapp
This copies the starter app, applies the recommended config.php settings, installs the dependencies and builds the stylesheet. Then set up the web server and the database:
sudo chmod -R 755 application/{logs,cache,session}
sudo chown -R nginx:nginx application/{logs,cache,session}
sudo cp nginx.sample.conf /etc/nginx/conf.d/myapp.conf # set root to {your app}/public
sudo systemctl restart nginx
mysql -u root -p < database.sql
Open http://{your-server}:3000/ and sign in as robin@example.com (admin) or taylor@example.com (member). The password is password.
The starter app is the skeleton/ directory of this repository. It also runs with Docker: cd skeleton && docker compose up --build.
Add to an existing app
composer require shumatsumonobu/ci-extension
Extend your base classes from the library, and set $config['subclass_prefix'] = 'App'; and $config['composer_autoload'] in config.php. The skeleton's AppController.php, AppModel.php and hooks.php are complete examples.
Usage
Controller
use X\Annotation\Access; class Users extends AppController { protected $model = 'UserModel'; // Twig template application/views/users.html with the variable "users" #[Access(allow_login: true, allow_logoff: false, allow_role: "admin")] public function index() { parent::set('users', $this->UserModel->get_all())::view('users'); } // JSON: {"id":"1","name":"Robin",...} #[Access(allow_login: true, allow_logoff: false)] public function get(int $id) { parent::set($this->UserModel->get_by_id($id))::json(); } }
Responses: json(), view(), html(), text(), js(), image(), download(), internalRedirect() and error(). Set data with set($key, $value) or set($value), the status with status(), and CORS headers with setCorsHeader().
Access control
Declare who can call each action with the #[Access] attribute:
| Argument | Default | Meaning |
|---|---|---|
allow_login |
true |
Signed-in users can access |
allow_logoff |
true |
Signed-out users can access |
allow_role |
"" |
Comma-separated roles allowed for signed-in users. Empty allows every role |
allow_http |
true |
false makes the action CLI only |
A method without the attribute allows everyone. The check itself is a post_controller_constructor hook in your app, which reads the attribute with \X\Annotation\AnnotationReader::getAccessibility() and decides where to redirect:
// application/config/hooks.php use X\Annotation\AnnotationReader; $hook['post_controller_constructor'] = function() { if (is_cli()) return; $CI =& get_instance(); $access = AnnotationReader::getAccessibility($CI->router->class, $CI->router->method); $loggedin = !empty($_SESSION['user']); if (!$access->allow_http) throw new \RuntimeException('HTTP access is not allowed'); else if ($loggedin && !$access->allow_login) redirect('/profile'); else if (!$loggedin && !$access->allow_logoff) redirect('/login'); else if ($loggedin && $access->allow_role !== '' && !in_array($_SESSION['user']['role'], array_map('trim', explode(',', $access->allow_role)))) redirect('/profile'); };
Hooks must be enabled: $config['enable_hooks'] = TRUE;.
Model
class UserModel extends AppModel { const TABLE = 'user'; public function getAdmins(): array { return $this ->where('role', 'admin') ->order_by('name') ->get() ->result_array(); } }
The query builder methods return the model, so they chain. Helpers: get_all(), get_by_id(), count_by_id(), exists_by_id(), insert_on_duplicate_update() and insert_on_duplicate_update_batch(). A failed query throws RuntimeException with the database's error code and message.
Twig templates
view() renders Twig 3 templates in application/views/. Every template gets these variables and functions:
| Name | Value |
|---|---|
session |
$_SESSION |
baseUrl |
base_url() |
action |
Current controller and method, such as users/index or api/users/get |
cache_busting('app.css') |
base_url('app.css') with the file's modification time, such as http://example.com/app.css?1731400000 |
{% if session.user is not empty %}
<p>Welcome, {{ session.user.name }}</p>
{% endif %}
<link rel="stylesheet" href="{{ cache_busting('app.css') }}">
Utilities
use X\Util\{Cipher, FileHelper, ImageHelper, Logger, RestClient, Validation}; ImageHelper::resize('photo.jpg', 'thumb.jpg', 320); // width 320, keeps the aspect ratio ImageHelper::extractFirstFrameOfGif('anim.gif', 'first.gif'); // needs Imagick ImageHelper::pdf2Image('doc.pdf', 'page.jpg', ['pageNumber' => 0]); // needs Imagick FileHelper::makeDirectory('/var/data/export'); // recursive, mode 0755 $key = Cipher::generateKey(); $iv = Cipher::generateInitialVector(); $encrypted = Cipher::encrypt('secret', $key, $iv); // AES-256-CTR $plain = Cipher::decrypt($encrypted, $key, $iv); $client = new RestClient(['base_url' => 'https://api.example.com']); $users = $client->get('/users')->response; Validation::email('user@example.com'); // true Logger::debug('user=', $user); // written with file and line
| Class | For |
|---|---|
ImageHelper |
Data URL to file, resize, GIF frames, PDF to images |
VideoHelper |
Data URL to video file |
FileHelper |
Create, copy, move, delete and find files and directories |
Cipher |
AES encryption, SHA-256, keys and random tokens |
CsvHelper |
Read and append CSV rows |
RestClient |
HTTP requests with Basic auth and SSL options |
Validation |
Host names, IP addresses, CIDR, ports, email addresses, paths |
IpUtils |
IPv4 and IPv6 checks, range checks, client IP from X-Forwarded-For |
EMail |
Email through CodeIgniter's email library, with Twig templates |
AmazonSesClient |
Email through Amazon SES |
Logger |
Debug, info and error logs with the caller's file and line |
ArrayHelper, StringHelper, DateHelper, HtmlHelper, UrlHelper, Iterator |
Small helpers |
See the API Reference for every method.
AWS
use X\Rekognition\Client; use X\Util\AmazonSesClient; // Face comparison. Omit "key" and "secret" to use the IAM role of the EC2 instance. $rekognition = new Client([ 'region' => 'ap-northeast-1', 'key' => $_ENV['AWS_KEY'], 'secret' => $_ENV['AWS_SECRET'], ]); $similarity = $rekognition->compareFaces($imageDataUrl1, $imageDataUrl2); // 0 to 100 // Email. "roleArn" sends through a role in another AWS account. (new AmazonSesClient(['region' => 'ap-northeast-1'])) ->from('noreply@example.com') ->to('user@example.com') ->subject('Welcome') ->message('Hello!') ->send();
Configuration
composer create-project applies these config.php settings. Set them yourself when you add the library to an existing app:
| Setting | Value |
|---|---|
base_url |
Built from $_SERVER['HTTP_HOST'] |
enable_hooks |
TRUE |
subclass_prefix |
'App' |
composer_autoload |
realpath(APPPATH . '../vendor/autoload.php') |
index_page |
'' |
permitted_uri_chars |
'a-z 0-9~%.:_\-,' |
sess_save_path |
APPPATH . 'session' |
cookie_httponly |
TRUE |
Database session driver
\X\Library\SessionDatabaseDriver stores sessions in a table and can copy session values into extra columns (for example, to find the sessions of a user). Each extra column gets the value of the first key with the same name in the session data, nested keys included ($_SESSION['user']['email'] fills the email column):
// application/libraries/Session/drivers/AppSession_database_driver.php class AppSession_database_driver extends \X\Library\SessionDatabaseDriver {} // application/config/config.php $config['sess_driver'] = 'database'; $config['sess_save_path'] = 'session'; // table name $config['sess_table_additional_columns'] = ['email']; // the table needs an "email" column
Installing Imagick on Amazon Linux 2023
sudo dnf -y install ImageMagick ImageMagick-devel php-pear php8.3-devel gcc make printf '\n' | sudo pecl install imagick echo "extension=imagick.so" | sudo tee /etc/php.d/40-imagick.ini sudo systemctl restart php-fpm
Upgrading from 5.x
Version 6 requires PHP 8.3, moves to Twig 3 and Intervention Image 4, and replaces the docblock annotation @Access(allow_login=true) with the attribute #[Access(allow_login: true)]. A method that still has only the docblock annotation is treated as having no access control, so convert all of them. See the 6.0.0 changelog for the steps.
Troubleshooting
| Problem | Cause and fix |
|---|---|
#[Access] has no effect |
Hooks are disabled. Set $config['enable_hooks'] = TRUE and register the hook in hooks.php |
| Access control stopped working after upgrading to 6.x | The docblock @Access(...) is no longer read. Replace it with #[Access(...)] above the method |
Class "Imagick" not found |
Install ImageMagick and the imagick extension |
| Template changes are not shown | Templates are cached. Clear application/cache/templates/ |
Failed to write session data warning with database sessions |
Use \X\Library\SessionDatabaseDriver, which implements updateTimestamp() |
Contributing
See CONTRIBUTING.md for the development environment, running the tests in Docker and the release steps.
Author
週末ものづくり部 ── @shumatsumonobu
License
MIT ── see LICENSE


