Search by

shumatsumonobu / ci-extension

shumatsumonobu

Extend CodeIgniter for ease of use

Package info

github.com/shumatsumonobu/ci-extension

pkg:composer/shumatsumonobu/ci-extension

Statistics

Installs: 4

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v6.0.0 2026-11-13 05:42 UTC

This package is auto-updated.

Last update: 2026-11-13 10:55:46 UTC


README

Version Downloads PHP License

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, openssl and xml extensions
  • Composer
  • imagick extension 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.

Sign in User list Edit user

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