Jump to content
Matt
 Share


IPB 3: Creating a new framework using PHP 5

One of the biggest discussions we had during Invision Power Board 3.0's planning was whether or not to drop support for PHP 4 and require a minimum of PHP 5. The advantages of using only PHP 5 were numerous and we really felt like we could increase security and efficiency by taking advantage of the new PHP 5 features. This decision became much easier when we learned that PHP 4 was no longer being developed.

To really see the benefit of using PHP 5, one must first consider how Invision Power Board's new framework is made possible by PHP 5.

Although Invision Power Board 1 and 2 were loosely based on the 'front controller' design pattern, it had no real framework to hang the code on. The closest it had to one was the 'ipsclass' super-class.

'ipsclass' was a convenient method of transporting various classes and functions around Invision Power Board. Convenient, but not ideal. One had to pass this 'super-class' from class to class forcing PHP 4 to use a reference (and being severely punished when forgetting!). This super-class contained almost all the 'core' functionality of Invision Power Board. Member, input and database objects were attached along with numerous other classes and functions. None of which was ordered in any logical format.

We have recoded Invision Power Board 3.0's framework from the ground up. We have done away with the 'ipsclass' super-class and employed the 'Controller -> Command -> View' pattern. This allows us to quickly add new code and to allow fast refactoring of our existing code. This pattern is built upon the 'IPS Registry'. This is a singleton class which maintains interfaces to several other registry objects (database, request, settings and member). Each of these objects maintains a clear place within the registry. This allows us to pass core data through the different levels of our pattern. Other functions from 'ipsclass' are moved into singtons: "IPSLib"; disparate functions that do not belong elsewhere, "IPSText"; functions for parsing and cleaning text, "IPSCookie"; functions to handle cookie management and "IPSMember"; functions that deal with loading, saving and parsing members. This offers a clear structure with clear boundries for each singleton class. Being singletons, you do not need to pass or reference the class in other files.

Here's an example:

IPB 2.3 Code

$value = $this->ipsclass->settings['board_name']$id    = $this->ipsclass->member['id'];$this->ipsclass->input['f'] = 2;print $this->ipsclass->get_cookie('foo');$text = $this->ipsclass->txt_alphanumerical_clean( $text );print $this->ipsclass->class_forums->build_info();

print $this->ipsclass->input['name'];








IPB 3.0 Code

$value = $this->settings->getSetting('board_name');$id = $this->member->getProperty('member_id');$this->request->setField( 'f', 2 );print IPSCookie::get('foo');$text = IPSText::alphanumerical_clean( $text );print $this->registry->getClass('class_forums')->build_info();

print $this->request->getField('name');








It's worth noting that we have also applied the ArrayAccess interface to the registry, so you may access them like so:

$this->settings['board_name'];

print $this->request['name'];



Although the code examples use $this->request, $this->member, etc, these are set up in a constructor. You would pass the IPS Registry singleton into the class. Here's a typical constructor:

{    $this->registry = $registry;    $this->member   = $registry->member();    $this->request  = $registry->request();    $this->settings = $registry->settings();    $this->DB        = $registry->DB();}

function __construct( ipsRegistry $registry )








You could also access the ipsRegistry class directly, although this is strongly discouraged:

print ipsRegistry::instance()->request()->getField('name');


PHP 5 offers a much better OOP (object orientated programming) environment where references are assigned automatically. You can also chain along functions, which we make great use of. This allows us to do some neat trickery, like so:

IPB 2.3 Code

print $this->ipsclass->compiled_templates['skin_boards']->board_index( $data );

$this->ipsclass->load_template('skin_boards');



IPB 3.0 Code

print $this->registry->getClass('output')->getTemplate('boards')->board_index($data);



You'll note that you no longer have to implicitly load the template anymore. This is handled within the 'getTemplate' function if it's not already loaded. This object is then returned for use to chain onto 'board_index()'. This simple adjustment of code makes for less manual code and less room for error.

We are also making great use of PHP 5 abstract classes and interfaces to define extensible classes. This will make it much easier and clearer for others writing their own additions to Invision Power Board. Having a clear interface to work with will reduce errors in development and formalize how you may access Invision Power Boards class structures.

The 'controller -> command' structure is built so that you may add new modules and sections dynamically without the need to change a single line of code elsewhere in the script. Modification authors can just drop in new folders and Invision Power Board will run them when called correctly via a URL. The controller makes use of variables in a URL and safely loads a command file if a matching command file is located. For example: "appcomponent=core&module=global&section=login" is mapped to "applications/core/modules_public/global/login.php". We make use of the Reflection class functions to ensure that any potential command file is a sub-class of the controller to prevent the risk of tampering.

We've barely scratched the surface, but it's clear that Invision Power Board 3's framework is very powerful and code-efficient. This is only made possible by the advancements in PHP 5 that we've taken full advantage of.

 Share

Comments

Recommended Comments

looks good i've been messing about with different design patterns lately to learn them properly as i'm sort of a do to learn type person. Like the way its all shaping out and using php5 really does improve security with the ability of setting visibility permissions. Also having data such as member data and config data in functions rather that arrays that can just be printed out is a good security improvement and of course means lots of huge arrays aren't being shifted about the files like they do now in the super class. Using instances to initiate classes too is another good addition of php5 design patters rather than having every file auto initiating like ipb currently does. Will keep an eye on this, keep us posted :D.

Link to comment
Share on other sites

Looks great but why not use camelCase all the way (alphanumerical_clean() -> alphanumericalClean(), build_info() -> buildInfo() etc ...) the code will look a lot cleaner when its consistent . Anyway looking forward to the next blog post :)

Link to comment
Share on other sites

The new code style looks really interestnig but I'm wondering how you chain methods from static classes. To chain methods of a instanced class all you have to do is return "$this" but how does this work in a static class since "$this" isnt available?

Link to comment
Share on other sites

On one the one hand this is great news but in the other hand, there will probably be no backwards compatibility for custom IPB components, won't there?

We for example use several IPB components which we implemented for ourselves and they of course use the ipsclass - so upgrading to IPB 3.0 would mean to change the old IPB 2.x components accordingly.
Which for us essentially might mean not being able to upgrade to IPB 3. :(

Unless you plan to preserve backwards compatibility?

Link to comment
Share on other sites

  • Management

[quote name='pxswodniw' date='May 9 2008, 08:58 AM']The new code style looks really interestnig but I'm wondering how you chain methods from static classes. To chain methods of a instanced class all you have to do is return "$this" but how does this work in a static class since "$this" isnt available?

If you're talking about ipsRegistry, then it's a true singleton, so you can use self::instance()

Link to comment
Share on other sites

[quote name='Lindsey_' date='May 9 2008, 10:53 AM'][quote name='Kiltec' date='May 9 2008, 05:21 AM']Unless you plan to preserve backwards compatibility?

You could get someone to upgrade it at a small price


Well, we have implemented rather sophisticated IPB compenents, so I doubt that it would be possible for a "small price".
Or if somebody were to do it for a small price, then I wouldn't really trust him... ;)

Also, the problem is not that we wouldn't be capable of refactoring the components, the problem is that it costs time (and thus money).
It also means that the further development of our site would grind to halt until the compatibility issues would be resolved.

Link to comment
Share on other sites

[quote name='Matt' date='May 9 2008, 12:00 PM'][quote name='pxswodniw' date='May 9 2008, 08:58 AM']The new code style looks really interestnig but I'm wondering how you chain methods from static classes. To chain methods of a instanced class all you have to do is return "$this" but how does this work in a static class since "$this" isnt available?

If you're talking about ipsRegistry, then it's a true singleton, so you can use self::instance()


I was thinking about the getClass method since I thougt it loaded a static class so i was wondering how you chained the methods of a static class. But I guess it returns an instance of the class class you "get".

Link to comment
Share on other sites

  • Management

It does, yes.
    * Function for storing class handles to     * prevent having to re-initialize them constantly    */    static function setClass( $key='', $value='' )    {        self::instance()->checkForInit();                if ( ! $key OR ! $value )        {            throw new Exception( "Missing a key or value" );        }        else if ( ! is_object( $value ) )        {            throw new Exception( "$value is not an object" );        }                self::$classes[ $key ] = $value;    }        /**    * Function for retrieving class handles    */    static function getClass( $key )    {        self::instance()->checkForInit();                    if ( ! is_object( self::$classes[ $key ] ) )        {            throw new Exception( "$key is not an object" );        }        else        {            return self::$classes[ $key ];        }    }

/**


































Link to comment
Share on other sites

Why do you pass an instance of ipsRegistry to the "typical" constructor, can't you just have the constructor get it itself, like so



that is what I do in my framework (which I'm now tempted to post ;))

function __construct()
{
    $this->registry = ipsRegistry::instance();
    $this->member   = $this->registry->member();
    $this->request  = $this->registry->request();
    $this->settings = $this->registry->settings();
    $this->DB        = $this->registry->DB();
}
Link to comment
Share on other sites

  • Management

Simple.

Good programming practice.

One of the main aims for the new framework was to decouple each layer. We didn't really want to the controller/command/view layers to have much in the way of knowledge of the actual registry itself. This is just a good habit to get into.

We could do as you've noted, but really we only want there to be one call to ipsRegistry::instance() or rather as few as possible so that if we decided to extend or change the registry into a real object or whatever, we don't have to go and change hundreds of files afterwards. Not only that, but continually grabbing another copy of the registry for each file is a bit ugly, really.

Our way has the registry threaded through the layers, but it's not implicitly tied into any format other than being called 'ipsRegistry'.

Link to comment
Share on other sites

Will there be an api_topics_and_posts.php or the equivalent? We use a number of its functions in our Task Manager scripts.

Easy enough to edit/replace $ipsclass, but I hope I don't have to write my own replacement for things like $api->create_new_reply()

Link to comment
Share on other sites

[quote name='cnmoore' date='May 11 2008, 07:41 AM']Will there be an api_topics_and_posts.php or the equivalent? We use a number of its functions in our Task Manager scripts.

Easy enough to edit/replace $ipsclass, but I hope I don't have to write my own replacement for things like $api->create_new_reply()

technically speaking depending on how its done it should be possible just to extend/include the post classes and call on existing methods so you wouldn't need a api which would just end up being code duplication. Depending on how it works they might have to make a api if the post class gets too complicated although this shouldn't really be the case.

Link to comment
Share on other sites

[quote name='Jaggi' date='May 12 2008, 02:20 PM']technically speaking depending on how its done it should be possible just to extend/include the post classes and call on existing methods so you wouldn't need a api which would just end up being code duplication. Depending on how it works they might have to make a api if the post class gets too complicated although this shouldn't really be the case.
Thanks for this, really convenient.

Link to comment
Share on other sites



Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Add a comment...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

×
×
  • Create New...