The New Myth family: SprintPHP Bonfire Practical CodeIgniter 3

New Myth Media Blog

Serving the New Myth Media Family.

Practical CodeIgniter 3 Released

My new book about making the most of CodeIgniter 3 is out!

Posts Filed Under “Codeigniter”

One of the often-requested features for CodeIgniter is a built-in template engine. For reasons I don't need to get into here, that's not something that will happen. All is not lost, though! The Parser in CI4 has been vastly upgraded since the previous version, and allows near infinite expandability to its feature set through filters and plugins. This post will take advantage of the Plugins feature to show how to implement two of the most important parts of a template engine: extending templates and content blocks within those templates.

NOTE: The example code is not feature complete, doesn’t do any security checks, and might be a bit hacky in places. It’s meant as a proof-of-concept and jumping-off block for you to understand working with plugins.

Initial Setup

Even though this is an example, I want to treat it as much like a real project as possible, so first let’s create a new namespace for the template engine and get our folder structures setup so that we’re ready to go.

I would keep this in its own module that’s easily reusable. We’ll keep things simple and go with Myth\Template as the main namespace, since I use Myth for much of my open-source stuff nowadays. We will keep this in the main directory, right alongside the application and system folders:

/application
/system
/Myth
    /Template

Only thing left to do is to let CodeIgniter know where it’s found, so open application/Config/Autoload.php and the new namespace to the $psr4 array in the class constructor:

`$psr4 = [
    'Config' => APPPATH.'Config',
    'App'  => APPPATH,
    'Myth\Template' => ROOTPATH.'Myth/Template'
];

Registering the Plugins

Every Parser plugin must be registered within Config\View.php in order for the Parser to find the plugins. When writing a larger add-on like this that would eventually combine a number of different plugins, it becomes quite cumbersome to have to enter every plugin for this manually. For example, every feature within the template system, like extend, block, and insert commands, need to be implemented as their own plugin. There is an easier way, though.

A recently added feature allows any config class (that extends from BaseConfig) to support Registrar classes. These classes simply add any number of entries to the config file. A single Registrar class can be used for any number of configuration files, making them ideal for shipping with bundles/modules/packages.

Before we go into the details on how they work, we need to ensure the View config file knows about it. Add the following to application/Config/View.php:

protected $registrars = [
    MythTemplateRegistrar::class
];

All classes listed here will be looped over, calling a static method within it that matches the name of the Config file, without the namespace and extension. In this case, our config file is View.php, so there should be a static method called View(). Create the class now at Myth/Template/Registrar.php and add the following code:

<?php namespace MythTemplate;

class Registrar
{
    public static function View()
    {
        return [
            'plugins' => [
                'extend' => [ function($str, array $params=[]) { return Engine::instance()->extend($str, $params); } ],
                'block'  => [ function($str, array $params=[]) { return Engine::instance()->block($str, $params); } ],
            ]
        ];
    }
}

This registers two new plugins, extend and block. Both of these plugins expect to work with the content between opening and closing tags, so the functions themselves are wrapped within an array. The values of each of these can be any type of valid callable. In this case, we’re using closures that grab an instance of the main template engine class, and run the appropriate method. Nothing too complex here.

The Engine

The template engine itself is a single class file, called Engine. Here’s the skeleton class:

<?php namespace MythTemplate;

use Config\Services;

class Engine
{
    protected static $instance;
    protected $parser;
    protected $blocks = [];

    public function __construct()
    {
        $this->parser = Services::parser();
    }

    public static function instance()
    {
        if (is_null(self::$instance))
        {
            self::$instance = new Engine();
        }

        return self::$instance;
    }

    public function extend(string $body, array $params = []) { }

    public function block(string $body, array $params=[]) { }

}

Before diving into the details of the methods themselves, let’s look at the parts we are working with.

First up is the static $instance class variable. This simply maintains a singleton of the class. Since the blocks and template are both rendered at different times, we need a single place to store the bits and pieces of the final page until we are ready to put it all together. Using a singleton gets us what we need. The instance is retrieved through the instance() method, as seen in the plugin definition.

In the constructor we grab a copy of CodeIgniter’s Parser and store it so we don’t have to keep instantiating it. While it’s done explicitly here, a better solution would be to pass it in through the constructor but that is left up to you to implement.

We have a $blocks array that stores all of the rendered blocks that need to be inserted into the layout file.

And, finally, we have the two methods that we call from the plugin definitions. We’ll look at those now.

{+ extend +}

The purpose of extend is to define which main layout/template the content of this view should be inserted into. So, it should accept a filename as it’s only parameter that we’ll call tpl. Due to the way plugins work if you want to use the content, we must wrap the content in opening and closing tag pairs, so we would end up doing something like this in our views:

{+ extend tpl=master +}
    ... custom content here ...
{+ /extend +}

NOTE: While not implemented just yet, the Parser class will be updated in the future to remove the need to name the parameters, but we’ll show what works currently here.

public function extend(string $body, array $params = [])
{
    if (! array_key_exists('tpl', $params))
    {
        throw new BadMethodCallException('Must provide tpl value to extend a template.');
    }

    // Parse the contents since it will likely be
    // filling blocks inside the template itself.
    $this->parser->renderString($body, null, true);

    // Parse our template last, so that all child
    // blocks will have had time to populate this->blocks.
    $out = $this->parser->render($params['tpl'], null, true);

    return $out;
}

When this plugin is called, $body holds all of the HTML between the opening and closing tags. We first verify that the tpl param exists or throw an exception. Next, we run the body through the parser itself. We have to do this so that any plugins will be ran on that page, the most important for us right now are any calls to {+ block +} which will store the block data locally in the Engine class.

Finally, we render the template itself, which will output the contents of any blocks that have been overridden in our views, merging everything together. The result of that merging is returned, where it gets displayed in the browser.

{+ block +}

The block plugin must operate in two separate ways. First, it must work within the individual views to allow the default contents of the block in the template to be overridden on a per-view basis. Second, it must work within the template files to spit any overridden content out. If no overridden content exists, it should show the default data.

A simple template file might be:

<html>
    <body>
        {+ block name=content +}
            <h1>Default Content\</h1>
        {+ /block +}
    </body>
</html>

And an individual view file might look like:

{+ extend tpl=master +}

    {+ block name=content +}
        <h1>New Content\</h1>
    (+ /block +}

(+ /extend +}

So, let’s see how that could be implemented:

public function block(string $body, array $params=[])
{
    if (! array_key_exists('name', $params))
    {
        throw new BadMethodCallException('Template blocks must have a name');
    }

    $name = $params['name'];

    // If we already have content for that (from a child view)
    // then simply return it.
    if (array_key_exists($name, $this->blocks)) return $this->blocks[$name];

    // Otherwise, save the content locally since we're inserting that
    // into the parent template somewhere.
    $this->blocks[$name] = $body;

    return $body;
}

First, we ensure the name param exists. Now, if we already have the content (which means we’re in a template file) return the existing content. Otherwise, store the body we were given (the part between the opening and closing block tags), and return it. That way, whether we’re in the view file or the template, we will show the right content: either the overridden content or the default.

By no means is this a perfect solution, and it’s only the start of what a full-featured template engine would need, but I hope it helps you understand Parser plugins a bit more, and maybe gets you inspired to band together to create a new template engine that might be adopted by the CI team. :)

One thing the community has wanted has always been a template engine designed specifically for CodeIgniter. After a lot of discussion and back and forth during the planning phase, it was decided that we would not be bundling a template engine in, but we would make it fairly simple to integrate third-party choices. In part this was due to the wide variety of engines already available and preferences of the many developers that use the framework. Being interface-driven, that goal has been met.

However, we also decided to keep the simple Parser that we've always had. One thing that came up, though, was that if we were going to keep it, it needed to be much more useful than it has been in previous versions. Work on this is nearing completion, so I thought I would take a moment to highlight some of the new changes it brings.

The More Things Change

Looping and variable substitution work just like they always have. Variables wrapped in curly braces will be substituted with the variable's value. You can still loop over arrays of data by simply using opening and closing curly braces.

<h1>{blog_title} - {blog_heading}</h1>
{blog_entry}
    <div>
        <h2>{title}</h2>
        <p>{body}{/p}
    </div>
{/blog_entry}

The one major change here, though, is that PHP is no longer executed when parsing views.

The Little Things

Since you can no longer use any PHP, you need more tools to make the templates work for you.

Conditional Logic

Simple conditional logic is now available with the if, elseif, and else tags.

{if role=='admin'}
    <h1>Welcome, Admin</h1>
{elseif role=='moderator'}
    <h1>Welcome, Moderator</h1>
{else}
    <h1>Welcome, User</h1>
{endif}

While they syntax might look a little cleaner, it is treated exactly like a standard PHP conditional, and all standard PHP rules would apply here. You can use any of the comparison operators you would normally, like ==, ===, !==, <, >, etc.

No-Parse Blocks

If you have a section of your template that you don't want the Parser to touch, you can do that easily by wrapping it in noparse tags.

{noparse}
    ... Your un-parsed content lives here ...
{/noparse}

Comments

It's a tiny thing, but sometimes you just have to leave yourself notes in the templates, so it now supports comments that are never displayed in the final HTML.

{# This is a comment #}

Automatic Escaping

All variable substitutions are automatically escaped for safer views, greatly reducing the chance of XSS attacks and more. You can choose a different escaping context (html, attr, css, or js) depending on where the variable is appearing. It defaults to html so most of the time you won't need to do anything special to make it happen.

// Defaults to basic HTML escaping
{ some_var }
// Escape within HTML attributes
{ some_var|esc(attr) }
// Escape within Javascript code
{ some_var|esc(js) }
// Escape within CSS style blocks
{ some_var|esc(css) }

If you don't want it escaped at all, you can use {! and !} braces instead of plain curly braces and that one variable will not be escaped.

{! not_escaped_var !}

This one is still being finalized, so it's possible the syntax might change, but this is the current plan.

The Bigger Things

These next two items bring a fair amount of power to what used to be an almost pointless parser.

Filters

You saw a hint of this above when I was talking about escaping, but the parser now supports filtering the variable to affect its final display. Filters are separated by pipes (|).

// Output: FILTERED
{ filtered | upper }
// Output: Filtered
{ FILTERED | title }

Filters can have one or more parameters to specify options.

// Output: 2017-10-03
{ myDate | date(Y-m-d) }

Multiple filters can be chained together.

// Output: 2018-10-03
{ myDate | date_modify(+1 year) | date(Y-m-d) }

Filters typically only work on strings, but CodeIgniter will still ship with a number of helpful filters. And it's very simple to create your own custom ones. The built-in ones are currently:

  • abs
  • capitalize
  • date
  • date_modify
  • default
  • esc
  • excerpt
  • highlight
  • highlight_code
  • limit_words
  • limit_chars
  • lower
  • nl2br
  • number_format
  • prose
  • round
  • strip_tags
  • title
  • upper

Plugins

Word of Warning: This feature is a concept only at this time, and actually implementation hasn't started, yet, but this is what's kicking around in my head. Syntax likely to change.

Plugins are a nod to Expression Engine's syntax that allows you to extend the capability of the Parser to fit most any need that you might have. Heck, you could probably flesh out any missing features needed for a template engine through plugins.

Basically, plugins allow you to specify an alias tag that a library's features can be called from. They are very similar in concept to View Cells, though I believe they'll be able to do some things that Cells can't. The only plugin expected to ship with the framework is the View Cell feature, actually.

This probably best makes sense with an example. Let's just use the View Cell feature to demonstrate, then. With Cells you can specify a class/method to generate and return HTML that is rendered in the Cell's place. It's tag might look something like this:

// A Simple version
{ cell: Acme\Blog\Posts::recentPosts }
// With Parameters
{ cell: Acme\Blog\Posts::recentPosts category=codeigniter limit=5 }

If the Acme\Blog package wanted to make things a little cleaner and create some View plugins, they might provide something like the following:

{ blog:recentPosts category=codeigniter limit=5 }

Again, all of this plugin syntax is preliminary and subject to change, but it's coming.


Hopefully, this type of Parser can be useful to some of you.

Continuing on from the previous post, this tutorial will look at taking our basic Entities and making the more flexible, and more capable. Again, this isn’t meant to be a full demonstration of the Repository pattern, but simply examining one particular aspect of it.

What was wrong with the previous example? For starters, all of our class properties had to be public to allow it to work with the Model. While that’s not the worst thing in the world, it is definitely not ideal.

Getters and Setters

The first step is to make the class properties protected instead of public. In order to make those visible to the Model, we’ll use the __get() and __set() magic methods to provide access.

public function __get(string $key)
{
    if (isset($this->$key))
    {
        return $this->$key;
    }
}

public function __set(string $key, $value = null)
{
    if (isset($this->$key))
        
    {

        $this->$key = $value;
      
    }
}

This solves our problem, but simply adds extra code between the value and your code, which is good for encapsulation, but we can do better here. There are going to be numerous times that you want to perform some logic whenever we a value on the Entity. For example, you might want to automatically hash a password whenever it’s set. Or you might want to always keep your dates stored as DateTime instances. So, how do we make this simple?

For that, let’s add some new functionality to the setter that allows us to call any method that had set_ and then property name, like set_password.

public function __set(string $key, $value = null)
{
    // if a set* method exists for this key,
       
    // use that method to insert this value.
       
    $method = 'set_'.$key;
     
    if (method_exists($this, $method))
     
    {
          
        $this->$method($value);
        
    }
      
    // A simple insert on existing keys otherwise.
     
    elseif (isset($this->$key))
        
    {
          
        $this->$key = $value;
      
    }

}

Now, you could solve your business needs with simple little functions like these:

protected function set_password(string $value)
{
    $this->password = password_hash($value, PASSWORD_BCRYPT);
}

protected function set_last_login(string $value)
{
    $this->last_login = new DateTime($value);
}

Whenever you set $user->password = ‘abc’, or $user->last_login = ’10-15-2016 3:42pm’ your custom methods will automatically be called, storing the property as your business needs dictate. Let’s do the same thing for the getters.

public function __get(string $key)
 
{
          
    // if a set* method exists for this key,
       
    // use that method to insert this value.
       
    if (method_exists($this, $key))
        
    {
              
        return $this->$key();
      
    }

         
    if (isset($this->$key))
        
    {
          
        return $this->$key;
        
    }
  
}

In this case, we’re simply checking for a method with the exact name as the class property. You can set these methods as public and then it would work the same, no matter whether it was called as a method or a property, $user->last_login or $user->last_login():

public function last_login($format=‘Y-m-d H:i:s’)
{
    return $this->last_login->format($format);
}

By setting a default value for $format, it works either way, but you can now get the value in the format you need it at that time, instead of being restricted to a single format.

Filler

This has already helped our classes to become more capable and flexible, at the same time helping us to maintain our business rules, and still easily be saved to the database and gotten back out again intact. But wouldn’t it be nice if we could just shove an array of key/value pairs at the class and have it fill the properties out automatically, but only work with existing properties? This makes it simple to grab data from $_POST, create a new Entity class, and shove it there before saving. Even better if we can customize data on the way in the same way we did with setters, right? Welcome to the fill() method:

public function fill(array $data)
  
{
          
    foreach ($data as $key => $var)
        
    {
              
        $method = 'fill_'. $key;
           
        if (method_exists($this, $method))
         
        {
              
            $this->$method($var);
          
        }

         
        elseif (property_exists($this, $key))
          
        {
              
            $this->$key = $var;
            
        }
      
    }
  
}

A quick example should make this one make sense. First, let’s grab the POST data, add it to a new User object, and save it to the database:

$data = $_POST;
$user = new App\Entities\User();
$user->fill($data);
$userModel->save($user);

If this were a registration form we were handling, we might be getting apassword field that we wanted to make sure was hashed. So, a quick fill_ method and we’re good to go. For this example, we’ll just re-use the setter we created earlier:

protected function fill_password(string $value)_
{
    $this->set_password($value);
}

The Entity Class

To make this all simple to re-use, we should create a new Entity class that our Entities can extend and get these features automatically. Here’s one such class, that also takes care of our timestamps, including timezone conversions.

<?php namespace Myth;

/**
 * Class Entity
 *
 * A base class for entities that provides some convenience methods
 * that make working with entities a little simpler.
 *
 * @package App\Entities
 */
class Entity
{
    /**
     * Given an array of key/value pairs, will fill in the
     * data on this instance with those values. Only works
     * on fields that exist.
     *
     * @param array $data
     */
    public function fill(array $data)
    {
        foreach ($data as $key => $var)
        {
            $method = 'fill_'. $key;
            if (method_exists($this, $method))
            {
                $this->$method($var);
            }

            elseif (property_exists($this, $key))
            {
                $this->$key = $var;
            }
        }
    }

    //--------------------------------------------------------------------

    //--------------------------------------------------------------------
    // Getters
    //--------------------------------------------------------------------

    /**
     * Returns the created_at field value as a string. If $format is
     * a string it will be used to format the result by. If $format
     * is TRUE, the underlying DateTime option will be returned instead.
     *
     * Either way, the timezone is set to the value of $this->timezone,
     * if set, or to the app's default timezone.
     *
     * @param string $format
     *
     * @return string
     */
    public function created_at($format = 'Y-m-d H:i:s'): string
    {
        $timezone = isset($this->timezone)
            ? $this->timezone
            : app_timezone();

        $this->created_at->setTimezone($timezone);

        return $format === true
            ? $this->created_at
            : $this->created_at->format($format);
    }

    //--------------------------------------------------------------------

    /**
     * Returns the updated_at field value as a string. If $format is
     * a string it will be used to format the result by. If $format
     * is TRUE, the underlying DateTime option will be returned instead.
     *
     * Either way, the timezone is set to the value of $this->timezone,
     * if set, or to the app's default timezone.
     *
     * @param string $format
     *
     * @return string
     */
    public function updated_at($format = 'Y-m-d H:i:s'): string
    {
        $timezone = isset($this->timezone)
            ? $this->timezone
            : app_timezone();

        $this->updated_at->setTimezone($timezone);

        return $format === true
            ? $this->updated_at
            : $this->updated_at->format($format);
    }

    //--------------------------------------------------------------------

    //--------------------------------------------------------------------
    // Setters
    //--------------------------------------------------------------------

    /**
     * Custom value for the `created_at` field used with timestamps.
     *
     * @param string $datetime
     *
     * @return $this
     */
    public function set_created_at(string $datetime)
    {
        $this->created_at = new \DateTime($datetime, new \DateTimeZone('UTC'));

        return $this;
    }

    //--------------------------------------------------------------------

    /**
     * Custom value for the `updated_at` field used with timestamps.
     *
     * @param string $datetime
     *
     * @return $this
     */
    public function set_updated_at(string $datetime)
    {
        $this->updated_at = new \DateTime($datetime, new \DateTimeZone('UTC'));

        return $this;
    }

    //--------------------------------------------------------------------

    //--------------------------------------------------------------------
    // Magic
    //--------------------------------------------------------------------

    /**
     * Allows Models to be able to get any class properties that are
     * stored on this class.
     *
     * For flexibility, child classes can create `get*()` methods
     * that will be used in place of getting the value directly.
     * For example, a `created_at` property would have a `created_at`
     * method.
     *
     * @param string $key
     *
     * @return mixed
     */
    public function __get(string $key)
    {
        // if a set* method exists for this key,
        // use that method to insert this value.
        if (method_exists($this, $key))
        {
            return $this->$key();
        }

        if (isset($this->$key))
        {
            return $this->$key;
        }
    }

    //--------------------------------------------------------------------

    /**
     * Allows Models to be able to set any class properties
     * from the result set.
     *
     * For flexibility, child classes can create `set*()` methods
     * to provide custom setters for keys. For example, a field
     * named `created_at` would have a `set_created_at` method.
     *
     * @param string $key
     * @param null   $value
     */
    public function __set(string $key, $value = null)
    {
        // if a set* method exists for this key,
        // use that method to insert this value.
        $method = 'set_'.$key;
        if (method_exists($this, $method))
        {
            $this->$method($value);
        }

        // A simple insert on existing keys otherwise.
        elseif (isset($this->$key))
        {
            $this->$key = $value;
        }
    }

    //--------------------------------------------------------------------
}

The Repository Design Pattern is very useful in application design. At the core of this, though, is the Entity, a class that simply represents a single object of whatever data you are modeling. This could be a single User, a Comment, an Article, or anything else in your app. The trick is to ensure that’s all it is. It can (and should!) handle some of the business logic, and can include convenience methods to combine data in various ways, or work with other Repositories to get related data. But it shouldn’t have a care in the world about how to persist itself. That’s the Repository’s job.

In today’s tutorial, we’ll skip all of the steps of using a Repository and just look at how to make working with Entity classes as simple as possible, while still being very flexible.

Getting Entities from the Model

The first thing to look at is how do we get this data from the model itself? Lucky for us, CodeIgniter’s Database layer has been able to convert the results from database queries into custom classes since at least version 2. I didn’t know about this until version 3 was about ready to be released, as it wasn’t documented at the time. Doing this is as easy as passing fully-qualified class name as the only parameter to the getResult() method:

$rows = $db->table(‘users’)->limit(20)->get();
$user = $rows->getResult(‘App\Entities\User’);

This would give you an array of User instances. As long as the Entity class has properties that a) match the names of the columns in the database, and b) the model can access those parameters, their values will be filled in and you’ll be able to work with the classes straight-away. Let’s take a look at some very basic versions to see this in action.

The UserModel might look something like this:

<?php namespace App\Models;

use CodeIgniter\Model;

class UserModel extends Model
{
    protected $table = ‘users’;
    protected $returnType = ‘App\Entities\User’;
    protected $useSoftDeletes = false;
    protected $allowedFields = [
        ‘username’, ‘email’, ‘password_hash’
    ];
    protected $useTimestamps = true;
}

Notice the $returnType property? By setting this to the Entity class, all of our results will be returned as instances of App\Entities\User by default. We can always change that at runtime if we need to.

Also, notice the $allowedFields property. This is especially important for a couple of reasons. The first is that the Model class forces you to fill this in if you want to pass it an array of key/value pairs during a save(), insert(), or update() call to help protect against Mass Assignment attacks, or simple human error. But it will also come in handy when we want to save the object back to the database. More on that a little later.

The Entity Class

Now lets look at the simplest version of the Entity class that we can make. In the next blog post we’ll explore a much more powerful version. We would create a new file at /application/Entities/User.php, creating the new Entities folder since it’s not there by default. It might look something like this:

<?php namespace App\Entities;

class User 
{
    public $id;
    public $username;
    public $email;
    public $password_hash;
    public $created_at;
    public $updated_at;

    public function setPassword(string $password)
    {
        $this->password_hash = password_hash($password, PASSWORD_DEFAULT);

        return $this;
    }

    public function createdOn($format = ‘Y-m-d’)
    {
        $date = new DateTime($this->created_at);
        return $date->format($format);
    }
}

As I said, this is an extremely basic version. But it works to demonstrate the usefulness. It contains properties for all of the columns in the database, not just the ones listed in $allowedFields. Since they are all public, they can be accessed from outside the class whenever you need them, whether in a controller, view, or in the model itself when saving. In real apps, we would likely want to make those protected properties to keep things a little safer. We’ll look at that in the next post, and combine it with some powerful convenience methods to really make working with Entities simple and, dare I say it, fun.

This small example, also includes two convenience methods. The first helps ensure a business rule by make a single place that determines how our password is hashed. The second allows you to retrieve a formatted version of the created_at date. Neither of these are ground-breaking. They’re only there to give you some ideas of basic methods you might find helpful.

Saving the Entity

CodeIgniter’s Model class is smart enough to recognize Entity classes whenever you perform a save(), update(), or insert() call, and convert that class to an array of key/value pairs that can be used to create or update the record in the database.

But, how does it know which fields should be allowed to update the database? Remember the $allowedFields array the model has? That’s the key. It uses that list of fields and grabs the values from the Entity class. In our example, it would create an array that looks something like:

[
    ‘username’ => ‘blackjack’,
    ‘email’ => ‘jack.black@example.com’,
    ‘password_hash’ => ‘. . .’
]

Notice that it did not grab the id, created_at, or update_at fields. That’s because the id field is automatically assigned by the database and we shouldn’t be able to change it, and the date fields are managed by the Model class itself, and we don’t want outside classes mucking with the dates.

So, when it comes to saving your data, there’s nothing special to do. Just pass your Entity to the save(), update(), or insert() method, and the Model takes care of the rest.

// Grab a user
$user = $userModel->find(15);

// Update their email
$user->email = ‘blackjack@example.com’;

// Save the user
$userModel->save($user);

Up Next

Hopefully, this gets you interested in exploring this type of pattern with your applications. Even if you don’t do a full-repository pattern, this simple change makes your code much more manageable and can be very powerful.

In the next post, we’ll take it a little farther by hiding those class properties, but still ensuring they’re accessible to the Model during creation and saving. Then we’ll craft a small Entity class that we can base all of our Entities from that provides some magic that allows us to manipulate the data on the way in and out, and even provide a new fill() method that takes an array and populates/changes the class properties. All of this allows for much more freedom, power, and flexibility in your Entity classes.

Now that the ribbon has been taken off of the first semi-release of CodeIgniter 4, people are wondering how they get started with it. A couple of things have changed that make a big impact on getting started when you're expecting it to be just like CI3. This article aims to help you out there.

Download It

There are two different ways you can download it currently, but they're both basically the same thing.

  1. From the terminal type: git clone git@github.com:bcit-ci/CodeIgniter4.git which will pull down the latest version of the develop branch into a new directory called CodeIgniter4.
  2. From GitHub do a straight download by clicking Clone or Download and then selecting Download Zip. Unzip into a new folder when you're done.

Look Around

If you start taking a look around the new code you'll see a slightly different folder layout than you're used to, though the changes are minimal:

/application        - Your app's files go here
/public             - This is the "web root" of your application. index.php is here
/system         - CodeIgniter lives here
/tests              - Holds tests for CodeIgniter and your application
/user_guide_src - The source files for the user guide. Instructions how to build them.
/writable           - A place for any folder that needs to be writable

The two most important right now are the public and the user_guide_src folders. public holds the new web root of the application. Best practices tell us that your application should live outside of the web and not be accessible from a browser. With this layout, only the files in public are available to end-users through the browser. This is in line with every other major framework out there, but is a change from the way that CodeIgniter has always done it.

The user_guide_src folder contains all of the current documentation for the framework. To the best of our knowledge it is completely up to date with the current release, and we plan on keeping it in sync as we go. This will be your best friend, as you explore over the coming days or weeks. While this isn't the generated HTML, all of the files in the source folder inside it are human-readable and laid out similarly to what you're used to in CI3 docs. Take time to read through the new things in here as most of your questions should be answered, and you'll hopefully find some nice surprises lurking in places.

The following pages are good reads to get started with:

Start Playing

In order to start playing around with the new code, you'll need to get it running in a browser. There's a number of ways to do it, but we'll cover two here.

PHP Server

PHP has a built-in web server now. This is the simplest way to get running. Jump to the command line, navigate to the public directory, and type the following:

$ php -S localhost:8000

That's it. Back to your browser, navigate to http://localhost:8000 and you should see the shiny new CI4 welcome page.

Virtual Host

A more permenant solution is to have another web server running locally, like Apache or NGINX, and create a new virtual host for it. This allows you to create a name for the site, like ci4.dev, and use that in your browser. This is especially helpful so that you don't have to worry about RewriteBase commands for Apache config, or any other tricky ways to get past the public folder. When you setup the virtual host, make sure it is pointing to the public folder or it won't work.

Here are some helper guides for those of you using popular AMP stacks locally:

Note that most of these are essentially the same thing, since you're editing raw Apache config files.

Laravel Homestead is another excellent solution for a PHP7 virtual machine running under NGINX.


Lastly, have fun!