My Thoughts on FluxBB 2.0

Howdy. I realise Im new to the project and haven’t contributed a single line of code to FluxBB before, but I would like to help develop 2.0. I’ve read up on the discussions in the development forum and notes from the last developer meeting.

Im a long time Django and Kohana user. Alot of the ideas below you may recognise from Django. I agree with the ideas/implementations/libraries discussed https://docs.google.com/document/d/1ooR0iUdQ3PXigeLv0MT6W0fIMAFsIiRT5rr6zF6YRck/edit?hl=en_GB&authkey=CJCYyqIO&pli=1# .

These are some of my thoughts on the architecture of Flux2.

PHP Version: PHP 5.2.4+

Class Naming & Autoloading

There are several different approaches we could use. I strongly suggest we implement some sort of class autoloader, it makes dealing with plugins a hell of alot easier. For example the controller that manages the viewing of forums, hypothetically located at /Flux/ViewForum.php could:

  1. Have no prefix, ViewForum
  2. Have lowercase f prefix, fViewForum
  3. Follow send style naming, Flux_ViewForum

In this document I refer to everything in Flux_ViewForum style naming for the sake of clarity. I’m not too fussed about which naming method is used.

MVC?

There would be a definite separation of Model, View (Template) and Controller code. Models would be constructed as their own class. For example

class Model_Viewforum

{

        public static function getTopicsBySlug($slug)

        {

                return DB::select(‘*’)->from(‘forums’)->where(‘slug’, ‘=’, $slug);

        }

        public static function create(array $info)

        {

                return DB::insert(‘forums’)->values($info);

        }

}

(I don’t suggest ^^^^^^ be the API for the query builder and models, its just to give you an idea of what I mean).

Query builders rock for database abstraction, I highly suggest Flux2 uses one. Having a model like this keeps our controllers clean (no big 10 line queries) and also allows plugin authors to easily interact with the core system.

Routing

All requests go through the index.php file in the root directory. If mod_rewrite is enabled then we can have URLs without the index.php in it, if not, the system still works. The system would route both URLs below to the same controller.

All main Flux routes would be declared in a single routes.php file. Plugins will be able to specify their own routes in their own /plugin/*name*/routes.php file. A route is an array which contains several bits of information. An example of one is below:

array (    'name' => 'viewTopicsInBoard',

            'route' => '^/forum/(?P<slug>.*)', //could be more strict for a slug

            'controller' => 'Flux_ViewForum',

            'function' => 'index',

            'reverse' => '/forum/{{ slug }}/',

            )

In this example. If the URI matches the regular expression above, then the controller, Flux_ViewForum will be included (autoloaded?), instantiated and the method index called. You’ll notice the regular expression captures some data, named slug. This will be the slug of the board name (in this example). The router automatically recognises that the route is capturing data and it passes that data as an argument to the Flux_ViewForum::index() function.

Controllers

Every major feature/component of FluxBB would have it’s own class definition/controller file. public methods that can be called by the Router must always accept one parameter, an instance of the Request class. They must always return a Response class.

class Flux_ViewForum
{

        public function index(Request $request, $slug)

        {

                $f = Model_ViewForum::getBySlug($slug);

                if($f)

                {

                        return new Response(‘Currently browsing forum with slug ‘ . $slug);

                }

                else

                {

                        return new Http404Response();

                }

                

}

}

Request and Response Objects

Borrowed from Django. They have a good explanation of Request/Response objects (named HttpRequest and HttpResponse), worth a read to see API.

Request and Response objects are very important! All public controller methods must accept an instance of the Request class as its first parameter, and must return a Response class.

In short the Request class is populated with things like GET and POST data, it contains information about the URI. The Response class is the only thing in the entire application that sends headers, echos data and sets cookies.

We can do clever things like inherit from the Response class and override some methods do have easy “custom responses” like Http404Response and JSONResponse.

This may seem like over kill, but setting things up this way gives us a flexible system (as you will see in Middleware below).

Middleware

Middleware is an idea from Django that I am borrowing. They are freaking awesome : ) . At its simplest a middleware is just a PHP class that inherits from a base Middleware abstract class. It can contain a couple of methods, processRequest and processResponse. Middleware can be specified in plugins or in Flux itself.

As shown in the diagram, all middleware processRequest functions are called before the controller is called. If a processRequest returns a Response object, then the Dispatch object won’t bother loading the controller and will return that Response object straight away. If it doesn’t return a Response object, then everything continues as normal. The example below shows how easy it would be to implement something like an admin logger.

class AdminLogger extends Middleware

{

        public function processRequest(Request $request)

        {

                if(substr($request->uri, 0, 5) == ‘/admin/’)

                {

//Someone is browsing in admin, call log function etc.

                }

        }

}

Each processResponse function is called AFTER the controller has been called. As a result it also accepts an instance of Response. processResponse must return an instance of Response, it can be the existing instance modified, or a new one completely. The example below shows how easy it would be to gzip all output, or implement full page caching.

class GzipOutput extends Middleware

{

        public function processResponse(Request $request, Response $response)
        {

                $outputFromController = $response->body;

                $newResponse = new Response();

                $newResponse->body = gzip_some_stuff_up($outputFromController);

                $newResponse->setHeader(‘Content-Encoding: gzip’);

return $newResponse;                

        }

}

Thanks
Logan