Building a Framework using Symfony Components – Part 2
architectureThis is the continuation of a previous post based on Fabien Potencier’s tutorial on creating your own framework on-top of the Symfony 2 components.
In the last lesson, we covered the first 6 parts of Fabien’s excellent tutorial. This time around, we’ll cover off the last 6 parts. So without any further ado…
Part 7 – Implementing the Model View Controller pattern
The first improvement introduced in Part 7 is to utilise namespacing for the Simplex framework. Introduced in PHP 5.3, namespaces allow us to better manage the interoperation of libraries by avoiding naming collisions. When writing object oriented code for PHP 5.3 and above it’s highly recommended that you utilise namespaces as a best practice.
To start off with, we move the Framework class into the Simplex namespace, so its fully qualified name is now Simplex\Framework
:
The next step is to formally create our Model and the primary Controller for the Calendar application. Because these are separate to the framework, we’ll add them to their own namespace, which we’ll call Calendar. For consistency, we can also break these further into Calendar\Controller
and Calendar\Model
to prepare for the growth of our application.
When we’re finished, we have:
In the process of making these namespace declarations, we also need to adjust the file structure of our project so that the autoloader will be able to find the required resources. When we’re finished, what we have should look like the following:
- /vendor : third-party libraries managed by Composer.
- /web : any files that need to be publicly accessible, such as the front-controller, JavaScript, stylesheets and images.
- /src : the files that implement your web application.
- /src/Simplex : files for the Simplex framework.
- /src/Calendar : files for the Calendar namespace.
- /src/Calendar/Controller : the controller code for the leap year web application.
- /src/Calendar/Model : the model(s) for the web application.
Resources: The code, the full lesson
Part 8 – Adding Unit Tests
Now that the framework is starting to get quite large, it’s time to add Unit tests into the mix. This will enable us to continue to extend and develop the Simplex framework with the knowledge that our automated unit tests will prevent us from introducing bugs into our code.
The first thing to do is to change our core framework functions so that they work using “mockable” interfaces instead of concrete class implementations. See the example below:
Assuming we have PHPUnit set up, we can then define our tests in the tests/Simplex/Tests/
folder. An example is shown below:
Resources: The code, the full lesson
Part 9 – Extending the framework using Events
At the moment, if we want to add functionality to the Simplex framework, the only way to do it is to touch the core library code. Wouldn’t it be great if instead of editing the core code we could just “tell” Simplex when we wanted to intervene during the request handling process, perform our desired action(s) and then return execution to the framework? That’s precisely what we can do using the Symfony 2 Event Dispatcher component.
The Event Dispatcher gives us a centralised way to specify functions that are “interested” in certain actions occurring within the application. It’s up to us to register the functions as well as telling the dispatcher when an event has occurred.
For example, we can define a function that gets called when an event named response occurs. The function expects that the HTTP Response object will be provided at runtime, and it will add a HTML comment to the end of the response body when invoked. This would look as such:
Of course this code alone won’t do anything. We need to tell the event dispatcher when the event is occurring, which is shown in the Framework class below:
Resources: The code, the full lesson
Part 10 – Leveraging the HttpKernel Component and Caching
The Symfony HttpKernel provides us with a set of tools for building web based applications. It provides the kernel
of a web framework, including a fully abstracted request-response life cycle, support for routing, lazy-loading of controllers, events and much more. We can leverage these features by adding the HttpKernel component to the project using our composer.json
file and then changing the core Framework class to implement HttpKernelInterface
as depicted below:
As we can see from the interface of the handle(…)
function, the basic purpose of the framework is to handle an incoming HTTP request by generating a HTTP response. It’s hard to see what immediate benefit we get from implementing the HttpKernelInterface
– aren’t we still doing the same thing? Essentially, yes, but because we’re implementing an interface instead of creating our own free-standing class, the internals of the HttpKernel class know how to interact with our framework code.
So if we want to add server-side caching, for example, it’s as easy as this:
Resources: The code, the full lesson
Part 11 – Embracing the HttpKernel
Instead of implementing the HttpKernelInterface
in our framework code, what we can choose to do instead is actually use the concrete implementation of the kernel that comes with the component. This means we can strip out a lot of the code we’ve written to handle servicing the request, but it does mean we need to conform to some conventions in order for pre-rolled HttpKernel to work.
To start off with, we change the core framework file to the following:
Now we just need to modify our front controller to make sure it utilises the HttpKernel according to its interface:
… and that’s it!
As with the caching example, the benefit of doing this isn’t immediately available until we see what kind of additional functionality this gives us access to. For example, this implementation of HttpKernel provides us with events for different actions throughout the request-response life cycle. It also provides base classes for different kinds of handlers that can be used listen for these events. Using these two pieces of information, we can easily create a function that will handle exceptions thrown during the request handling process:
… and then attach it to the dispatcher as such:
Resources: The code, the full lesson
Part 12 – Inversion of Control via Dependency Injection
As software systems grow, certain design patterns than made sense initially can start to be cumbersome, ineffective or unscalable. In a web framework we notice this particularly when deciding how to manage dependencies between different classes and libraries.
We can manage the dependencies in the front controller by creating instances of singleton objects in “global” scope, injecting references via the constructors:
… but this introduces bloat in what should be a very simple file. The front controller should be application specific – it feels like we’re implementing too much of the framework in the front controller. Alternatively, we could move these dependencies into the framework code:
… but this just moves the problem into a different place. It also adds some other problems: if we want to customise events or the routing mechanism, we have to touch the framework code.
A possible solution to both of these problems is to introduce the concept of a dependency container
. When implementing this pattern, we utilise a dedicated object to keep track of the classes that will be used for different contexts (routing, dispatching events) and specify what the dependencies of each of these classes are. Then at runtime instead of directly instantiating classes, we “ask” the container to give us a reference to an instance of the class that we want.
In the code below, we define the context
and matcher
classes, and register matcher
as a dependency of context
:
We can then get access to an instance of the matcher
at runtime using the following:
$sc->get(‘matcher’);
This is the core principle of dependency injection. It’s a fundamental change in the way we write web applications, but it provides a great deal of flexibility.
Resources: The code, the full lesson
Conclusion
This draws us to the end of the our summary of Fabien’s marathon 12 part tutorial on building your own framework. If you’ve stayed with us this long, congratulations! We highly recommend you work through the original exercises. Fabien’s tutorial is one of the best (advanced) PHP tutorials out there, and will definitely help you take your programming and architecture skills to the next level.
Until next time!
Want to find out more?
We've worked with businesses just like yours to execute successful web projects helping them to optimise operations, improve marketing, and sell more online with custom software solutions. Reach out and tell us about your project for a free no-commitment consultation.
Find out more