In this article, I want to highlight some differences between the two PHP frameworks that have been the most popular ones at the time of writing.
Zend Framework (ZF) is currently a quasi-standard in many PHP companies and Symony2’s popularity is constantly increasing. Symfony2 is pretty new now and many developers are thinking about if and when to make the switch from ZF to Symfony2 which is why I think we should spend time looking at the frameworks‘ differences.
I selected three topics that are implemented differently in these frameworks and will describe how each of them does it.
Project Setup
I took the setup of new projects as one category of comparison because it is an important to make this step as easy as possible for developers that are learning a new framework. In my opinion, the adoption time of a new technology somehow correlates with the easiness of starting a project with using this technology.
Zend Framework Project Setup
To set up a Zend Framework (ZF) project, download the framework’s minimal package and extract it to an empty directory. You will see two new folders: bin
and library
. To initialize the project structure, run the included cli script in the project directory like this:
php bin/zf.php create project .
When you now go to http://<hostname>/
with your web browser, you see a blue welcome page which indicates that the installation was successful. To configure a database connection, you will have to manually edit the file application/configs/application.ini
and add the following lines to the [production]
environment.
After this step, you have a fully functional Zend Framework project which can be configured using different environments. The next step is to implement custom controllers, views and model classes.
Symfony2 Project Setup
Setting up a project with Symfony2 is pretty easy. You start by downloading a copy of the Symfony2 Standard Edition, extract it to an empty directory and can start to configure your project. However your system has to fulfill some requirements which can be checked by running the script check.php
in the app
folder (just run php check.php
in the command line). When all requirements are fulfilled (and you created a virtual host with its DocumentRoot
pointing to the project’s web
directory), you can navigate your browser to http://<hostname>/config.php
and configure the project via a fancy web UI. After checking all requirements again, the Symfony2 sandbox asks you to enter your database configuration which will be written to a configuration file afterwards. The sandbox also includes a sample page where you can see that everything is up and running: http://<hostname>/app_dev.php/demo/hello/World
.
To use the sandbox for a real project, I suggest removing the demo pages visited before. Remove the line registering the demo bundle from the file app/AppKernel.php
and the demo bundle’s router configuration from app/config/routing_dev.yml
. Last you can delete the bundle’s folder src/Acme
.
app/AppKernel.php
app/routing_dev.yml
To be able to define different database connection parameters for the various environments (as with ZF), copy the file app/config/parameters.ini
to (this example uses the environment dev, of course you can to the same for any other environment!) app/config/parameters_dev.ini
and adjust the other config files accordingly:
Project Setup – Conclusion
The initialization process of a new project is very easy with both of the compared frameworks. One benefit of ZF is that it eases the differentiation between different environments when it comes to configuration – all configuration options can appear in one file in multiple configuration environments that inherit default values from other environments in a defined hierarchy. With Symfony2 you have to create a separate config file and include it into your project’s configuration as described above.
Symfony2 on the other hand comes with a nice example bundle that contains a sample configuration, controllers, views and even a very basic authentication infrastructure which can be used as template for your own bundles. Of course, the initial project structure of a ZF project has some default controllers as well, but they don’t show you how to configure e.g. security.
Model View Controller
The Model View Controller (MVC) pattern is one of the basic structural patterns used in application development. It basically applies the concept of Separation of Concerns to the structure of applications in a way that separates business logic (Model), the rendering of user interfaces (View) and user interaction handling (Controller) from each other. Most object-oriented frameworks provide scaffolding for MVC implementations – so do Zend Framework and Symfony2. Let’s have a look how MVC is applied in both cases.
MVC in Zend Framework Projects
In a classical project infrastructure, ZF projects use controllers that contain multiple actions. Every HTTP request sent to the application is being handled by one (in some cases also multiple) controller’s action and most of the times, one action is mapped to one view (This is of course due to the fact that in the WWW, user interaction required a reload of the complete web page – at least for a long time. Nowadays, many web pages (so-called single-page- or Ajax-applications) do not need reloads to change the displayed data and views.).
Views are implemented as templates that contain HTML markup code with inlined PHP snippets. To pass data to a view, the controller has to set this data as a member variable on its view. In the view script you can later access this data by using $this->variable
.
In web development, the term Model Class is often mistaken for Persistent Entity Class. Model objects do not only represent data stored in a database, but should also contain all the business logic of your applications. I have seen many applications that do all the logic inside of controller classes and only use models to have an abstraction over the database.
ZF cannot force you to have model classes for all your logic and it also does not try to do so. What it does is giving you a utility for implementing your database abstraction: Zend_Db
.
Zend_Db
is a component of ZF that provides several APIs to access databases and have an abstraction for persistent records. In times of Doctrine 2, I would suggest neither to learn the use of Zend_Db
nor to implement a custom database abstraction. Doctrine 2 can be integrated into ZF projects very easily as you can look up in my article from December 6th and it probably will do a better and more efficient job than a custom solution, too.
Besides my own affinity for Doctrine 2, there are several impartial reasons for not using Zend_Db
. In my opinion, the biggest disadvantage is that to add custom logic to your entity objects, you end up writing subclasses of Zend_Db_Table_Row_Abstract
. This is a thing I really don’t like. Dealing with business logic entities that map the application domain should not require you to think about persistence at all (espacially not to subclass a certain class just to make your objects persistable)! You might say „but this is Active Record!“ now. Yes, you are right – Zend_Db_Table_Row
implements the Active Record pattern. But: Active Record has not heard a word about separation of concerns. Hence it is just a collection of bad OO code.
MVC in Symfony2 Projects
Using Symfony2, you split your application into one or more bundles. Each bundle has its own controller and model classes, configuration and views. Similar to ZF’s modules, controllers and the related model classes are grouped into bundles together with views used by these controllers. This way it is easy to reuse code from other projects by just copying the required bundle.
One difference regarding controllers is the definition of the term controller in Symfony2: A controller is a PHP function that houses all the logic necessary to return a Response object that represents a particular page. (from the Symfony2 Glossary)
Thus a controller does not have to be a full-blown class with differnt actions but only a function handling a specific type of requests. Inside your controller functions, you have to create and return a Response
object. How you do this is up to you. There are cases in which you use a templating language (Symfony2 comes with Twig which might take some getting used to it but is very powerful eventually) for rendering HTML but you can always just serialize some data to provide it via a REST API, too.
As with ZF, views are implemented using templates in Symfony2. To use a template, you call the render
method in your controller and pass all data required to replace the placeholders in the template. The render
method will create a Response
object for you which can be returned by your action directly.
When it comes to model classes, there are no restrictions in Symfony2. It is recommended to use Doctrine 2 (which also is included in the distribution package of Symfony2) as your applications‘ ORM system. Doctrine 2 is already integrated into Symfony2 which makes the configuration really easy. If you follwed the steps in the Project Setup section above, you even configured Doctrine 2 already!
MVC Round-Up
Real MVC is hard to implement the correct way. This is espacially true when it comes to web applications. One major component missing to a real MVC implementation in classic web applications is the synchronity of model and view (which is normally done using an Observer Pattern). Using PHP, HTTP and HTML, you normally render a web page once and the user has to reload the page to see whether there have been changes on the displayed data. By using Ajax in combination with XMPP or any other RealTime-Web implementation, you can imitate this mechanisms. Doing so, you will end up with two MVC implementations: One on the server side and one inside the browser.
After the comparison of ZF and Symfony2 regarding MVC, you cannot say that one does a better job at it. Both frameworks help you separating your code, but the real work has still to be done by the developer using the framework.
Dependency Handling
Dependency Injection (DI) has been a buzzword in the last few years (again, PHP adopted this technique some years after the Java world did) and still is, so we will have a look on how the ZF and Symfony2 are handling objects dependent on each other.
Dependencies in ZF
There is no real DI mechanism in Zend Framework by now. It comes however with multiple components that do something similar to dependency injection. We will have a short look at two of them: The Zend_Registry
and Zend_Application_Resource
s.
The Zend_Registry
is a container for services that should be available globally in an application. You can access the registry statically, store values in it and access them somewhere else. This approach can be seen as an implementation of the Service Locator pattern. If you have external dependencies in your controller and/or model classes, you can just read them from the registry every time you need them. Two downsides of the Zend_Registry
is that it does support lazy initialization of dependencies and that it cannot provide non-shared objects (to achive this, you can always put factory objects into the registry!).
Another mechanism related to DI is provided by the package Zend_Application_Resource
. Resources are dependencies configured at the time the Zend_Application
is being bootstrapped. You can access all application resources in your controller by calling $this->getFrontController()->getParam('bootstrap')->getResource($resourceName)
.
Dependency Injection in Symfony2
Symfony2 is shipped with a dependency injection component which can be used as a standalone package, too. Every Symfony2 project uses it since the framework itself makes use of it in its core components. Besides that, you can benefit from DI in your own bundles as well. Symfony2’s dependency injection container is configured via extensions that specify their services and the objects required to build these services. Imagine for example a user management bundle which sends welcome emails to new users. Therefore there might be a service called Acme\UserBundle\WelcomeMailer
which requires a mailer object to perform its work. In this case you would create an extension for the DI container and define your service like this:
To access the welcome mailer in a controller, you just call $this->get('welcome.mailer.acme.user')
and receive a lazily created instance of the class Acme\UserBundle\WelcomeMailer
. Objects defined as arguments of a service will be provided to the service’s constructor as parameters. If you want to use the welcome mailer somewhere else, just define it as an argument of this other class and you will have an instance of the service injected.
Dependency Management: Conclusion
As we have seen, the winner of this discipline is Symfony2. It enables developers to define their objects‘ dependencies explicitely in a very flexible way. Zend Framework on the other side provides mechanisms that ease the distribution of service objects, but does not implement lazi dependency initialization or and injection of dependencies at all.
These were just a few differences between Symfony2 and Zend Framework 1. If you got interested in Symfony2 by this article, you should have a look at the framework’s website and read the Symfony2 bible.
All in all I prefer Symfony2 for new projects because it makes me write better OO code. Zend Framework is fine, too, but it looks kind of outdated to me. Anyways I’m also excited to see how ZF 2 (currently beta) works and will spend some time comparing it to Symfony2 as well – maybe in another blog article.
nice overview about functions of both frameworks. Too bad you did´t not take a view on the documentations which is still very poor for Symfony 2,
sometimes you just have to look into the framework code to figure out how thinks work, and those is even not as easy to understand as ZF.
Dir you work in larger SF2 projects yet?