PHP as a language is maturing and frameworks surrounding it seem to be following the trend. The first generation of frameworks, those built around PHP 4 are now long forgotten and nobody seems to remember the time when use of classes was impossible (or discouraged when possible) using our favorite language. The second generation was mostly centered around solid object model introduced by PHP 5 and included widely deployed tools such as CodeIgniter, Zend Framework 1 or Symfony 1.
While CodeIgniter silently died away, folks around Zend Framework and Symfony decided to rewrite their tools and learn from their own mistakes. Many frameworks have appeared during this exciting period of PHP development. They all introduced some new thinking to PHP: namely, design patterns are now used more prominently and there is a lot of attention on abstracting object creation. This means that instead of standard $obj = new Class()
, a different approach is taken with Abstract Factories, object composition, etc.
When a colleague and I were tasked to find a framework to start a new PHP project in 2014, we couldn’t possibly review each one of them and making an informed decision was bound to disregard a good number of good frameworks.
For example, we didn’t review FuelPHP. While this framework has a lot of going for it – namely simplicity and good documentation, we also remembered the issues surrounding their plan to release a version 2 which became vaporware, which (probably unfairly) gave us a reason to eliminate it.
We also shied away from CakePHP, which is proved by years of existance, but seemed overly kitchen-sinkish in its approach. Some frameworks like Yii, while obviously solid, we had to skip because there wasn’t enough hours in a week to review it properly.
So, after some research, we decided to review the following frameworks:
- Zend Framework 2 – I have worked with Zend Framework 1 and have extensive experience with the new release. I can’t say I am too happy with it, so while I wouldn’t terribly mind using it for a new project, I would rather poke an eye out than have to create a new module in it.
- Symfony 2 was an obvious contender because we have used parts of it before. Like Zend, Symfony is very modular and you can use bits and pieces included in it without using the entire framework. This means that I already had a lot of experience with Doctrine, which Symfony relies upon for ORM.
- Laravel seems to be framework du jour, a sort of a new CodeIgniter, with a low learning curve. It’s based on some Symfony modules, so it wouldn’t be completely unproven. Also, Facebook HHVM seems to support it fully, which goes to show that it is taken seriously by a wider audience.
- Phalcon is another trending framework with a primary selling point of being written in C (PHP modules can either be native, written in PHP, or written in C, like most low level modules are) which gives it a nice speed advantage.
Our project is a simple one: an e-commerce site with a twist – we want to sell t-shirts and give affiliates a lot of control. We would be prototyping this site, meaning that we would adopt an agile approach, first developing a prototype and extending upon it in iterative development, as opposed to the traditional systems life cycle which would assume strict planning and full specification before start of development.
Our requirements and review points:
- rapidity of development,
- scalability,
- performance,
- ability to write modular code and
- quality of documentation.
Some requirements were obvious, like unit testing capabilities, which most modern frameworks support nicely. We wanted to make sure the framework is feature-complete so we could concentrate on concepts and not worry about e.g. how to store a session to the database. Other things were completely irrelevant, like ease of deployment (we are not among the shared hosting crowd, which would disqualify Phalcon immediately).
Zend Framework 2 was the first on the desk. I have built a couple of largish projects in it so it was my call to determine what is good and what is not about it.
Zend Framework 2 positives were:
- Modularity is perfected in it – it has beautiful ways to make your code modular and forces you into good design patterns to make it so.
- Things are abstracted away beautifully in this framework, which means you have a lot of control over everything. For example, if you want your controller to have access to particular libraries and models, you create a factory for it so when it’s loaded all dependencies are ready. Most frameworks nowadays use this approach, known as dependency injection, but Zend revolves around it.
- Modules exist for a lot of things, which is always nice. However, most modules aren’t very mature so you won’t find a solution to every problem there, or you will have to reinvent the wheel because module’s author didn’t write it in the way that you would like.
- Some things are very well thought out: for example for each session container you can define its time-to-live and number of hops (page requests) it will last. So you can create a custom “flash messenger” which will last for 1 post page request by doing:
$container = new \Zend\Session\Container('name') $container->setExpirationHops(1, 'post') $container->foo = 'bar';
Unfortunately, Zend Framework 2 has some negatives too:
- I gave you an example for setExpirationHops method of \Zend\Session\Container. I know about it because I dug into the code of one of Zend’s controller plugins. It was documented for the first version of this framework, but I dare you to find it in new documentation. It’s not there, and it should be. It is in API documentation, but the information is skimpy to say the least. Documentation is written in a tutorialish manner, meaning information is dumbed down and simplified and lacks a lot of important stuff.
- So, the only way to really learn it is to browse the code. I said that Zend does a great job of abstracting things away and letting you program by contract, but try digging into its code and you will find it hard to follow. The execution of code isn’t linear, it rather depends on events and forces you to adhere, so it’s very hard to follow what part of code is calling which event and what kind of magic happens to every object in the execution flow.
- There is no ORM implemented out of the box. While there is a solid query builder and ways to interact with database, if you want ORM you need to use third party modules.
The biggest drawback to Zend Framework 2 is that it is incredibly verbose. To create a controller, in most frameworks you would drop a class in a controllers’ directory and it would be available. In Zend, you will do that, but also have to tell the service manager (which handles the creation of every object) where it is and if it has any dependencies.
Configuration is complex and allows for a lot of customization, but when you are writing configuration files, you are returning arrays and there is no autocompletion in your IDE for it. So, you have to search through (bad) documentation to figure out how to do it. Documentation often recommends this approach in which you pass big arrays to factories, but it’s not a favor to developers.
Combine this verbosity with a simple matter of fact: when you use factories to create objects, your IDE will not know how to autocomplete anything. E.g., this is a piece of code that you will write a lot, despite the fact that Zend developers recommend against it:
/* @var \Some\Class\Of\Mine $obj */ $obj = $this->serviceLocator->get('SomeService');
Otherwise, how does your IDE know what $obj is? How does a developer inheriting your code, or you after a month, have any idea what $obj is? A bit ugly… A better solution to this is to handle your depending objects with dependency injection, but verbosity of doing that will discourage you and you will always fallback to abusing serviceLocator for everything. Symfony2 has this solved much better with it’s configuration files.
Symfony 2 leaves a different type of impression. While Zend Framework 2 feels over-engineered, written by people who know a lot about PHP but have developed very few sites in it, Symfony 2 feels very natural and has a solution to most common problems.
Some clear advantages emerged for Symfony 2:
- Symfony is feature rich. Both Zend and Symfony have a kitchen-sink approach, but Zend is much lower level. For example, Symfony offers a way to manage your assets practically out of the box. While you can find similar modules for Zend Framework 2, they are third party. This may sound like a so what? situation, but there is a crucial difference: using bundled modules (or bundles in Symfony 2 lingo) will force you into respecting the grander design of the framework. Also, if there is one official way to accomplish a task, tomorrow, when you hire a new developer, they will have little doubt in what the approach was. This advantage goes a long way sometimes.
- The structure and code is very intuitive. E.g. if you are reading the tutorial about parsing forms, you will notice that the example given binds the form to a model class. You will quickly come into a situation when there is no model to bind and Googling “how to create a form without a model class” will give you just the right information. Folk behind Symfony 2 clearly gave a lot of thought to what developers need, and that’s great.
- There is a great support for modular development by using bundles.
There was some bad stuff about Symfony 2 as well. Namely:
- It takes learning. Documentation is quite useful, but very tutorialish, while I would much prefer a heads first approach. You can’t dive into development without spending some hours reading first so you can figure out how to do it properly. Of course, this can be a good investment of time because it forces you into best practices, but I am sure it could be done better.
After reviewing Symfony 2, we started looking into Laravel. Laravel uses some components of Symfony 2, and it is hyped about the Internet a lot. Most people who really like it quote its ease of use.
Advantages of Laravel were:
- Easy learning curve and clear documentation, insomuch that it resembles CodeIgniter. Documentation is very important and both those frameworks got it right: divide it into discreet tasks that developers care about (e.g. how to handle sessions, or how to to authentication), explain it succinctly and completely. I wish other frameworks did this as well.
- Sane approach to development and easy to get started.
Disadvantages were:
- Confusing ecosystem and lingo: I don’t understand why developers can’t just use well established terminology. For example, what’s wrong with calling the ORM – ORM? How does this make sense to a newcomer:
class User extends Eloquent {}
. What is “Eloquent” and why do I have to look it up? - A lot of logic that should belong to a controller is defined in routes. For example, the documentation suggests you should hint which controllers and actions should be available to non logged in users by customizing your routes configuration. This seems rather out of place to me and goes against loose coupling principle.
- Caching abilities seem to be quite limited.
- There is no support for traditional modules. You can work with packages, but they are targeted at vendors. You can’t easily group your controllers, models and views into logical groups.
The biggest disadvantage was that everything is hidden behind a facade. This means that they use standard dependency injection for a lot of things, but then hide it behind a facade with magic methods. E.g. you write code like:
Session::someMethodCall()
Session actually does not implement someMethodCall, but rather uses a magic method to call some form of: $di->get(‘session’)->someMethodCall(). This is pointless and wrong for many reasons, not least the fact that, again, there is no autocompletion for your IDE (you can do it via a third party package though). Also, while most of the Laravel’s code is namespaced, facade classes aren’t! Perhaps there is a good reason for this, but to me it was confusing.
There are other, very minor, issues I had with Laravel: inconsistency of using namespaces and dreaming up words to represent functionalities (Illuminate, Eloquent, etc.) are one of them. But also, blatant disrespect for FIG standards and different coding styles for example code wasn’t to my liking.
Overall, Laravel seems nice. However, it’s also recipe for disaster. Just like PHP allows novices to write atrocious code, and attracts novices because of the low learning curve, so does Laravel use some anti-patterns and attracts newcomers because it has great documentation. In the right hands, Laravel can be a good framework, but in the wrong (most) hands, it will cause unmaintainable code good only for hobbyist projects.
Finally, after traditional frameworks were exhausted, I installed Phalcon and gave it a spin.
It’s fast. Very fast: under 1 ms calls on my machine for most controllers without database calls. But you don’t code without database calls, so in the end, it won’t do a tremendous amount of difference.
I can’t say a lot about Phalcon. E.g. form helpers for radio html fields were only introduced recently to it (version 1.3), so it’s still in heavy development. Custom routes didn’t come until version 0.4, which gives you a state of maturity.
When it comes to its design, it kind of resembles Zend Framework 1 in its functionality but utilizes dependency injection heavily and openly, so it follows the trend.
I like it a lot, but I think I will wait for a couple of years before I know it’s not vaporware.
So, to summarize:
- For rapidity of development Laravel wins hands down. But down the road, you will hit the ceiling of its capabilities quite soon. Zend Framework 2 loses heavily, with Phalcon being very easy to get started with and crank out code. Symfony 2 is balanced somewhere in between Phalcon and Zend.
- Scalability goes to Zend Framework 2 and Symfony 2. When I say scalability, I talk about scalability of code – developing a true enterprise application. Their excellent modular design and great unit testing integration lets you really build upon the code.
- Performance goes to Phalcon, but Facebook’s HHVM runs Laravel just fine which would make it just as quick.
- Modularity goes to Zend Framework 2 and Symfony 2. Phalcon doesn’t lack there too much, but it’s not as highly polished.
- Quality of documentation is in a way a matter of personal taste. I really like Laravel’s approach to concise and complete, broken down, sections explaining common tasks.
Overall however, I think that Symfony 2 is the way to go.
Clear, Concise, Broad and in depth with descriptions, And exactly what I was looking for.
Thank you very much for breaking these frameworks down in the way you have.
Respect.
This felt like a very well written, mostly unbiased, and good article. Too bad Yii2 isn’t part of it, but thanks a lot for taking the time to write it!
It is a nice and in depth article, but one thing you didnt mention about Symfony is that it is too slow. I remember it was even slow on my localhost, so forget about it being fast on a server in real life. Unfortunately, when it comes to PHP frameworks one is forced to choose not the best one, but the least evil.
Very Nice and detailed comparision. Thanks for sharing such informative article.
Worth to read this comparison. Thanks a lot. I must appreciate your efforts and time to write a simple and meaningful article.