Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>You haven't had many good answers on this. Let's add a lot more detail.</p> <h1>How CodeIgniter works</h1> <h2>URL Routing</h2> <p>CodeIgniter's first step is actually done outside of CodeIgniter itself. You will find this step in your HTTP daemon. If it is Apache, you will see a rewrite rule similar to this:</p> <pre><code> RewriteRule ^(.+)$ /index.php?$1 [L] </code></pre> <p>And if you are running nginx, you will see:</p> <pre><code> rewrite ^/(.+)$ /index.php?/$1 last; </code></pre> <p>This is one of the core directives of every framework, and will cause it to process and parse every single request (with, based on <code>RewriteCond</code>/<code>try_files</code> directives, may include files found or not). The query string provided by the user will be available in <code>$_SERVER['QUERY_STRING']</code>. Keep this in mind, we will come back to this more than once.</p> <h2>Enter the framework</h2> <p>Your framework, if you plan on building one, should feature something called a <strong>Router</strong>. This is a neat little class (or collection of classes) that will handle the dynamic creation of <strong>routes</strong>. Routes are directional mappings between an URI (possibly containing wildcards) and a controller's function (or functions).</p> <p>This is often overlooked by people writing their own frameworks for the first time, and can make the difference between something that works, and something that is such a royal pain to use (the difference in PHP is slim. This really becomes apparent on other technologies).</p> <p>So. A router. Takes a list of routes, converts them to classes and functions. We'll want to cache our controllers and provide the dynamic allocation of them, so we will bring a new concept to the table: <strong>Inversion of Control</strong>. This will be familiar to you if you have worked with more recent frameworks (Kohona, Laravel etc). Effectively, it allows you to bind class names to go-to strings and control the instantiation of such classes through a mediator (thereby following the Hollywood principle - you request the controller, you do not instantiate it).</p> <p>A classical IoC wrapper looks like this:</p> <pre><code> class IoC { private static $cache = array(); private static $singletons = array(); public static function register($name,$className,$isSingleton=false) { if (!isset(self::$cache[$name])) { self::$cache[$name] = array("class" =&gt; $className, "singleton" =&gt; !!$isSingleton); } } public static function instance($name) { if (!isset(self::$cache[$name])) throw new Exception("Class not found in IoC container"); $cN = self::$cache[$name]['class']; if (!empty(self::$cache[$name]['singleton'])) { if (!isset(self::$singletons[$cN])) { self::$singletons[$cN] = new $cN(); } return self::$singletons[$cN]; } else { return new $cN(); } } } </code></pre> <p>This has multiple benefits:</p> <ul> <li>You may very well one day decide to change your controller to <code>Controller2</code> from <code>Controller1</code>. By using the <code>IoC</code> wrapper to resolve it, you're saving yourself some pain - not having to go across your entire code</li> <li>Handles <code>Singletons</code> natively without making them hard to unit-test</li> </ul> <p>So. Strong with our knowledge, we can now instantiate a class based on an arbitrary keyword of our choosing using <code>IoC::instance("ourkeyword");</code>. We're going to be using this quite a bit in a tiny bit. First off, one warning: <strong>this IoC wrapper assumes that you will use spl_autoload to lazy-load your classes</strong>.</p> <p>The router itself will be something along the lines of:</p> <pre><code> class Router { public $routes = array(); public function register($route, $controller, $method, $request_type=7) { $this-&gt;routes[] = array("route" =&gt; $route, "call" =&gt; array($controller, $method), "request_type" =&gt; 7); } public function route($str) { foreach ($this-&gt;routes as $v) { // Do your request_type match here. Exercise left for the reader! if (preg_match($str, $v['route'], $matches)) { try { $controller = IoC::instance($v['call'][0]); if (!method_exists($controller, $v['call'][1])) throw new Exception("Method not found: ".$v['call'][1]); array_shift($matches); call_user_func_array(array($controller,$v['call'][1]), $matches); } catch (Exception $e) { } } } } } </code></pre> <p>That's your router! You may be wondering why <code>IoC</code> is purely a static class, and <code>Router</code> is instantiated. The reason is simple: you may want your modules to have their own sub-router rather than having to always deal with one router. I tend to do that a lot, which allows me to completely compartmentalize my PAC structures and build a logical routing hierarchy.</p> <p>That's all the routing! From the front, your framework code now looks like:</p> <pre><code> IoC::register("controller1","My\\Controller"); $router = new Router(); $router-&gt;register(...); $router-&gt;route($_SERVER['QUERY_STRING']); </code></pre> <p>Nice, neat and abstracting away all the problems! From this, you can build your framework to follow a MVC architecture or any of its variants. This works stupidly well with PAC.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload