# 爆速軽量フレームワーク codeigniter PHP 開発
仕事の関係で laravel も使ったことありますが、最初はあれこれ設定に悩まれましたが、1 年くらい使ったら laravel の便利さ強さがわかりました。
codeigniter になれたせいかわからないけど、やっぱり codeigniter のクエリビルダーの使いやすさはわすれられなくて、twig テンプレエンジンと組み合わせると最強最高のコーディング体験になります。久々 codeigniter やってなかったこともあり、メモもいままでなかったので、自由研究しながら、1 年ぶりに codeigniter でプロジェクト立ち上げです。
laravel との比べはしないけど、主に codeigniter の便利機能と忘れがちのところメモります。
# codeigniter はいつから PHP8 サポート?
2020 年 11 月 26 日に PHP8 リリースされましたが、codeigniter は php8 サポートするでしょうか?
PHP8 は 2 倍以上の速くなるそうですが、従来バージョンとの互換性が問題ですね。
PHP8 の新機能で関数にマルチタイプを追加すると、PHP 7.x との下位互換性が失われし、codeigniter のメリットの一つとして幅広いバージョンの PHP 対応していることもなくなります。
codeigniter のフォーラムに質問したものがいますが、==「近い将来追加される可能性はほとんどない」==そうです。
確かにバージョンの互換性は大事ですが、新バージョンの言語にいつか対応しないとどうなるでしょうか少し不安があります。これから laravel とかのフレームワークと比較する時も選択に影響するかもしれません。
codeigniter ファイト!
# htaccess 設定して url にある index.php 削除
.htaccess ファイル
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php/$1 [L]
# モデル query builder
laravel のクエリビルダーも使ったことありますが、やはり codeigniter のほうが抜群に使いやすい
codeigniter 3.2 公式 query builder 編 (opens new window)
# insert した行の id を返却 insert_id
insert_id は laravel の insertGetId() のように codeigniter model に insert した行の id 取得可能
User_model.php
public function insertRow($data)
{
$this->db->table($this->table)->insert($data);
return $this->db->insert_id();
}
# コンパイル sql 文出力 get_compiled_select
get_compiled_select は laravel の toSql()のように codeigniter model にコンパイルしたクエリ sql 文を返却
echo $this->db->select('title, content, date')->get_compiled_select();
// 出力: SELECT title, content, date FROM mytable
# where の4つの使い方
- key,value 個別指定
$this->db->where('column_key', $value);
- イコールじゃないパターン
$this->db->where('column_key !=', $value);
$this->db->where('id <', $id);
- 連想配列指定
$this->db->where(['id' => $id]);
- string 生の文字列渡す
ちょっと違和感感じるけど、文字列も配列もそのままwhereに掘り込んだら動いてくれる、PHP らしいプログラム!
$where = "name='Joe' AND status='boss' OR status='active'";
$this->db->where($where);
// $this->db->where() にはオプションで第3の引数を渡すこともできます。
// FALSE を渡した場合、CodeIgniter はフィールド名やテーブル名を守りません。
$this->db->where('MATCH (field) AGAINST ("value")', NULL, FALSE);
# get_where
get_where(テーブル名,where 配列,limit,offset)
// get_where(テーブル名,where 配列,limit,offset)
$this->db->get_where('table_name', ['id' => $id], $limit, $offset);
// 以下のものと同じ
$this->db->where(['id' => $id])
->limit($limit)
->offset($offset)
->get('table_name);
# dotenv 使う
laravel 使い慣れてたら、.envは便利でいいですね。codeigniter もデフォルトで入れるべきだと思うくらい。
調べてたら composer に dotenv があったので、codeigniter に dotenv 入れてみる
# 方法 1 vlucas/phpdotenv
- 環境:CodeIgniter3
- ライブラリ: vlucas/phpdotenv
- php: ^5.5.9 || ^7.0
codeigniter の hook 機能使って、pre_system システム読み込む前に env の読み込んで初期設定することで、グローバルに env 内容展開。
# vlucas/phpdotenv インストール
composer require vlucas/phpdotenv
# config ファイル設定
config/config.php hook 有効化
// composer autoload有効化
$config['composer_autoload'] = TRUE;
// hooks有効化
$config['enable_hooks'] = TRUE;
# hooks ファイル編集
codeigniter/config/hooks.php
<?php
defined('BASEPATH') or exit('No direct script access allowed');
$hook['pre_system'] = function () {
try {
$dotenv = Dotenv\Dotenv::createImmutable(APPPATH . '../'); // 自分の環境に合わせてenvファイルのパス設定
$dotenv->load();
} catch (Exception $e) {
//
}
function env(string $key, $default = null)
{
$value = $_ENV[$key];
return ($value) ? $value : $default;
}
};
ポイント:パス設定
createImmutable で.env ディレクトリまで指定
.env
DB_HOST=localhost
DB_USER=root
DB_PASS=
DB_NAME=test2
config/database.php
$db['default'] = array(
'dsn' => '',
'hostname' => env('DB_HOST'),
'username' => env('DB_USER'),
'password' => env('DB_PASS'),
'database' => env('DB_NAME'),
完了!任意の場所からenv()使えます。
config/config.php の中で使えない
どこでも env 可能だと思って config/config.php の中は設定したらエラーになった
config.php の中で base_url 設定も env から呼び込めばいいなあと思って設定して見ました
$config['base_url'] = 'http://' . env('BASE_URL');
# Call to undefined function env()エラー
Fatal error: Uncaught Error: Call to undefined function env() in C:\xampp\htdocs\hclo\application\config\config.php:26 Stack trace: #0 C:\xampp\htdocs\hclo\system\core\Common.php(249): require() #1 C:\xampp\htdocs\hclo\system\core\Common.php(300): get_config() #2 C:\xampp\htdocs\hclo\system\core\Common.php(171): config_item('subclass_prefix') #3 C:\xampp\htdocs\hclo\system\core\Common.php(652): load_class('Exceptions', 'core') #4 [internal function]: _exception_handler(Object(Error)) #5 {main} thrown in C:\xampp\htdocs\hclo\application\config\config.php on line 26
Fatal error: Uncaught Error: Call to undefined function env() in C:\xampp\htdocs\hclo\application\config\config.php:26 Stack trace: #0 C:\xampp\htdocs\hclo\system\core\Common.php(249): require() #1 C:\xampp\htdocs\hclo\system\core\Common.php(300): get_config() #2 C:\xampp\htdocs\hclo\system\core\Common.php(171): config_item('subclass_prefix') #3 C:\xampp\htdocs\hclo\system\core\Common.php(617): load_class('Exceptions', 'core') #4 C:\xampp\htdocs\hclo\system\core\Common.php(689): _error_handler(1, 'Uncaught Error:...', 'C:\\xampp\\htdocs...', 26) #5 [internal function]: _shutdown_handler() #6 {main} thrown in C:\xampp\htdocs\hclo\application\config\config.php on line 26
結論: config 内に env 使うことができない
SERVER の HTTP_HOST 指定しました。
$config['base_url'] = $_SERVER['HTTP_HOST'];
# 方法 2 symfony/dotenv
- 環境:CodeIgniter3
- ライブラリ: symfony/dotenv
- php: ^7.2.5
symfony dotenv は、シンフォニーチームが開発した php システムが利用できる.env ファイル読み込むためのコンポーネントです。
MY_Controller で実装すると、controller 読み込んでからでないと env 設定使えないので、hook の pre_system 使うべき
vlucas/phpdotenv もいけるけど、個人的に symfony が好きなシンフォニー派なので、symfony/dotenv 使います。愛用 twig もシンフォニー製
ただ、php 7.2 からはちょっとバージョン高くない?
env function 作成しておくと便利に使える
# symfony/dotenv インストールと初期設定
上記の vlucas/phpdotenv と一緒で割愛
# hooks 設定
codeigniter/config/hooks.php
<?php
defined('BASEPATH') or exit('No direct script access allowed');
use Symfony\Component\Dotenv\Dotenv;
$hook['pre_system'] = function () {
try {
$dotenv = new Dotenv();
$dotenv->load(APPPATH . '../.env'); // 自分の環境に合わせてenvファイルのパス設定
} catch (Exception $e) {
//
}
function env(string $key, $default = null)
{
$value = $_ENV[$key];
return $value ?? $default;
}
};
ポイント
.env ファイルまでパス設定する必要がある
.env
DB_HOST=localhost
DB_USER=root
DB_PASS=
DB_NAME=test2
config/database.php
$db['default'] = array(
'dsn' => '',
'hostname' => env('DB_HOST'),
'username' => env('DB_USER'),
'password' => env('DB_PASS'),
'database' => env('DB_NAME'),
完了!任意の場所からenv()使えます。
# Message: Class 'Symfony\Component\Dotenv\Dotenv' not found エラー発生
An uncaught Exception was encountered
Type: Error
Message: Class 'Symfony\Component\Dotenv\Dotenv' not found
Filename: /Users/t/Documents/www.com/application/config/hooks.php
windows では動いてくれてたけど、mac では動かない!あるあるパターンですけど、dotenv not found って composer install 忘れたかなあと思ったりしますが、install コマンド走らせても特に解消しない
composer update かけてみたら原因がわかりました。
$ composer update
Loading composer repositories with package information
Updating dependencies (including require-dev)
Your requirements could not be resolved to an installable set of packages.
Problem 1
- symfony/dotenv v5.0.4 requires php ^7.2.5 -> your PHP version (7.1.23) does not satisfy that requirement.
- symfony/dotenv v5.0.3 requires php ^7.2.5 -> your PHP version (7.1.23) does not satisfy that requirement.
- symfony/dotenv v5.0.2 requires php ^7.2.5 -> your PHP version (7.1.23) does not satisfy that requirement.
- symfony/dotenv v5.0.1 requires php ^7.2.5 -> your PHP version (7.1.23) does not satisfy that requirement.
- symfony/dotenv v5.0.0 requires php ^7.2.5 -> your PHP version (7.1.23) does not satisfy that requirement.
- Installation request for symfony/dotenv ^5.0 -> satisfiable by symfony/dotenv[v5.0.0, v5.0.1, v5.0.2, v5.0.3, v5.0.4].
原因はドキュメントに書いてあります。requires php: ^7.2.5
うん、ドキュメントはちゃんと読むべき!
比べてどうする ちなみに、vlucas/phpdotenv の php 環境要件はずっと低いけど、php: ^5.5.9 || ^7.0で動きます。
vlucas/phpdotenv のほうが人気の理由がこれにあるかもしれません。
# ライフサイクル
codeigniter のライフサイクル知りたかったけど、調べても見つからなくて、そういえば laravel はどうかなあと laravel のライフサイクルを調べました。
laravel ライフサイクル
- public/index.php
- Http/Console カーネル(
$bootstrappersmiddleware)
リクエスト実行前$bootstrappers 配列に定義しているサービスプロバイダ,env,config,Facadeとかがここでこ見込まれる。 - Router
- Controller
- View
laravel $bootstrappers
vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php
protected $bootstrappers = [
\Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class,
\Illuminate\Foundation\Bootstrap\LoadConfiguration::class,
\Illuminate\Foundation\Bootstrap\HandleExceptions::class,
\Illuminate\Foundation\Bootstrap\RegisterFacades::class,
\Illuminate\Foundation\Bootstrap\RegisterProviders::class,
\Illuminate\Foundation\Bootstrap\BootProviders::class,
];
laravel config 読み込み LoadConfiguration
vendor/laravel/framework/src/Illuminate/Foundation/Bootstrap/LoadConfiguration.php
public function bootstrap(Application $app)
{
$items = [];
if (file_exists($cached = $app->getCachedConfigPath())) {
$items = require $cached;
$loadedFromCache = true;
}
$app->instance('config', $config = new Repository($items));
if (! isset($loadedFromCache)) {
$this->loadConfigurationFiles($app, $config);
}
$app->detectEnvironment(function () use ($config) {
return $config->get('app.env', 'production');
});
date_default_timezone_set($config->get('app.timezone', 'UTC'));
mb_internal_encoding('UTF-8');
}
# codeigniter のフック
フレームワークコアの拡張として紹介されていますが、非常にわかりやすくて使いやすい!これですぐに middleware やアクセス トラッキング、IP 制限など作れてしまう!
# フック有効化
application/config/config.phpファイル編集でフックの有効化 $config['enable_hooks'] = TRUE;
# フック定義
application/config/hooks.php
<?php
defined('BASEPATH') or exit('No direct script access allowed');
use Symfony\Component\Dotenv\Dotenv;
$hook['pre_controller'] = array(
'class' => 'MyClass',
'function' => 'Myfunction',
'filename' => 'Myclass.php',
'filepath' => 'hooks',
'params' => array('beer', 'wine', 'snacks')
);
$hook['pre_system'] = function () {
try {
$dotenv = new Dotenv();
$dotenv->load(APPPATH . '../.env');
} catch (Exception $e) {
//
}
function env(string $key, $default = null)
{
$value = $_ENV[$key];
return $value ?? $default;
}
};
# フックポイント
- pre_system
システム実行中の非常に早い段階で呼び出されます。ベンチマーククラスと フッククラスだけがこの時点でロードされています。ルーティングや 他のプロセスは実行されていません。
- pre_controller
いずれかのコントローラが呼び出される直前に呼び出されます。 すべての基本クラス、ルーティング、およびセキュリティチェックは実行済みです。
- post_controller_constructor
コントローラがインスタンス化された直後に、 しかしメソッド呼び出しをする前に呼び出されます。
- post_controller
コントローラの実行が完全に終わった直後に呼び出されます。
- display_override
_display()メソッドをオーバーライドします。システム実行の最後で ウェブブラウザに確定ページを送信するために使用されるものです。これにより 独自の方法による表示が可能になります。 CI スーパーオブジェクトへの参照が $this->CI =& get_instance() により必要となること、 そして確定データは $this->CI->output->get_output() を呼び出すことによって利用できるようになることを覚えておいてください。 - cache_override
_display_cache()メソッドのかわりに独自のメソッドを呼び出すことができます。これにより、 独自のキャッシュ表示機構を使用できるようになります。 - post_system
最終的にレンダリングされるページがブラウザに送られた後、 つまりブラウザに確定データが送信されシステム実行が終了する時に 呼び出されます。
ここでみる codeigniter のライフサイクル
- ベンチマーククラス & フッククラス
- config 系設定ファイル
- controller & model
- view など display メソッド
参考:フック――フレームワークコアの拡張 (opens new window)
# twig テンプレートエンジン実装
composer install してから MY_Controller の設定
application/config/config.php ファイル composer autoload 設定
$config['composer_autoload'] = true;composer_autoload を true に変更application/core/MY_Controller.php ファイル設定
# twig2 初期設定
<?php
class MY_Controller extends CI_Controller
{
protected $twig;
public function __construct()
{
parent::__construct();
$loader = new Twig_Loader_Filesystem('./application/views');
$this->twig = new Twig_Environment($loader);
// option設定
// $this->twig = new Twig_Environment($loader, array('cache' => APPPATH.'/cache/twig', 'debug' => true));
// twig global 設定
// if (!empty($this->session->is_login)) {
// $this->twig->addGlobal('is_login', $this->session->is_login);
// }
// debug
// $this->output->enable_profiler('TRUE');
}
}
# twig3 初期設定
<?php
class MY_Controller extends CI_Controller
{
protected $twig;
public function __construct()
{
parent::__construct();
// Twig 初期化
$loader = new \Twig\Loader\FilesystemLoader('../application/views');
$this->twig = new \Twig\Environment($loader, [
// 'cache' => '/path/to/compilation_cache',
]);
}
}
# Class 'Twig_Loader_Filesystem' not found エラー対応
An uncaught Exception was encountered
Type: Error
Message: Class 'Twig_Loader_Filesystem' not found
twig loader 見つからないと言ってます。twig3 初期設定と従来 twig2 の初期設定と違う ので twig2 から twig3 にアップしたらエラー出ます。
# The "./application/views" directory does not exist エラー対応
An uncaught Exception was encountered
Type: Twig\Error\LoaderError
Message: The "./application/views" directory does not exist ("\x\public_html./application/views").
public index.php 対してのパス設定ミスが原因
$loader = new \Twig\Loader\FilesystemLoader('../application/views');
環境によりますが、MY_Controller.php の\Twig\Loader\FilesystemLoader を../application/viewsに設定したり、./application/viewsに設定したりする必要があります。
# Call to a member function display() on null エラー対応
An uncaught Exception was encountered
Type: Error
Message: Call to a member function display() on null
display function が見つからないことが原因 $this->twig にセットしてない可能性?
# Call to a member function insert() on null エラー対応
An uncaught Exception was encountered
Type: Error
Message: Call to a member function insert() on null
model insert 呼び込みできないエラー
$this->load->model('model_name')忘れているでは?ライブラリ自動読み込みしてないでは?
config/autoload.php
$autoload['libraries'] = array('database');
# Invalid argument supplied for foreach() エラー対応
A PHP Error was encountered
Severity: Warning
Message: Invalid argument supplied for foreach()
foreach()で回す時に出てくるエラーで、foreach が使えるのは配列とオブジェクトだけって怒られています
回す前に(array)配列変換してあげることで問題解決
foreach((array) $items as $i){
echo $i;
}