Interesting read: https://www.clariontech.com/blog/php-vs....ss-in-2025
↧
PHP vs. Other Web Development Technologies: Why PHP is Still the Boss in 2025
↧
PHP Developer Salary Guide 2025
A very interesting read about pay for PHP developers. Check it out. (DEV.TO link): https://dev.to/arasosman/php-developer-s...-2025-3d2e
↧
↧
CI4 Authentication: Best Practices in 2025?
Hey everyone, great to be back in the CodeIgniter community after a few years. For a new project in CI4, what's the current consensus on the best way to handle user authentication? Is the built-in Shield library the recommended way to go for most projects, or are there other popular third-party options worth looking into? Cheers.
↧
CSP unsafe-inline and Kint clashing
Hello!
I need to enable "unsafe-inline" in my scriptSrc in CSP to get a script to work. This third party script creates a new script tag, which has no nonce. It's unfortunate, but that's what the client needs in this case.
I can add "unsafe-inline" to my CSP config. However, the final CSP header ends up containing a nonce, and Chrome is saying that it ignores the "unsafe-inline" whenever a nonce is present in the list. It looks like in the Autoloader's configureKint() function, it generates this nonce if CSP is enabled. So that leaves me stuck, because I need CSP enabled, but I also need no nonce to exist in the list. I think it would make sense to rework it a little bit in such a way where you can enable "unsafe-inline" in which case it should be able to be smart enough to stop producing any nonces.
I think this concept applies to both styleSrc and scriptSrc.
I just tested it and indeed, no nonce appears in the final header if I comment out the CSP portion of the configureKent() function.
Another idea on how to possibly fix this is to only generate those nonces if they are needed/used. Cheers, thanks for thinking about this with me.
I need to enable "unsafe-inline" in my scriptSrc in CSP to get a script to work. This third party script creates a new script tag, which has no nonce. It's unfortunate, but that's what the client needs in this case.
I can add "unsafe-inline" to my CSP config. However, the final CSP header ends up containing a nonce, and Chrome is saying that it ignores the "unsafe-inline" whenever a nonce is present in the list. It looks like in the Autoloader's configureKint() function, it generates this nonce if CSP is enabled. So that leaves me stuck, because I need CSP enabled, but I also need no nonce to exist in the list. I think it would make sense to rework it a little bit in such a way where you can enable "unsafe-inline" in which case it should be able to be smart enough to stop producing any nonces.
I think this concept applies to both styleSrc and scriptSrc.
I just tested it and indeed, no nonce appears in the final header if I comment out the CSP portion of the configureKent() function.
Another idea on how to possibly fix this is to only generate those nonces if they are needed/used. Cheers, thanks for thinking about this with me.
↧
Validation of nested inputs
Hi there,
Im having the following POST input:
Array (
[parcel] => Array (
[1] => Array (
[weight] => 2.00
[width] => 30.00
[height] => 30.00
[length] => 30.00
[type] => pack
)
[2] => Array
(
[weight] => 2.00
[width] => 30.00
[height] => 30.00
[length] => 30.00
[type] => pack
)
)
)
The input names look like "parcel[$parcel_id][width]" etc with validation rules such as "required|numeric|greater_than[1]|less_than[1000]".
If for some reason I change the input name to "parcel[$parcel_id][width][test]" CI4 skips the rules and the width becomes array which is a confusing. Why does it skip the checks? If I have numeric for example I expect at that level to have numbers not arrays so the logical result would be to trigger the numeric fail.
Also If i remove one of the fields completely, when I retriive the results with getValidated() the field in the array is missing and again no require error is produced.
Is this normal behavior? In order to validate properly fields like that I will have to make separate validation which makes the usage of the validation service pointless.
Kindly advice.
Im having the following POST input:
Array (
[parcel] => Array (
[1] => Array (
[weight] => 2.00
[width] => 30.00
[height] => 30.00
[length] => 30.00
[type] => pack
)
[2] => Array
(
[weight] => 2.00
[width] => 30.00
[height] => 30.00
[length] => 30.00
[type] => pack
)
)
)
The input names look like "parcel[$parcel_id][width]" etc with validation rules such as "required|numeric|greater_than[1]|less_than[1000]".
If for some reason I change the input name to "parcel[$parcel_id][width][test]" CI4 skips the rules and the width becomes array which is a confusing. Why does it skip the checks? If I have numeric for example I expect at that level to have numbers not arrays so the logical result would be to trigger the numeric fail.
Also If i remove one of the fields completely, when I retriive the results with getValidated() the field in the array is missing and again no require error is produced.
Is this normal behavior? In order to validate properly fields like that I will have to make separate validation which makes the usage of the validation service pointless.
Kindly advice.
↧
↧
Intellij IDEA / PHPStorm Plugin
Hi folks!
As I recently wrote in Slack I think about to contribute the CI4 from DX point of view and I'd like to propose a tight PHPStorm integration with the CI4 framework:
- autocompletion
- references search
- inspections
- quick fixes
- etc
Is there anyone who prefer PHPStorm in CI4 development?
The plugin is for frameworks users, not core developers itself (but sometimes yes).
Last time I use CodeIgniter was 2015 maybe. I really like to see that it's another PHP player who has big enough audience.
I would need your help to build the plugin, especially the most important cases where the plugin may bring the most help, i.e. validation rules: set_rules(
'username', 'Username',
'required|min_lengths[5]|max_length[12]|is_unique[users.username]',
array(
'required' => 'You have not provided %s.',
'is_unique' => 'This %s already exists.'
)
);
Almost all these strings can be completed and referenced by the plugin. It also may show warning if `min_lengths` doesn't exist and suggest to use `min_length`.
It will be a lot of work, but with your help I think we can do it.
What do you think?
As I recently wrote in Slack I think about to contribute the CI4 from DX point of view and I'd like to propose a tight PHPStorm integration with the CI4 framework:
- autocompletion
- references search
- inspections
- quick fixes
- etc
Is there anyone who prefer PHPStorm in CI4 development?
The plugin is for frameworks users, not core developers itself (but sometimes yes).
Last time I use CodeIgniter was 2015 maybe. I really like to see that it's another PHP player who has big enough audience.
I would need your help to build the plugin, especially the most important cases where the plugin may bring the most help, i.e. validation rules: set_rules(
'username', 'Username',
'required|min_lengths[5]|max_length[12]|is_unique[users.username]',
array(
'required' => 'You have not provided %s.',
'is_unique' => 'This %s already exists.'
)
);
Almost all these strings can be completed and referenced by the plugin. It also may show warning if `min_lengths` doesn't exist and suggest to use `min_length`.
It will be a lot of work, but with your help I think we can do it.
What do you think?
↧
Coming in 4.7: Controller Attributes
One of the things I'd like to see is a push towards simplifying again. I think the Auto-Routing (Improved) is a great direction to lean new developers toward. i think the mental model might just seem a little less complex than the defined routes that we added in 4. Don't get me wrong, the defined routes are powerful, and they serve as excellent documentation. But for those just getting started. or those that prefer things a little simpler, the auto-routing is fantastic. It was missing features that you could get through defined routes, though.
So, to attempt to bring feature parity between defined routes and auto-routing, we are adding Controller Attributes in 4.7. There are three atributes currently defined, but they pack a lot of features into them.
Filters
Any of the defined Controller Filters can be run either on the controller as a whole, or on a single method:
Restrict
You can restrict the controller, or a single method, by environment, host, or subdomain:
Cache
This is one that I've thought about for a long time but adding it in isolation never felt right. Combined with the other features, it seemed like the perfect time.
This allows you to easily specify that the output of a controller's method should be cached. This means that the method never actually gets executed while it's cached.
Unless I missed something, that should see feature parity when you realize that prefixes and groups, etc are already handled by the auto-routing method itself.
I hope you'll find this helpful.
The merged PR can be seen here: https://github.com/codeigniter4/CodeIgniter4/pull/9745
So, to attempt to bring feature parity between defined routes and auto-routing, we are adding Controller Attributes in 4.7. There are three atributes currently defined, but they pack a lot of features into them.
Filters
Any of the defined Controller Filters can be run either on the controller as a whole, or on a single method:
PHP Code:
#[Filter(by: 'group', having: ['admin', 'superadmin'])]
class AdminController extends BaseController
{
#[Filter(by: 'permission', having: ['users.manage'])]
public function users()
{
// Will have 'group' filter with ['admin', 'superadmin']
// and 'permission' filter with ['users.manage']
}
}
Restrict
You can restrict the controller, or a single method, by environment, host, or subdomain:
PHP Code:
#[Restrict(environment: ['development', '!production'])]
class HomeController extends BaseController
{
// Restrict access by hostname
#[Restrict(hostname: 'localhost')]
public function index()
{
}
// Multiple allowed hosts
#[Restrict(hostname: ['localhost', '127.0.0.1', 'dev.example.com'])]
public function devOnly()
{
}
// Restrict to subdomain, e.g. admin.example.com
#[Restrict(subdomain: 'admin')]
public function deleteItem($id)
{
}
}
Cache
This is one that I've thought about for a long time but adding it in isolation never felt right. Combined with the other features, it seemed like the perfect time.
This allows you to easily specify that the output of a controller's method should be cached. This means that the method never actually gets executed while it's cached.
PHP Code:
class HomeController extends BaseController
{
// Cache this method's response for 2 hours
#[Cache(for: 2 * HOUR)]
public function index()
{
return view('welcome_message');
}
// Custom cache key
#[Cache(for: 10 * MINUTE, key: 'custom_cache_key')]
public function custom()
{
return 'This response is cached with a custom key for 10 minutes.';
}
}
Unless I missed something, that should see feature parity when you realize that prefixes and groups, etc are already handled by the auto-routing method itself.
I hope you'll find this helpful.
The merged PR can be seen here: https://github.com/codeigniter4/CodeIgniter4/pull/9745
↧
Coming in 4.7: API Improvements
I've been thinking a lot about CodeIgniter's place in the market recently. One place that I think CodeIgniter is quite strongly placed is for building API's, and API's are only growing in usage to complement heavy Javascript. frontends, acting as MCP servers, etc.
We already have some decent tools to help out with that, but I think that we can add some tools to make CodeIgniter very attractive for those types of projects. In the 4.7 release, we have already made some strides toward that, and I still have a couple of more ideas I'd like to implement.
Here's a quick. breakdown of what's already been accepted and merged in:
API Response Pagination
A new method, paginate(), has been added to the APIResponseTrait to help standardize and provide consistent, additional information for API clients, with very little work on the developer's part.
While this example shows using a model, it also works with a query builder instance like you would get from.
This would return a response shaped like:
[json]
{
"data": [
{
"id": 1,
"username": "admin",
"email": "admin@example.com"
},
{
"id": 2,
"username": "user",
"email": "user@example.com"
}
],
"meta": {
"page": 1,
"perPage": 20,
"total": 2,
"totalPages": 1
},
"links": {
"self": "http://example.com/users?page=1",
"first": "http://example.com/users?page=1",
"last": "http://example.com/users?page=1",
"next": null,
"previous": null
}
}
[/json]
This also follows best practices and RFC 8288 by setting the Links header with the same links in our response, and the X-Total-Count header with the total number of results. This provides information for both the human-readable portion used in frontend work, and with some machine readable information in the headers that is much easier and less error prone to be parsed.
API Transformers
Second, we have provided a simple mechanism for Transformers - classes that convert your raw database results to a safe, structured format to pass back to the API client. This is widely considered a must-have best practice so that sensitive information isn't accidentally returned from API calls.
We have kept this simple in implementation, but with enough flexibility to keep it useful. They're simple to create:
This example shows the data as an array received from the database. If you're using Entities, you would of course have full access to any computed properties, etc to use within the Transformer. In your controller you can simply do:
This shows transforming both a single resource and any number of resources. This also works with the new paginate method shown above:
I won't give examples here, but you can define functions that will automatically include other data. You can use this for include related model's data, etc.
Finally, we give the API client some flexibility by allowing them to choose to limit which includes are actually included, or to restrict the fields returned.
I think these two go a long way toward getting us toward really strong API powers. My next PR is getting close, providing a better error-handling mechanism using the Problem Details RFC as a standard error format. Then some new docs and probably a new generator command to quickly stub out a working API resource CRUD.
When the user
We already have some decent tools to help out with that, but I think that we can add some tools to make CodeIgniter very attractive for those types of projects. In the 4.7 release, we have already made some strides toward that, and I still have a couple of more ideas I'd like to implement.
Here's a quick. breakdown of what's already been accepted and merged in:
API Response Pagination
A new method, paginate(), has been added to the APIResponseTrait to help standardize and provide consistent, additional information for API clients, with very little work on the developer's part.
PHP Code:
class UserController extends BaseController
{
use ResponseTrait;
public function index()
{
$model = model(UserModel::class)
->where('active', 1);
return $this->paginate($model, 20);
}
}
While this example shows using a model, it also works with a query builder instance like you would get from
Code:
db_connect()->table('users')This would return a response shaped like:
[json]
{
"data": [
{
"id": 1,
"username": "admin",
"email": "admin@example.com"
},
{
"id": 2,
"username": "user",
"email": "user@example.com"
}
],
"meta": {
"page": 1,
"perPage": 20,
"total": 2,
"totalPages": 1
},
"links": {
"self": "http://example.com/users?page=1",
"first": "http://example.com/users?page=1",
"last": "http://example.com/users?page=1",
"next": null,
"previous": null
}
}
[/json]
This also follows best practices and RFC 8288 by setting the Links header with the same links in our response, and the X-Total-Count header with the total number of results. This provides information for both the human-readable portion used in frontend work, and with some machine readable information in the headers that is much easier and less error prone to be parsed.
API Transformers
Second, we have provided a simple mechanism for Transformers - classes that convert your raw database results to a safe, structured format to pass back to the API client. This is widely considered a must-have best practice so that sensitive information isn't accidentally returned from API calls.
We have kept this simple in implementation, but with enough flexibility to keep it useful. They're simple to create:
PHP Code:
class UserTransformer extends BaseTransformer
{
public function toArray(mixed $resource): array
{
return [
'id' => $resource['id'],
'username' => $resource['name'], // Renaming the field
'email' => $resource['email'],
'member_since' => date('Y-m-d', strtotime($resource['created_at'])),
];
}
}
This example shows the data as an array received from the database. If you're using Entities, you would of course have full access to any computed properties, etc to use within the Transformer. In your controller you can simply do:
PHP Code:
class Users extends BaseController
{
use ResponseTrait;
public function show($id)
{
$user = model('UserModel')->find($id);
if (! $user) {
return $this->failNotFound('User not found');
}
$transformer = new UserTransformer();
return $this->respond($transformer->transform($user));
}
public function index()
{
$users = model('UserModel')->findAll();
$transformer = new UserTransformer();
return $this->respond($transformer->transformMany($users));
}
}
This shows transforming both a single resource and any number of resources. This also works with the new paginate method shown above:
PHP Code:
public function index()
{
$model = model(UserModel::class);
return $this->paginate(resource: $model, perPage: 20, transformWith: UserTransformer::class);
}
I won't give examples here, but you can define functions that will automatically include other data. You can use this for include related model's data, etc.
Finally, we give the API client some flexibility by allowing them to choose to limit which includes are actually included, or to restrict the fields returned.
I think these two go a long way toward getting us toward really strong API powers. My next PR is getting close, providing a better error-handling mechanism using the Problem Details RFC as a standard error format. Then some new docs and probably a new generator command to quickly stub out a working API resource CRUD.
When the user
↧
PHP 8.5: What's New and Changed
↧
↧
HTTP_REFERRER and redirect()->back()
When users of my app use nuerous open browser tabs working with my CodeIgiter apps they get erroneous output from my use of the redirect()->back() function in my controllers. They get the content of a different page then the one they are curretly viewing.
Is ther any way to fix this other than abadoning the redirect()->back() method and use redirect()->to("/aspecific/uri"); ???
I've resorted to putting
in all my forms.
Is ther any way to fix this other than abadoning the redirect()->back() method and use redirect()->to("/aspecific/uri"); ???
I've resorted to putting
PHP Code:
<input type="hidden" name="current_uri" value=<?= current_ur() ?>">
↧
Warnings on public type variables
1) I have noticed that we keep getting warnings on missing parameter type missing
Example:
These are ridded through out the CodeIgniter classes etc;
Also:
2) when creating view pages and passing $data you have to
create a comment TYPE, like this or you get a undefined variable warning.
Example:
Example:
PHP Code:
// warning
private $test;
// should be
private string $test
Also:
2) when creating view pages and passing $data you have to
create a comment TYPE, like this or you get a undefined variable warning.
Example:
PHP Code:
/**
* Contact View.
*
* @var TYPE_NAME $title
*
*/
↧
isAJAX() error on called controller
Complex issue here...at least for me.
In my project there is a controller that use other controllers...just to avoid code duplication.
So, for example, I have the main controller Register.php that use a Pictures controller.
To do so, in Register controller I have:
The called Picture code is:
I get the following error ( if I remove the isAJAX() code the controller return correct data:
I'm wondering if th Pictures controller could be used both for AJAX call o from other controller
In my project there is a controller that use other controllers...just to avoid code duplication.
So, for example, I have the main controller Register.php that use a Pictures controller.
To do so, in Register controller I have:
Code:
namespace Acme\Admin\Controllers\Registers\Civil;
use App\Controllers\BaseController;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use Psr\Log\LoggerInterface;
use Acme\Admin\Controllers\Registers\Civil\Pictures\Pictures;
use Acme\Admin\Models\Registers\Civil\RegisterModel;
use Codeigniter\Exceptions\PageNotFoundException;
class Register extends BaseController
{
protected $pictures;
public function initController(
RequestInterface $request,
ResponseInterface $response,
LoggerInterface $logger,
) {
parent::initController($request, $response, $logger);
helper('Acme\Admin\Helpers\formatdate');
$this->pictures = new Pictures();
}
public function getRecord($id = null)
{
$model = new RegisterModel();
# Get rcord details for given register id
$record = $model->getRegistration($id);
# Get images by register id
if($pics = $model->getRecordPictures($id))
{
foreach ($pics as $key => $pic)
{
$res = $this->pictures->data($pic->imgid);
}
}
return $this->response->setJSON($jsondata);
}
}The called Picture code is:
Code:
namespace Acme\Admin\Controllers\Registers\Civil\Pictures;
use App\Controllers\BaseController;
use CodeIgniter\Files\File;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use Psr\Log\LoggerInterface;
use Acme\Admin\Models\Registers\Civil\PicturesModel;
use Codeigniter\Exceptions\PageNotFoundException;
class Pictures extends BaseController
{
public function initController(
RequestInterface $request,
ResponseInterface $response,
LoggerInterface $logger,
) {
parent::initController($request, $response, $logger);
}
public function data($imgid = null)
{
$modelPictures = new PicturesModel();
$jsondata['status'] = TRUE;
$jsondata['error'] = FALSE;
if(!$picture = $modelPictures->getPictureData($imgid))
{
$jsondata['status'] = FALSE;
$jsondata['error'] = TRUE;
$jsondata['msg'] = 'There are no data.';
return $this->response->setJSON($jsondata);
}
$jsondata['picture'] = $picture;
if(!$this->request->isAJAX())
{
return $jsondata;
}
return $this->response->setJSON($jsondata);
}
}I get the following error ( if I remove the isAJAX() code the controller return correct data:
I'm wondering if th Pictures controller could be used both for AJAX call o from other controller
Code:
Call to a member function isAJAX() on null↧
CodeIgniter 4.6.3 fresh install - 404 error
I have just made a fresh installation of version 4.6.3. I have moved the contents of the public folder to main folder. I have configured baseurl in the app.php, paths.php path in index.php correctly.
If I type, http://localhost/twiboxn, the default home controller works and shows the welcome message.
If I type http://localhost/twiboxn/home/index, it goes to 404 error page not found.
If I type, http://localhost/twiboxn, the default home controller works and shows the welcome message.
If I type http://localhost/twiboxn/home/index, it goes to 404 error page not found.
↧
↧
CodeIgniter Forums
All of you do not know this but Lonnie has been working on fixing our CodeIgniter Forums for the pass two weeks.
He has done an outstanding job on getting it working fast and more secure.
Please everyone give Lonnie a Big Thank You!
Thank you Lonnie.
He has done an outstanding job on getting it working fast and more secure.
Please everyone give Lonnie a Big Thank You!
Thank you Lonnie.
↧
Is PHP Finally Shedding Its “Legacy” Label in 2025?
For years, PHP has carried the “old and messy” reputation compared to modern languages like Node.js, Go, or Python. But with PHP 8+ introducing JIT, Fibers, attributes, union types, and significant performance boosts, many developers are starting to see it in a new light.
Big players like WordPress, Drupal, and Laravel still power massive portions of the web, and new frameworks are pushing PHP into areas beyond traditional CMS use. Some benchmarks even show PHP 8.3 competing closely with Node in performance-heavy workloads.
Do you think PHP has finally shaken off its “legacy” stigma? Or will the perception always linger, no matter how much the language evolves?
Big players like WordPress, Drupal, and Laravel still power massive portions of the web, and new frameworks are pushing PHP into areas beyond traditional CMS use. Some benchmarks even show PHP 8.3 competing closely with Node in performance-heavy workloads.
Do you think PHP has finally shaken off its “legacy” stigma? Or will the perception always linger, no matter how much the language evolves?
↧
CI 3.1.10 how to update but not to 4?
Is there a way to update to the last version of CI 3.x? I can't seem to find anything as it's all directing to 4.
Thanks
Thanks
↧
Help needed
Hi there,
I am moving a CI3 app from a single server to run in 3 docker containers.
Using docker-compose with images:
With nginx set to the same config as previously, with the exception of the connection to PHP:
and PHP listening on :9000 (default within the image)
When I post to save a record, the controller also receives the session cookie in $_REQUEST.
This is obviously unwanted behaviour as it should be handled (and removed) before it gets to the application controller.
This does not happen in the monolithic setup.
Any ideas on what is causing it or how to fix it?
Help, please
I am moving a CI3 app from a single server to run in 3 docker containers.
Using docker-compose with images:
Code:
nginx:stable-alpine
php:8.1-fpm-alpine
mariadb:latestWith nginx set to the same config as previously, with the exception of the connection to PHP:
Code:
fastcgi_pass php:9000;and PHP listening on :9000 (default within the image)
When I post to save a record, the controller also receives the session cookie in $_REQUEST.
This is obviously unwanted behaviour as it should be handled (and removed) before it gets to the application controller.
This does not happen in the monolithic setup.
Any ideas on what is causing it or how to fix it?
Help, please
↧
↧
Question about image validation size setting
Hello,
How can I accurately specify the image upload size in a CI4 application? I wanted to upload an image that is 49.7 KB, but I'm encountering a size restriction. How can I adjust the limit to a maximum of 5 MB? Currently, I believe I've set it to 5 MB. Here are my image rules:
How can I accurately specify the image upload size in a CI4 application? I wanted to upload an image that is 49.7 KB, but I'm encountering a size restriction. How can I adjust the limit to a maximum of 5 MB? Currently, I believe I've set it to 5 MB. Here are my image rules:
Code:
$profile_pix_validation_rule = [
'userfile' => [
'label' => 'Image File',
'errors' => ['max_size' => "Image size can be 10mb max"],
'rules' => [
'uploaded[passport_image]',
'mime_in[passport_image,image/jpg,image/jpeg,image/png]',
'max_size[profile_pix,5120]',
'max_dims[profile_pix,1024,768]',
],
],
];↧
php spark shows only "" (empty quotes) — other CI4 projects work fine
Hi everyone,
I’m facing a strange issue with one specific CodeIgniter 4 (v4.6.3) project. I did try to update composer but getting same error When I run any php spark command (for example php spark list or php spark serve), I only get:
I’m facing a strange issue with one specific CodeIgniter 4 (v4.6.3) project. I did try to update composer but getting same error When I run any php spark command (for example php spark list or php spark serve), I only get:
Code:
php spark serve
""
and nothing else — no error, no output.
Composer dependencies are fully updated (composer update ran without issues).The vendor/ folder exists.PHP version: 8.x (from XAMPP)CI4 version: 4.6.3
However Other CI4 projects on the same machine work perfectly.↧
isset returns false for a property, dd shows the property exists
The $query->getResultObject() function returns an array of objects.
I am trying to access a property from one of the objects so returned but I keep getting the error message "Undefined property: stdClass::$propertyName".
When I inspect the property,
returns false but
shows the property and its value.
I cannot tell what the matter is. I am using CI_VERSION = '4.6.3';
I am trying to access a property from one of the objects so returned but I keep getting the error message "Undefined property: stdClass::$propertyName".
When I inspect the property,
PHP Code:
isset()
PHP Code:
dd()
I cannot tell what the matter is. I am using CI_VERSION = '4.6.3';
↧