{"id":1409,"date":"2023-04-04T17:30:00","date_gmt":"2023-04-04T12:00:00","guid":{"rendered":"https:\/\/qalbit.com\/blog\/?p=1409"},"modified":"2026-07-30T15:26:43","modified_gmt":"2026-07-30T09:56:43","slug":"simplifying-web-development-with-codeigniter-hmvc-architecture","status":"publish","type":"post","link":"https:\/\/qalbit.com\/blog\/simplifying-web-development-with-codeigniter-hmvc-architecture\/","title":{"rendered":"CodeIgniter HMVC: Modules, PHP 8.2 Fixes, and the CodeIgniter 4 Question"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Most people reading this are not starting a new CodeIgniter HMVC project. They are keeping an old one running.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">That is the honest state of this topic. Modular Extensions \u2013 HMVC gave CodeIgniter 3 something the framework never shipped: real modules, each with its own controllers, models, and views, callable from anywhere in the application. A lot of software got built that way between roughly 2011 and 2019, and a lot of it is still in production. Then PHP 8.2 landed, deprecated dynamic properties, and every one of those applications started filling its logs with the same warning.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This guide covers three things, in the order people actually need them: the fix for the PHP 8.2 deprecation notices, how the HMVC module structure works if you are inheriting a codebase you did not write, and what your options are on CodeIgniter 4 \u2014 where the wiredesignz extension does not work at all. We do <a href=\"\/technologies\/codeigniter\/\" data-type=\"link\" data-id=\"\/technologies\/codeigniter\/\">legacy CodeIgniter work<\/a>, so this is written from the maintenance side rather than the tutorial side.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If you landed here from a search for a specific error message, skip straight to the next section.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Fixing &#8220;Creation of dynamic property CI::$load is deprecated&#8221;<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">This is the error that brings most people here:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Deprecated: Creation of dynamic property CI::$load is deprecated\nin \/application\/third_party\/MX\/Base.php on line NN\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">You may also see it as <code>CI::$app-&gt;load<\/code>, or against other properties \u2014 <code>CI::$config<\/code>, <code>CI::$router<\/code>, <code>CI::$db<\/code>. Same cause, same fix.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Worth knowing up front: this is not really an HMVC problem. It is a CodeIgniter 3 problem that HMVC makes louder. The framework core throws the same class of notice on its own \u2014 there are reports against <a href=\"https:\/\/github.com\/bcit-ci\/CodeIgniter\/issues\/6278\"><code>CI_URI::$config<\/code> in <code>system\/core\/URI.php<\/code><\/a> and <a href=\"https:\/\/github.com\/bcit-ci\/CodeIgniter\/issues\/6298\"><code>CI_Loader::$input<\/code> in <code>system\/core\/Loader.php<\/code><\/a> with no HMVC involved at all. Patching the extension cleans up the extension. The core will keep going.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Why it happens<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">PHP 8.2 <a href=\"https:\/\/wiki.php.net\/rfc\/deprecate_dynamic_properties\">deprecated dynamic properties<\/a>. Assigning to a property that was never declared on the class now emits a deprecation notice on first assignment:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>class Example {}\n\n$e = new Example();\n$e-&gt;something = 'value';\n\/\/ Deprecated: Creation of dynamic property Example::$something is deprecated\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Modular Extensions \u2013 HMVC was written years before this rule existed, and it leans on dynamic assignment deliberately. The <code>CI<\/code> class in <code>MX\/Base.php<\/code> exists to mirror CodeIgniter&#8217;s loaded components onto module controllers, and it does that by assigning properties it never declared. Under PHP 8.1 that was normal. Under 8.2 it is a deprecation. Under a future major version it will very likely be an error.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Two things worth knowing before you start:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Your error message names the exact file and line.<\/strong> Do not go hunting. In a standard install it points at <code>application\/third_party\/MX\/Base.php<\/code>, but if a previous developer moved things around it may not. Trust the trace over any tutorial, including this one.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Nothing is broken yet.<\/strong> These are deprecation notices, not fatal errors. The application still runs. What they do is flood your logs, slow things down under volume, and occasionally leak into output if <code>display_errors<\/code> is on in an environment where it should not be. So this is urgent in the sense that it will get worse, not in the sense that the site is down.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Fix 1: <code>#[AllowDynamicProperties]<\/code> \u2014 fastest<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">PHP 8.2 shipped <a href=\"https:\/\/www.php.net\/manual\/en\/class.allowdynamicproperties.php\">an attribute that opts a class back into the old behaviour<\/a>. Add it above the class declaration in <code>MX\/Base.php<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#&#91;AllowDynamicProperties]\nclass CI\n{\n    \/\/ ... existing class body unchanged\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Two details that make this the pragmatic choice:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The effect is inherited.<\/strong> Attributes themselves are not inherited in PHP, but the dynamic-property permission granted by <code>AllowDynamicProperties<\/code> is \u2014 child classes of a marked class also allow dynamic properties without declaring the attribute themselves. Patch the base class and the subclasses are covered.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>It is safe on older PHP.<\/strong> In PHP 7.x, <code>#<\/code> opens a comment, so the whole line is ignored rather than causing a parse error. If your deployment pipeline still touches PHP 7 anywhere, this will not break it.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If the file uses namespaces, you need the leading backslash or an import, or PHP will look for the attribute inside the current namespace and silently fail to apply it:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#&#91;\\AllowDynamicProperties]\nclass CI { }\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">You will likely need the same attribute on <code>MX_Controller<\/code> and <code>MX_Model<\/code> in <code>third_party\/MX\/Controller.php<\/code> and <code>third_party\/MX\/Model.php<\/code>, since those assign loaded libraries onto themselves the same way. Run the app, read the log, patch what actually fires \u2014 rather than pre-emptively decorating every class in the directory.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Fix 2: Declare the properties \u2014 cleaner<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The attribute silences the warning. Declaring the properties fixes the underlying design:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>class CI\n{\n    public $load;\n    public $config;\n    public $router;\n    public $uri;\n    public $lang;\n    public $input;\n    public $output;\n    public $security;\n    public $benchmark;\n    public $db;\n\n    \/\/ ... existing class body\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The catch is that you have to know the full set, and the full set depends on which libraries your application autoloads. Miss one and it still warns. Work from your log: each notice names the property, so collect them over a full pass through the application before you start writing declarations.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This is the right fix if you expect to be on this codebase for years, or if you are heading toward PHP 9 where dynamic properties are expected to become an error rather than a notice. It is the wrong fix if you are migrating off CodeIgniter 3 in the next six months \u2014 you would be doing careful work on code you are about to delete.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Fix 3: Suppress the notice \u2014 do this only as a stopgap<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ In index.php, or your environment's error_reporting call\nerror_reporting(E_ALL &amp; ~E_DEPRECATED &amp; ~E_STRICT);\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This hides every deprecation notice in the application, not just this one. That means the next PHP version&#8217;s warnings \u2014 the ones that tell you what will break in the version after \u2014 are hidden too. It is a reasonable thing to do at 2am to stop a log partition filling up. It is not a fix, and if you use it, write yourself a ticket.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Verifying it worked<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Deprecation notices fire once per property per request, so a single page load is not a test. Clear your log, then exercise the routes that load the most libraries \u2014 usually an authenticated dashboard rather than the homepage \u2014 and check the log is still empty:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&gt; application\/logs\/log-$(date +%Y-%m-%d).php\n# hit several routes, then:\ngrep -c \"dynamic property\" application\/logs\/log-$(date +%Y-%m-%d).php\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If you patched <code>MX\/Base.php<\/code> and the notices moved to a different class, that is progress, not failure. Patch the next one.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">There is no upstream fix coming<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Before you go looking for a patched release: there isn&#8217;t one, and there is nobody to wait for.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The original Modular Extensions \u2013 HMVC lives in a <a href=\"https:\/\/bitbucket.org\/wiredesignz\/codeigniter-modular-extensions-hmvc\">Bitbucket repository<\/a> under Mercurial, and Bitbucket removed Mercurial support in 2020. The forks that exist on GitHub \u2014 <a href=\"https:\/\/github.com\/codinghamster\/codeigniter-modular-extensions-hmvc\"><code>codinghamster<\/code><\/a>, <code>natanfelles<\/code>, <code>brianwozeniak<\/code>, <code>invitecomm<\/code>, and others \u2014 are mirrors with small routing or Composer improvements. None of them advertises PHP 8.2 compatibility. The <code>codinghamster<\/code> fork is the most practical of them if you want Composer installation rather than copied directories, but it will not solve this for you either.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">That means the patch is yours to own, which makes the next point matter.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Keep a record of the patch<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">You are editing a third-party library inside <code>third_party\/<\/code>. The next developer who reinstalls the extension will wipe your fix without knowing it existed. Leave a comment above the attribute explaining what it is and why, and note it in the project README. If you installed via Composer, express it as a patch file rather than editing <code>vendor\/<\/code> \u2014 otherwise the next <code>composer install<\/code> silently undoes your afternoon.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The PHP Version Problem Behind All of This<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">It is worth understanding why these notices appeared at all, because the answer determines how much more effort CodeIgniter 3 deserves.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">CodeIgniter 3&#8217;s final release is 3.1.13. The branch is in maintenance mode \u2014 critical security patches, no active development. Versions 3.1.12 and 3.1.13 <a href=\"https:\/\/www.codeigniter.com\/userguide3\/changelog.html\">added compatibility with PHP 8.0 and PHP 8.1<\/a>, and that is where official support stops. There is no CodeIgniter 3 support for PHP 8.2 or newer.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Now put that next to <a href=\"https:\/\/www.php.net\/supported-versions.php\">PHP&#8217;s own release schedule<\/a>. <strong>PHP 8.1 reached end of life on 31 December 2025.<\/strong> The versions still receiving security support are 8.2, 8.3, and 8.4 \u2014 and PHP 8.2 is itself security-only, reaching end of life on 31 December 2026.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Read those two paragraphs together and the position is uncomfortable: the newest PHP your framework officially supports no longer gets security patches, and every PHP version that does get patched is officially unsupported by your framework. There is no configuration that is fully supported on both sides. You are choosing which kind of unsupported you prefer.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Running CI3 on PHP 8.2 or later is what most teams pick, and it works \u2014 with the deprecation notices this guide fixes, and with the understanding that you are now the maintainer of your own compatibility layer. That is a real, defensible choice. It is just worth making it deliberately rather than discovering it from a log file, and worth noting that 8.2 buys you less runway than it looks like.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Some of the patching is genuinely small. The extension&#8217;s own documentation, for instance, tells you to extend the form validation library like this so that callbacks work:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ application\/libraries\/MY_Form_validation.php\nclass MY_Form_validation extends CI_Form_validation\n{\n    public $CI;\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">That <code>public $CI;<\/code> is exactly the same declaration this whole guide is about \u2014 someone hit the dynamic-property problem long before PHP made it a deprecation, and solved it by declaring the property. The pattern you need is already in the codebase; PHP 8.2 just made it mandatory everywhere.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">How HMVC Actually Works<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">If you are maintaining a codebase someone else wrote, this is the mental model you need.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>MVC<\/strong> splits an application three ways. The model holds data and business rules, the view renders output, the controller mediates. One set of each, application-wide.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>HMVC<\/strong> \u2014 Hierarchical Model-View-Controller \u2014 makes that triad repeatable. Instead of one controllers directory for the whole application, each module gets its own controllers, models, and views, and modules can call each other. A <code>blog<\/code> module can render a widget from a <code>comments<\/code> module without either one knowing much about the other.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The word doing the work is <em>hierarchical<\/em>. A module&#8217;s request can trigger another module&#8217;s request, and that one can trigger a third. It is MVC that nests.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In practice you get:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Modules you can lift out.<\/strong> A <code>payments<\/code> module with its own MVC set can move to another project by copying a directory, assuming its dependencies come with it.<\/li>\n\n\n\n<li><strong>Widget-style composition.<\/strong> The parts of a page that repeat across templates \u2014 a cart summary, a notification bell \u2014 become module calls rather than helper functions with view fragments bolted on.<\/li>\n\n\n\n<li><strong>Merge conflicts that stay local.<\/strong> Two developers on two modules touch two directory trees. On flat MVC they both touch <code>controllers\/<\/code>.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">The cost is indirection. Tracing a request through four nested module calls is genuinely harder than reading one controller, and HMVC codebases tend to accumulate module-to-module dependencies that nobody drew a diagram of. On a small application it is overhead you will not recover.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">CodeIgniter 3 never shipped HMVC. The pattern arrived through wiredesignz&#8217;s Modular Extensions \u2013 HMVC, which is why almost every CI3 HMVC codebase you will meet has the same <code>third_party\/MX\/<\/code> directory in it.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"\/contact-us\/\"><img decoding=\"async\" width=\"1024\" height=\"338\" src=\"https:\/\/qalbit.com\/blog\/wp-content\/uploads\/2023\/04\/estimate-custom-codeigniter-1024x338.jpg\" alt=\"Get a Custom CodeIgniter Solution Now!\" class=\"wp-image-2558\" srcset=\"https:\/\/qalbit.com\/blog\/wp-content\/uploads\/2023\/04\/estimate-custom-codeigniter-1024x338.jpg 1024w, https:\/\/qalbit.com\/blog\/wp-content\/uploads\/2023\/04\/estimate-custom-codeigniter-300x99.jpg 300w, https:\/\/qalbit.com\/blog\/wp-content\/uploads\/2023\/04\/estimate-custom-codeigniter-768x253.jpg 768w, https:\/\/qalbit.com\/blog\/wp-content\/uploads\/2023\/04\/estimate-custom-codeigniter-1536x506.jpg 1536w, https:\/\/qalbit.com\/blog\/wp-content\/uploads\/2023\/04\/estimate-custom-codeigniter.jpg 1820w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Installing Modular Extensions \u2013 HMVC on CodeIgniter 3<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Most readers already have this installed and are debugging it rather than adding it. Either way, knowing where the pieces live is what makes the codebase readable.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">File placement<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Three things get dropped into an existing CodeIgniter 3 application:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>application\/\n\u251c\u2500\u2500 core\/\n\u2502   \u251c\u2500\u2500 MY_Loader.php      # extends CI_Loader, adds module awareness\n\u2502   \u2514\u2500\u2500 MY_Router.php      # extends CI_Router, resolves module routes\n\u251c\u2500\u2500 third_party\/\n\u2502   \u2514\u2500\u2500 MX\/                # the extension itself\n\u2502       \u251c\u2500\u2500 Base.php       # the CI class \u2014 where the PHP 8.2 notice fires\n\u2502       \u251c\u2500\u2500 Ci.php\n\u2502       \u251c\u2500\u2500 Config.php\n\u2502       \u251c\u2500\u2500 Controller.php # MX_Controller\n\u2502       \u251c\u2500\u2500 Lang.php\n\u2502       \u251c\u2500\u2500 Loader.php\n\u2502       \u251c\u2500\u2500 Model.php      # MX_Model\n\u2502       \u251c\u2500\u2500 Modules.php\n\u2502       \u2514\u2500\u2500 Router.php\n\u2514\u2500\u2500 modules\/               # your modules live here\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The two <code>MY_<\/code> files are the integration point. CodeIgniter 3 automatically loads any class in <code>application\/core\/<\/code> prefixed with the value of <code>$config['subclass_prefix']<\/code> \u2014 <code>MY_<\/code> by default. That is how a third-party library takes over routing and loading without you editing framework files.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If your <code>subclass_prefix<\/code> has been changed, those files must be renamed to match, or the extension silently does nothing and every module route 404s. This is a common cause of &#8220;I installed it and nothing happened.&#8221;<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Module structure<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>application\/modules\/\n\u251c\u2500\u2500 blog\/\n\u2502   \u251c\u2500\u2500 controllers\/\n\u2502   \u2502   \u2514\u2500\u2500 Blog.php\n\u2502   \u251c\u2500\u2500 models\/\n\u2502   \u2502   \u2514\u2500\u2500 Blog_model.php\n\u2502   \u2514\u2500\u2500 views\/\n\u2502       \u2514\u2500\u2500 index.php\n\u2514\u2500\u2500 comments\/\n    \u251c\u2500\u2500 controllers\/\n    \u2502   \u2514\u2500\u2500 Comments.php\n    \u251c\u2500\u2500 models\/\n    \u2502   \u2514\u2500\u2500 Comments_model.php\n    \u2514\u2500\u2500 views\/\n        \u2514\u2500\u2500 widget.php\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Each module mirrors the shape of <code>application\/<\/code> itself. A module can also carry its own <code>config\/<\/code>, <code>helpers\/<\/code>, <code>language\/<\/code>, and <code>libraries\/<\/code> directories, and the loader will find them.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Pointing the loader at your modules<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">If you keep modules somewhere other than <code>application\/modules\/<\/code>, declare it in <code>application\/config\/config.php<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$config&#91;'modules_locations'] = &#91;\n    APPPATH . 'modules\/' =&gt; '..\/modules\/',\n];\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The array key is the absolute path; the value is the path relative to the front controller. Getting the second one wrong produces views that cannot be found while controllers resolve fine \u2014 a confusing failure worth recognising.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Per-module routes and autoloading<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Each module can carry its own <code>config\/routes.php<\/code>, which keeps routing next to the code it routes to:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ application\/modules\/blog\/config\/routes.php\n$route&#91;'blog'] = 'blog\/index';\n$route&#91;'blog\/(:any)'] = 'blog\/view\/$1';\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Module controllers also accept an <code>$autoload<\/code> property, which runs before the constructor:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>class Blog extends MX_Controller\n{\n    public $autoload = &#91;\n        'helper'    =&gt; &#91;'url', 'text'],\n        'libraries' =&gt; &#91;'pagination'],\n        'model'     =&gt; &#91;'blog_model'],\n    ];\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This is per-controller. For module-wide autoloading, use <code>application\/modules\/blog\/config\/autoload.php<\/code> instead \u2014 the two can be combined.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Controllers and models<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Module controllers extend <code>MX_Controller<\/code> rather than <code>CI_Controller<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;?php\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\nclass Blog extends MX_Controller\n{\n    public function __construct()\n    {\n        parent::__construct();\n        $this-&gt;load-&gt;model('blog_model');\n    }\n\n    public function index()\n    {\n        $data&#91;'posts'] = $this-&gt;blog_model-&gt;get_published();\n        $this-&gt;load-&gt;view('index', $data);\n    }\n\n    public function view($slug = null)\n    {\n        $post = $this-&gt;blog_model-&gt;get_by_slug($slug);\n\n        if (empty($post)) {\n            show_404();\n        }\n\n        $this-&gt;load-&gt;view('single', &#91;'post' =&gt; $post]);\n    }\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Two things differ from a standard CI3 controller. <code>$this-&gt;load-&gt;view('index')<\/code> resolves inside the module&#8217;s own <code>views\/<\/code> directory first, so modules do not collide over view names. And <code>parent::__construct()<\/code> is not optional here \u2014 skip it and the loader is never initialised, which surfaces as a null-property error that looks nothing like its cause.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Models extend <code>MX_Model<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;?php\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\nclass Blog_model extends MX_Model\n{\n    public function get_published($limit = 10)\n    {\n        return $this-&gt;db\n            -&gt;where('status', 'published')\n            -&gt;order_by('published_at', 'DESC')\n            -&gt;limit($limit)\n            -&gt;get('posts')\n            -&gt;result();\n    }\n\n    public function get_by_slug($slug)\n    {\n        return $this-&gt;db\n            -&gt;where('slug', $slug)\n            -&gt;get('posts')\n            -&gt;row();\n    }\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If you have seen <code>MY_Controller extends MX_Controller<\/code> in a codebase, that is a project-level base class \u2014 shared authentication, a common layout, that sort of thing \u2014 sitting between the extension and the module controllers. It is a normal pattern, not part of the extension.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Calling One Module From Another<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">This is the part that justifies HMVC. Everything above is directory organisation; this is the actual capability.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><code>Modules::run()<\/code> \u2014 render and output<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ Inside a blog view, embed the comments widget for this post\necho Modules::run('comments\/widget\/render', $post-&gt;id);\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>Modules::run()<\/code> executes the named module&#8217;s controller method and returns its output as a string. Output is buffered, so <code>$this-&gt;load-&gt;view()<\/code> inside the called controller works exactly as it would normally \u2014 you do not need to return anything. The called module loads its own models and renders its own views. The caller knows a route, not an implementation.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The path argument has three forms, and picking the wrong one is the most common reason a call silently returns nothing:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ Module and controller names differ \u2014 you must name the method, even 'index'\nModules::run('comments\/widget\/render', $post-&gt;id);\n\n\/\/ Module and controller names match, method is not 'index'\nModules::run('comments\/render', $post-&gt;id);\n\n\/\/ Module and controller names match and the method is 'index'\nModules::run('comments', $post-&gt;id);\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Parameters after the path are optional and unlimited.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><code>$this-&gt;load-&gt;module()<\/code> \u2014 use it like a library<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>class Blog extends MX_Controller\n{\n    public function view($slug = null)\n    {\n        $this-&gt;load-&gt;module('comments');\n\n        $post = $this-&gt;blog_model-&gt;get_by_slug($slug);\n        $count = $this-&gt;comments-&gt;count_for_post($post-&gt;id);\n    }\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Note what happens here: the loaded module controller becomes a property on the calling controller, so you reach it as <code>$this-&gt;comments<\/code>, not through a return value. It then behaves like a library \u2014 except that it has its own models and libraries, loaded independently of the caller. If the controller name matches the module name you can pass just the module name; otherwise use <code>module\/controller<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Use <code>Modules::run()<\/code> when you want rendered markup to drop into a template. Use <code>$this-&gt;load-&gt;module()<\/code> when you want to call methods and work with what they return.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">One caution learned the hard way on codebases like this: <code>Modules::run()<\/code> inside a loop is a performance trap. Each call is a full controller instantiation with its own model loading and query execution. Rendering a widget once per row across a fifty-row table means fifty controller boots. If a page is inexplicably slow in an HMVC application, count the <code>Modules::run()<\/code> calls in the view before you look anywhere else.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Does CodeIgniter 4 Support HMVC?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Short answer: not HMVC as such, and the wiredesignz extension does not work on it. But CodeIgniter 4 solves the same problem a different way, and the replacement is better than what it replaces.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">What changed<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">CodeIgniter 4 ships a PSR-4 autoloader and namespaced classes. There is no <code>MY_Loader<\/code>, no <code>subclass_prefix<\/code> magic, no <code>MX_Controller<\/code>. Modular Extensions \u2013 HMVC hooks into machinery that no longer exists, so there is nothing to install and no port to wait for.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">What CodeIgniter 4 has instead is <strong><a href=\"https:\/\/codeigniter.com\/user_guide\/general\/modules.html\">Code Modules<\/a><\/strong> \u2014 a first-class feature in the official documentation. There is no enforced module structure or hierarchical directory scan. Instead, you register a namespace and the framework finds your code through it.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Setting up a module<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Create the directory anywhere you like \u2014 alongside <code>app\/<\/code>, or inside it:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>project\/\n\u251c\u2500\u2500 app\/\n\u251c\u2500\u2500 modules\/\n\u2502   \u2514\u2500\u2500 Blog\/\n\u2502       \u251c\u2500\u2500 Config\/\n\u2502       \u2502   \u2514\u2500\u2500 Routes.php\n\u2502       \u251c\u2500\u2500 Controllers\/\n\u2502       \u2502   \u2514\u2500\u2500 Blog.php\n\u2502       \u251c\u2500\u2500 Models\/\n\u2502       \u2502   \u2514\u2500\u2500 BlogModel.php\n\u2502       \u2514\u2500\u2500 Views\/\n\u2502           \u2514\u2500\u2500 index.php\n\u251c\u2500\u2500 public\/\n\u2514\u2500\u2500 system\/\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Register the namespace in <code>app\/Config\/Autoload.php<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>public $psr4 = &#91;\n    APP_NAMESPACE =&gt; APPPATH,\n    'Modules\\Blog' =&gt; ROOTPATH . 'modules\/Blog',\n];\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then the controller is an ordinary namespaced class:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;?php\n\nnamespace Modules\\Blog\\Controllers;\n\nuse CodeIgniter\\Controller;\nuse Modules\\Blog\\Models\\BlogModel;\n\nclass Blog extends Controller\n{\n    public function index()\n    {\n        $model = new BlogModel();\n\n        return view('Modules\\Blog\\Views\\index', &#91;\n            'posts' =&gt; $model-&gt;getPublished(),\n        ]);\n    }\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">And routes point at the namespace:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$routes-&gt;group('blog', &#91;'namespace' =&gt; 'Modules\\Blog\\Controllers'], static function ($routes) {\n    $routes-&gt;get('\/', 'Blog::index');\n    $routes-&gt;get('(:segment)', 'Blog::view\/$1');\n});\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">What you gain and what you give up<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Gained:<\/strong> standard PHP. Namespaces and PSR-4 are language features, not a patched loader. Composer can install a module as a package. Your IDE resolves classes properly. No third-party extension sits between you and the framework, which means no PHP 8.2 patch to maintain.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Given up:<\/strong> the automatic hierarchy. There is no direct <code>Modules::run()<\/code> equivalent \u2014 no built-in &#8220;execute that module&#8217;s controller and hand me the HTML.&#8221; For view composition in CodeIgniter 4 you use view cells, which call a class method and return output, and which are honestly a cleaner mechanism for the widget case than a nested controller call ever was.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If the honest answer you were looking for is whether you can lift a CI3 HMVC application onto CI4 unchanged: no. The module <em>concept<\/em> survives the move. The code does not.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Should You Stay on CodeIgniter 3 HMVC?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">We do legacy CodeIgniter work, so treat what follows accordingly \u2014 but the answer is genuinely &#8220;it depends,&#8221; and the deciding factors are not the ones people usually reach for.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Staying is defensible when:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The application is stable and feature-frozen. Something that runs an internal process nobody is changing does not need a modern framework; it needs a working one.<\/li>\n\n\n\n<li>It is not internet-facing, or sits behind enough network controls that an unpatched framework is not your top risk.<\/li>\n\n\n\n<li>The remaining lifespan is short. Migrating an application you plan to retire in eighteen months is rarely worth it.<\/li>\n\n\n\n<li>A few <code>#[AllowDynamicProperties]<\/code> attributes genuinely are the whole problem, and you have run the app end to end to confirm that.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Migrating starts paying when:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The application is public and handles anything sensitive. You are running a framework in security-patch-only mode on a PHP version it never officially supported. Both halves of that are your problem now, and <a href=\"\/blog\/data-security-best-practices\/\" data-type=\"link\" data-id=\"\/blog\/data-security-best-practices\/\">the security basics<\/a> are harder to hold when the framework underneath is not being patched.<\/li>\n\n\n\n<li>You are still adding features. Every new feature written into CI3 HMVC is more to migrate later, at a worse exchange rate.<\/li>\n\n\n\n<li>Hiring is getting hard. Developers who know CodeIgniter 3 <em>and<\/em> Modular Extensions \u2013 HMVC specifically are a shrinking pool, and there is no official documentation to onboard them with \u2014 HMVC was never a CodeIgniter feature, and the extension&#8217;s own repository has been effectively frozen since Bitbucket dropped Mercurial. Bringing in <a href=\"\/hire-php-developers\/\" data-type=\"link\" data-id=\"\/hire-php-developers\/\">PHP developers<\/a> who have not seen this stack before costs real onboarding time.<\/li>\n\n\n\n<li>The compatibility patching has started spreading. One attribute in <code>MX\/Base.php<\/code> is a fix. Fifteen scattered across <code>third_party\/<\/code> and <code>system\/core\/<\/code> is a fork you did not mean to create.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The middle path most teams actually take:<\/strong> patch the PHP 8.2 notices this week so the logs are clean and the pressure is off, then plan <a href=\"\/digital-transformation\/\" data-type=\"link\" data-id=\"\/digital-transformation\/\">the modernisation<\/a> properly over a quarter or two \u2014 module by module, behind a router that sends some paths to the old application and some to the new one. Modules make this less painful than it sounds, which is one of the few places where an HMVC structure genuinely pays back at the end of its life rather than the beginning.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The move does not have to be to CodeIgniter 4. If you are rewriting substantial parts anyway, <a href=\"\/technologies\/laravel\/\" data-type=\"link\" data-id=\"\/technologies\/laravel\/\">Laravel<\/a> is worth comparing \u2014 it has no HMVC extension either, and does not need one, since service providers and namespaced packages cover the same ground. We have written a fuller <a href=\"\/blog\/codeigniter-vs-laravel-which-is-best-qalbit\/\" data-type=\"link\" data-id=\"\/blog\/codeigniter-vs-laravel-which-is-best-qalbit\/\">CodeIgniter versus Laravel comparison<\/a> if that is the decision in front of you. For the mechanical side of a CI3-to-Laravel move, the open-source <a href=\"https:\/\/github.com\/OpenSID\/legacy-to-laravel\">legacy-to-laravel<\/a> project provides compatibility shims for common CodeIgniter 3 patterns, which can shorten the first phase considerably.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">If you came here with a log full of <code>Creation of dynamic property CI::$load is deprecated<\/code>, the fix is one attribute in <code>third_party\/MX\/Base.php<\/code>, and it will take you about five minutes. Do that first.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Then decide what you are actually doing with the application. Patching PHP compatibility into CodeIgniter 3 works, and it will keep working for a while, but each release makes it a slightly worse deal. CodeIgniter 4 has no HMVC extension and does not need one \u2014 namespaced Code Modules do the job with plain PHP and nothing sitting between you and the framework.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">We <a href=\"\/technologies\/codeigniter\/\" data-type=\"link\" data-id=\"\/technologies\/codeigniter\/\">stabilise, patch, and migrate legacy CodeIgniter applications<\/a>, including CodeIgniter 3 HMVC codebases where the original developers are long gone. If you want a straight assessment of whether yours is worth migrating or worth leaving alone, <a href=\"\/contact-us\/\" data-type=\"link\" data-id=\"\/contact-us\/\">get in touch<\/a> \u2014 or run the numbers yourself with our <a href=\"\/tools\/software-development-cost-calculator\/\" data-type=\"link\" data-id=\"\/tools\/software-development-cost-calculator\/\">development cost calculator<\/a> first.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"\/contact-us\/\"><img decoding=\"async\" width=\"1024\" height=\"338\" src=\"https:\/\/qalbit.com\/blog\/wp-content\/uploads\/2023\/04\/contact-us-codeigniter-hmvc-1024x338.jpg\" alt=\"Contact Our CodeIgniter Experts Now!\" class=\"wp-image-2559\" srcset=\"https:\/\/qalbit.com\/blog\/wp-content\/uploads\/2023\/04\/contact-us-codeigniter-hmvc-1024x338.jpg 1024w, https:\/\/qalbit.com\/blog\/wp-content\/uploads\/2023\/04\/contact-us-codeigniter-hmvc-300x99.jpg 300w, https:\/\/qalbit.com\/blog\/wp-content\/uploads\/2023\/04\/contact-us-codeigniter-hmvc-768x253.jpg 768w, https:\/\/qalbit.com\/blog\/wp-content\/uploads\/2023\/04\/contact-us-codeigniter-hmvc-1536x506.jpg 1536w, https:\/\/qalbit.com\/blog\/wp-content\/uploads\/2023\/04\/contact-us-codeigniter-hmvc.jpg 1820w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n","protected":false},"excerpt":{"rendered":"<p>Web development is a rapidly growing field, and developers are always looking for new ways to make their workflow more efficient and organized. One framework that has gained popularity in recent years is CodeIgniter HMVC (Hierarchical Model-View-Controller).<\/p>\n","protected":false},"author":1,"featured_media":3466,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[415],"tags":[62,122],"class_list":["post-1409","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-web-development","tag-codeigniter","tag-hmvc"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>CodeIgniter HMVC: Fix the CI::$load Deprecated Error<\/title>\n<meta name=\"description\" content=\"Fix &quot;Creation of dynamic property CI::$load is deprecated&quot; on PHP 8.2, plus HMVC module setup and what CodeIgniter 4 offers instead.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/qalbit.com\/blog\/simplifying-web-development-with-codeigniter-hmvc-architecture\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"CodeIgniter 3 supports PHP up to 8.1. PHP 8.1 died in December.\" \/>\n<meta property=\"og:description\" content=\"CodeIgniter 3 officially supports PHP 8.1 at most. PHP 8.1 lost security support in December 2025. Every patched PHP version is unsupported by your framework. Here is the fix, and the decision behind it.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/qalbit.com\/blog\/simplifying-web-development-with-codeigniter-hmvc-architecture\/\" \/>\n<meta property=\"og:site_name\" content=\"QalbIT\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/qalbit.sol\/\" \/>\n<meta property=\"article:published_time\" content=\"2023-04-04T12:00:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-07-30T09:56:43+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/qalbit.com\/blog\/wp-content\/uploads\/2023\/04\/codeigniter-hmvc-php-82-deprecation-fix-scaled.webp\" \/>\n\t<meta property=\"og:image:width\" content=\"2560\" \/>\n\t<meta property=\"og:image:height\" content=\"1440\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/webp\" \/>\n<meta name=\"author\" content=\"Abidhusain Chidi\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:title\" content=\"CodeIgniter 3 supports PHP up to 8.1. PHP 8.1 died in December.\" \/>\n<meta name=\"twitter:description\" content=\"CodeIgniter 3 officially supports PHP 8.1 at most. PHP 8.1 lost security support in December 2025. Every patched PHP version is unsupported by your framework. Here is the fix, and the decision behind it.\" \/>\n<meta name=\"twitter:creator\" content=\"@qalb_it\" \/>\n<meta name=\"twitter:site\" content=\"@qalb_it\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Abidhusain Chidi\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"15 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/qalbit.com\\\/blog\\\/simplifying-web-development-with-codeigniter-hmvc-architecture\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/qalbit.com\\\/blog\\\/simplifying-web-development-with-codeigniter-hmvc-architecture\\\/\"},\"author\":{\"name\":\"Abidhusain Chidi\",\"@id\":\"https:\\\/\\\/qalbit.com\\\/blog\\\/#\\\/schema\\\/person\\\/459440a31e21814bc9603465945ed83e\"},\"headline\":\"CodeIgniter HMVC: Modules, PHP 8.2 Fixes, and the CodeIgniter 4 Question\",\"datePublished\":\"2023-04-04T12:00:00+00:00\",\"dateModified\":\"2026-07-30T09:56:43+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/qalbit.com\\\/blog\\\/simplifying-web-development-with-codeigniter-hmvc-architecture\\\/\"},\"wordCount\":3158,\"publisher\":{\"@id\":\"https:\\\/\\\/qalbit.com\\\/blog\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/qalbit.com\\\/blog\\\/simplifying-web-development-with-codeigniter-hmvc-architecture\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/qalbit.com\\\/blog\\\/wp-content\\\/uploads\\\/2023\\\/04\\\/codeigniter-hmvc-php-82-deprecation-fix-scaled.webp\",\"keywords\":[\"codeigniter\",\"HMVC\"],\"articleSection\":[\"Web Development\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/qalbit.com\\\/blog\\\/simplifying-web-development-with-codeigniter-hmvc-architecture\\\/\",\"url\":\"https:\\\/\\\/qalbit.com\\\/blog\\\/simplifying-web-development-with-codeigniter-hmvc-architecture\\\/\",\"name\":\"CodeIgniter HMVC: Fix the CI::$load Deprecated Error\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/qalbit.com\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/qalbit.com\\\/blog\\\/simplifying-web-development-with-codeigniter-hmvc-architecture\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/qalbit.com\\\/blog\\\/simplifying-web-development-with-codeigniter-hmvc-architecture\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/qalbit.com\\\/blog\\\/wp-content\\\/uploads\\\/2023\\\/04\\\/codeigniter-hmvc-php-82-deprecation-fix-scaled.webp\",\"datePublished\":\"2023-04-04T12:00:00+00:00\",\"dateModified\":\"2026-07-30T09:56:43+00:00\",\"description\":\"Fix \\\"Creation of dynamic property CI::$load is deprecated\\\" on PHP 8.2, plus HMVC module setup and what CodeIgniter 4 offers instead.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/qalbit.com\\\/blog\\\/simplifying-web-development-with-codeigniter-hmvc-architecture\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/qalbit.com\\\/blog\\\/simplifying-web-development-with-codeigniter-hmvc-architecture\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/qalbit.com\\\/blog\\\/simplifying-web-development-with-codeigniter-hmvc-architecture\\\/#primaryimage\",\"url\":\"https:\\\/\\\/qalbit.com\\\/blog\\\/wp-content\\\/uploads\\\/2023\\\/04\\\/codeigniter-hmvc-php-82-deprecation-fix-scaled.webp\",\"contentUrl\":\"https:\\\/\\\/qalbit.com\\\/blog\\\/wp-content\\\/uploads\\\/2023\\\/04\\\/codeigniter-hmvc-php-82-deprecation-fix-scaled.webp\",\"width\":2560,\"height\":1440,\"caption\":\"Illustration of CodeIgniter HMVC modules feeding into a PHP 8.2 deprecation warning being resolved in a code editor\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/qalbit.com\\\/blog\\\/simplifying-web-development-with-codeigniter-hmvc-architecture\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/qalbit.com\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"CodeIgniter HMVC on PHP 8.2\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/qalbit.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/qalbit.com\\\/blog\\\/\",\"name\":\"QalbIT Blog\",\"description\":\"Complex problem, Simple Solution\",\"publisher\":{\"@id\":\"https:\\\/\\\/qalbit.com\\\/blog\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/qalbit.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/qalbit.com\\\/blog\\\/#organization\",\"name\":\"QalbIT Infotech Pvt Ltd\",\"alternateName\":\"QalbIT\",\"url\":\"https:\\\/\\\/qalbit.com\\\/blog\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/qalbit.com\\\/blog\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/qalbit.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/qalbit-logo-512.png\",\"contentUrl\":\"https:\\\/\\\/qalbit.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/qalbit-logo-512.png\",\"width\":512,\"height\":512,\"caption\":\"QalbIT Infotech Pvt Ltd\"},\"image\":{\"@id\":\"https:\\\/\\\/qalbit.com\\\/blog\\\/#\\\/schema\\\/logo\\\/image\\\/\"},\"sameAs\":[\"https:\\\/\\\/www.facebook.com\\\/qalbit.sol\\\/\",\"https:\\\/\\\/x.com\\\/qalb_it\",\"https:\\\/\\\/www.linkedin.com\\\/company\\\/qalbit\\\/\",\"https:\\\/\\\/www.instagram.com\\\/qalb_it\\\/\"]},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/qalbit.com\\\/blog\\\/#\\\/schema\\\/person\\\/459440a31e21814bc9603465945ed83e\",\"name\":\"Abidhusain Chidi\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/qalbit.com\\\/blog\\\/wp-content\\\/litespeed\\\/avatar\\\/41af0dceb95a80e1573c1834535ff9cd.jpg?ver=1786998901\",\"url\":\"https:\\\/\\\/qalbit.com\\\/blog\\\/wp-content\\\/litespeed\\\/avatar\\\/41af0dceb95a80e1573c1834535ff9cd.jpg?ver=1786998901\",\"contentUrl\":\"https:\\\/\\\/qalbit.com\\\/blog\\\/wp-content\\\/litespeed\\\/avatar\\\/41af0dceb95a80e1573c1834535ff9cd.jpg?ver=1786998901\",\"caption\":\"Abidhusain Chidi\"},\"description\":\"Leading QalbIT Infotech Pvt Ltd, he brings over a decade of expertise in web, mobile, and cloud technologies, driving digital success for startups and businesses. His strategic approach to SaaS, PaaS, and BaaS solutions delivers innovative, scalable results tailored to client needs.\",\"sameAs\":[\"https:\\\/\\\/qalbit.com\\\/qalbit\\\/blog\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"CodeIgniter HMVC: Fix the CI::$load Deprecated Error","description":"Fix \"Creation of dynamic property CI::$load is deprecated\" on PHP 8.2, plus HMVC module setup and what CodeIgniter 4 offers instead.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/qalbit.com\/blog\/simplifying-web-development-with-codeigniter-hmvc-architecture\/","og_locale":"en_US","og_type":"article","og_title":"CodeIgniter 3 supports PHP up to 8.1. PHP 8.1 died in December.","og_description":"CodeIgniter 3 officially supports PHP 8.1 at most. PHP 8.1 lost security support in December 2025. Every patched PHP version is unsupported by your framework. Here is the fix, and the decision behind it.","og_url":"https:\/\/qalbit.com\/blog\/simplifying-web-development-with-codeigniter-hmvc-architecture\/","og_site_name":"QalbIT","article_publisher":"https:\/\/www.facebook.com\/qalbit.sol\/","article_published_time":"2023-04-04T12:00:00+00:00","article_modified_time":"2026-07-30T09:56:43+00:00","og_image":[{"width":2560,"height":1440,"url":"https:\/\/qalbit.com\/blog\/wp-content\/uploads\/2023\/04\/codeigniter-hmvc-php-82-deprecation-fix-scaled.webp","type":"image\/webp"}],"author":"Abidhusain Chidi","twitter_card":"summary_large_image","twitter_title":"CodeIgniter 3 supports PHP up to 8.1. PHP 8.1 died in December.","twitter_description":"CodeIgniter 3 officially supports PHP 8.1 at most. PHP 8.1 lost security support in December 2025. Every patched PHP version is unsupported by your framework. Here is the fix, and the decision behind it.","twitter_creator":"@qalb_it","twitter_site":"@qalb_it","twitter_misc":{"Written by":"Abidhusain Chidi","Est. reading time":"15 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/qalbit.com\/blog\/simplifying-web-development-with-codeigniter-hmvc-architecture\/#article","isPartOf":{"@id":"https:\/\/qalbit.com\/blog\/simplifying-web-development-with-codeigniter-hmvc-architecture\/"},"author":{"name":"Abidhusain Chidi","@id":"https:\/\/qalbit.com\/blog\/#\/schema\/person\/459440a31e21814bc9603465945ed83e"},"headline":"CodeIgniter HMVC: Modules, PHP 8.2 Fixes, and the CodeIgniter 4 Question","datePublished":"2023-04-04T12:00:00+00:00","dateModified":"2026-07-30T09:56:43+00:00","mainEntityOfPage":{"@id":"https:\/\/qalbit.com\/blog\/simplifying-web-development-with-codeigniter-hmvc-architecture\/"},"wordCount":3158,"publisher":{"@id":"https:\/\/qalbit.com\/blog\/#organization"},"image":{"@id":"https:\/\/qalbit.com\/blog\/simplifying-web-development-with-codeigniter-hmvc-architecture\/#primaryimage"},"thumbnailUrl":"https:\/\/qalbit.com\/blog\/wp-content\/uploads\/2023\/04\/codeigniter-hmvc-php-82-deprecation-fix-scaled.webp","keywords":["codeigniter","HMVC"],"articleSection":["Web Development"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/qalbit.com\/blog\/simplifying-web-development-with-codeigniter-hmvc-architecture\/","url":"https:\/\/qalbit.com\/blog\/simplifying-web-development-with-codeigniter-hmvc-architecture\/","name":"CodeIgniter HMVC: Fix the CI::$load Deprecated Error","isPartOf":{"@id":"https:\/\/qalbit.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/qalbit.com\/blog\/simplifying-web-development-with-codeigniter-hmvc-architecture\/#primaryimage"},"image":{"@id":"https:\/\/qalbit.com\/blog\/simplifying-web-development-with-codeigniter-hmvc-architecture\/#primaryimage"},"thumbnailUrl":"https:\/\/qalbit.com\/blog\/wp-content\/uploads\/2023\/04\/codeigniter-hmvc-php-82-deprecation-fix-scaled.webp","datePublished":"2023-04-04T12:00:00+00:00","dateModified":"2026-07-30T09:56:43+00:00","description":"Fix \"Creation of dynamic property CI::$load is deprecated\" on PHP 8.2, plus HMVC module setup and what CodeIgniter 4 offers instead.","breadcrumb":{"@id":"https:\/\/qalbit.com\/blog\/simplifying-web-development-with-codeigniter-hmvc-architecture\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/qalbit.com\/blog\/simplifying-web-development-with-codeigniter-hmvc-architecture\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/qalbit.com\/blog\/simplifying-web-development-with-codeigniter-hmvc-architecture\/#primaryimage","url":"https:\/\/qalbit.com\/blog\/wp-content\/uploads\/2023\/04\/codeigniter-hmvc-php-82-deprecation-fix-scaled.webp","contentUrl":"https:\/\/qalbit.com\/blog\/wp-content\/uploads\/2023\/04\/codeigniter-hmvc-php-82-deprecation-fix-scaled.webp","width":2560,"height":1440,"caption":"Illustration of CodeIgniter HMVC modules feeding into a PHP 8.2 deprecation warning being resolved in a code editor"},{"@type":"BreadcrumbList","@id":"https:\/\/qalbit.com\/blog\/simplifying-web-development-with-codeigniter-hmvc-architecture\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/qalbit.com\/blog\/"},{"@type":"ListItem","position":2,"name":"CodeIgniter HMVC on PHP 8.2"}]},{"@type":"WebSite","@id":"https:\/\/qalbit.com\/blog\/#website","url":"https:\/\/qalbit.com\/blog\/","name":"QalbIT Blog","description":"Complex problem, Simple Solution","publisher":{"@id":"https:\/\/qalbit.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/qalbit.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/qalbit.com\/blog\/#organization","name":"QalbIT Infotech Pvt Ltd","alternateName":"QalbIT","url":"https:\/\/qalbit.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/qalbit.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/qalbit.com\/blog\/wp-content\/uploads\/2026\/07\/qalbit-logo-512.png","contentUrl":"https:\/\/qalbit.com\/blog\/wp-content\/uploads\/2026\/07\/qalbit-logo-512.png","width":512,"height":512,"caption":"QalbIT Infotech Pvt Ltd"},"image":{"@id":"https:\/\/qalbit.com\/blog\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/qalbit.sol\/","https:\/\/x.com\/qalb_it","https:\/\/www.linkedin.com\/company\/qalbit\/","https:\/\/www.instagram.com\/qalb_it\/"]},{"@type":"Person","@id":"https:\/\/qalbit.com\/blog\/#\/schema\/person\/459440a31e21814bc9603465945ed83e","name":"Abidhusain Chidi","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/qalbit.com\/blog\/wp-content\/litespeed\/avatar\/41af0dceb95a80e1573c1834535ff9cd.jpg?ver=1786998901","url":"https:\/\/qalbit.com\/blog\/wp-content\/litespeed\/avatar\/41af0dceb95a80e1573c1834535ff9cd.jpg?ver=1786998901","contentUrl":"https:\/\/qalbit.com\/blog\/wp-content\/litespeed\/avatar\/41af0dceb95a80e1573c1834535ff9cd.jpg?ver=1786998901","caption":"Abidhusain Chidi"},"description":"Leading QalbIT Infotech Pvt Ltd, he brings over a decade of expertise in web, mobile, and cloud technologies, driving digital success for startups and businesses. His strategic approach to SaaS, PaaS, and BaaS solutions delivers innovative, scalable results tailored to client needs.","sameAs":["https:\/\/qalbit.com\/qalbit\/blog"]}]}},"featured_image_url":"https:\/\/qalbit.com\/blog\/wp-content\/uploads\/2023\/04\/codeigniter-hmvc-php-82-deprecation-fix-scaled.webp","author_name":"Abidhusain Chidi","author_image_url":"https:\/\/qalbit.com\/blog\/wp-content\/uploads\/2024\/09\/abidhusain-ceo-150x150.png","author_position":"","_links":{"self":[{"href":"https:\/\/qalbit.com\/blog\/wp-json\/wp\/v2\/posts\/1409","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/qalbit.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/qalbit.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/qalbit.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/qalbit.com\/blog\/wp-json\/wp\/v2\/comments?post=1409"}],"version-history":[{"count":3,"href":"https:\/\/qalbit.com\/blog\/wp-json\/wp\/v2\/posts\/1409\/revisions"}],"predecessor-version":[{"id":3467,"href":"https:\/\/qalbit.com\/blog\/wp-json\/wp\/v2\/posts\/1409\/revisions\/3467"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/qalbit.com\/blog\/wp-json\/wp\/v2\/media\/3466"}],"wp:attachment":[{"href":"https:\/\/qalbit.com\/blog\/wp-json\/wp\/v2\/media?parent=1409"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/qalbit.com\/blog\/wp-json\/wp\/v2\/categories?post=1409"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/qalbit.com\/blog\/wp-json\/wp\/v2\/tags?post=1409"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}