esoTalk

Blog

An Introduction to esoTalk's Code

16 November 2011

When rewriting esoTalk, I had two goals in mind: to make the code straightforward and easy to understand, and to make the code easily extensible. Achieving these things would encourage developers to contribute and collaborate on the core code, and extend the software by writing plugins and skins.

I'm not a big fan of dependencies. When I make software, I like to have complete control and understanding over everything I'm working with—that's just the way I work as a programmer, I guess. Perhaps it's a flaw. Perhaps not. But for this reason, I chose not to use an existing PHP framework.

Every framework makes assumptions about the applications that will be built upon it, and therefore you're likely to lose a layer of control and efficiency when building upon one. With an application like esoTalk, which has a very specific purpose, I wanted to keep the code focused and concise. Instead of using a framework, I built my own pseudo-framework specifically for esoTalk. I planned everything out in a way I thought was logical, and only included components that esoTalk needs.

I'm pretty happy with the result. While it may not be as robust, powerful, or proven as existing frameworks, it can be and is tailored specifically to esoTalk's needs—and that's an advantage.

Since it might be a little while before proper documentation is out (although it is a high priority!), I thought I'd write a blog post about the basics of the esoTalk "framework". Most of this can be learned by reading inline documentation (in code comments), but I thought it would be good to lay it all out together in a more understandable way.

ET

"ET" is used as a prefix for every class in esoTalk, and obviously stands for "esoTalk".

There is a static class named "ET". This class serves as a central means of access to various services and functions which are needed around the application, including:

  • the database object (ET::$database)
  • the session object (ET::$session)
  • configuration reading & writing (ET::config() and ET::writeConfig())
  • language translation (ET::translate(), ET::define(), ET::loadLanguage())
  • lazy instantiation of various classes (ET::memberModel(), ET::formatter(), etc.)
  • loaded plugins (ET::$plugins)
  • the loaded skin (ET::$skin)
  • event triggering (ET::trigger())
  • error rendering (ET::pageNotFound() and ET::fatalError())

ETFactory

ETFactory is a static class which simply allows classes to be instantiated without having to know what the real name of the class is. For example, the database object could be created using ETFactory::make("database"), whatever class that may refer to, be it the default ETDatabase or some class that a plugin may want to override the default with.

Anatomy of a Request

The only point of entry to esoTalk is index.php. Every request goes through this file. The process that takes place in index.php is as follows:

  1. Set up the environment. Define constants, set up error handling, etc.
  2. Include the configuration files. config.defaults.php is included first, and then anything in config/config.php takes precedence.
  3. Register default classes with ETFactory, including helpers, models, and controllers.
  4. Include plugins.
  5. Instantiate the cache, session, and database objects, and set them as properties of the static ET class.
  6. Include the skin and language.
  7. Parse the request string (The "conversation/lock/123″ part of "http://esotalk.org/forum/conversation/lock/123″):
    • The first part maps to the controller.
    • The second part maps to the controller method, if it exists as a non-inherited public method of the controller class. Otherwise, we 1. default to the "index" method.
    • Any subsequent parts map to arguments which are passed to the controller method.
  8. Dispatch the method and arguments to the controller. For "conversation/lock/123″, we will essentially be calling the "lock" method of 1. the "conversation" controller, passing "123″ as a function argument.

Controllers

A controller takes user input from a request, handles it, and responds by rendering content/data.

Controllers inherit a bunch of methods to do things common to the esoTalk web interface, including methods to:

  • Respond by rendering views or redirecting ($this->render(), $this->redirect())
  • Set data to be passed to a view before it is rendered ($this->data())
  • Add messages to be displayed when the page is rendered ($this->message())
  • Add JS/CSS files to be included on the page ($this->addJSFile(), $this->addCSSFile())

Let's take a look at what a controller method could look like:

public function index($memberId)
{
    // Insert code to get member data for the $memberId.
    // Let's assume that we get this data and set it to $member.

    // Pass along the $member data to the view.
    $this->data("member", $member);

    // Show a message in the bottom-left of the screen, because we can.
    $this->message("Hi, this is a message.", "warning");

    // Render the page, specifying what view we want to use.
    $this->render("member/profile");
}

Views

Views are simply files that output HTML code. Following along from the previous example, our "member/profile" view might look like:

<?php
$member = $data["member"];
?>
<h1><?php echo $member["username"]; ?>'s Profile</h1>

Response Types

The response type specifies the type of content that we want the controller to respond with. The response type can be specified in the request by putting a period following the method name, followed by the response type we want. For example, "conversation/lock.json/123″ would set the controller's response type to "json" and call the "lock" method.

By default, the response type is "default", which instructs the controller's render() method to render the content inside the master view (the HTML wrapper.) Other response types include:

  • "view", which will render just the inner view without the HTML wrapper.
  • "json", which will output a JSON object using data collected with the $this->json() method in a controller.
  • "ajax", which is the same as "json", but will render the inner view into the "view" key of the JSON object.

Models

Models handle data. A model contains methods to read and write data specific to a type of record. For example, some of the methods in the conversation controller are getById, getMembersAllowed, addReply, and setSticky.

Data should almost always be read and written using models. This allows models to enforce data integrity and keeps everything consistent between controllers.

Conclusion

I think that covers all of the basic parts of code that make up esoTalk. Of course, there are other helper classes and functions (to help with building SQL queries, URLs, forms, uploading files, formatting text, and rendering common assets), but I won't go into detail about them right now. All functions and methods are documented, and I've made sure to write plenty of inline comments, so you can discover a lot just by looking through the source code. Don't hesitate to ask questions on the esoTalk support forum!

Again, I'm pretty happy with the code structure of esoTalk. It's simple, efficient, and extensible. But, of course, there's always room for improvement—what are your thoughts, and how could esoTalk's code structure be made better?