April 16, 2020

PHP Frameworks: Top 12 Factors to Consider

PHP Development
Zend Framework
Back to top

What is a PHP framework?

A PHP framework provides the core infrastructure for turning an incoming HTTP request into an HTTP response. That’s because PHP is most often used for web development. At a minimum, frameworks manage HTTP negotiation, dependency graphs, templating, and error handling.

Back to top

Top 12 Considerations When Choosing a PHP Framework

There are many frameworks to choose from including Laminas (formerly Zend Framework), Laravel, Symfony, Nette, Yii, and CakePHP. Deciding which one is best for you is easier when you compare how each framework addresses the following 12 capabilities in relation to your requirements:  

  1. HTTP negotiation
  2. Controllers, request handlers, and dependency injection 
  3. Templates and dynamic content
  4. Error handling
  5. Rapid application development tooling
  6. Data storage
  7. Extensibility and framework coupling
  8. Maintainability
  9. Performance
  10. Deployment
  11. Security
  12. Support

The first four capabilities listed are the most critical. If your framework provides them, you can save time and improve application quality. And with some in-house architectural and structural guidelines, you can develop anything. The importance of the last eight capabilities will depend completely on your requirements. 

Back to top

#1. PHP Framework Consideration: HTTP Negotiation

To help ensure the framework you choose can meet your requirements today and into the future, it’s important to review how it: 

  • Routes incoming requests.
  • Creates and returns responses to requests.

For example, for each request, the framework should be able to:

  • Determine if the request is well-formed.
  • Marshal any data sent to the structures the application expects
  • Deliver a response in the format the user expects — whether that's an HTML page, an RSS feed, or JSON.
Back to top

#2. Framework Consideration: Controllers, Request Handlers, and Dependency Injection 

To match a request to a handler, you need to obtain the handler and all its dependencies. You can hand-wire handlers and dependencies in your code. However, when you take this manual approach, applications quickly become an unmaintainable mess. And re-purposing code for re-use, or moving code to new locations based on new requirements, can make this approach even less attractive. 

Frameworks can automatically match every request to a handler by using "controllers" or "request handlers.” Controllers and request handlers are generally written as PHP objects. And based on what they do, they may require other objects as dependencies. 

To simplify code management, most modern frameworks provide dependency injection containers for managing the various handlers they expose, and the services that act as their dependencies. If a framework uses dependency injection, it should comply with the standards outlined in the PSR-11 Container Interface specification from the PHP Framework Interop Group (PHP-FIG). 

Back to top

#3. Framework Consideration: Templates and Dynamic Content

Most modern web applications have dynamic content that shares a common display structure or template. To populate a template, applications need to pull data from a source, such as a database or web service, and then pass it to the template to render the final page. 

Traditionally, most frameworks provided their own template renderers. However, you can simplify your code and maximize long-term flexibility by using a framework that provides only an integration layer to templating engines, which allows you to choose the one you prefer. This is the approach taken by SlimPHP and Mezzio.  

Back to top

#4. Framework Consideration: Error Handling

No web application is perfect. At some point, you will hit error conditions. Production-ready PHP applications disable the display_errors directive in php.ini. However, if no other error handling mechanism is in place, a user could get the dreaded White Screen of Death (WSOD) in response to their request. 

To avoid errors including the WSOD, evaluate the framework’s error handling options. For example, when an error occurs, does the framework automatically:

  • Notify the user that something went wrong?
  • Log the error?
  • Send an email message to specified recipients?
  • Ping a monitoring service?
  • Provide ways for you to tie into error handling to handle errors yourself?
Back to top

#5. Framework Consideration: Rapid Application Development

Many frameworks provide rapid application development (RAD) tooling to manage repeatable tasks such as:

  • Adding HTTP handlers to an existing application.
  • Creating a set of CRUD-style handlers.
  • Creating classes.
  • Updating numerous configuration settings. 

RAD tools can save time because instead of writing boilerplate code to manage these types of tasks, you can issue a single CLI command and move on to the next thing. Such tools are particularly nice when you need to quickly prototype an application.

I'll note, however, that RAD tools often come at an extensibility cost, so consider your long-term goals. In addition, RAD tools generally have a small impact on overall time savings across an application’s full lifecycle. Really, you’ll spend the most time on your business logic instead of a few seconds here and there on repeatable tasks. That’s why RAD tools should rarely be a deciding factor when selecting a framework.

Back to top

#6. Framework Consideration: Data Storage

Early PHP frameworks tended to offer database abstraction. The landscape has since changed dramatically and today, database abstraction is something that can be delivered by standalone libraries. Additionally, data sources vary widely; today we also have:

  • Document databases, such as MongoDB
  • Key/value stores such as Redis and Memcached
  • Logging and search services such as Elastic
  • An abundance of REST APIs.
  • Object relational managers and object document managers, such as those available from the Doctrine Project

Although it can be useful to have data abstraction library built-in to your framework, it’s advantageous to use a dedicated DBAL or ORM library because:

  • Project contributors are generally database experts, so you're likely getting a best-of-breed solution.
  • You can choose a different framework without having to change your data-access logic.
Back to top

#7. Framework Consideration: Extensibility and Framework Coupling

Framework authors often write code specific to their needs, and then generalize how it works for wider applications. However, if your needs fall even slightly outside the framework’s capabilities, you will need to:

  • Alter how the framework works. 
  • Use extensions to meet your requirements. 
  • Consider another framework.

Context is key when looking at the flexibility and extensibility of framework capabilities. For instance, you may want to:

  • Provide a way to filter or format data in a repeatable way. Most template engines enable you to register custom functions or filters for this purpose. 
  • Use a database not supported by a framework. With any luck, the framework provides a custom adapter so you can use your preferred database. 
  • Define a custom workflow or alter the existing framework workflow for Model View Controller (MVC) architectures. Some frameworks provide a hook or event system that allows you to tap into the defined workflow and change how incoming requests are marshaled and transformed into a response.

To Protect Choice Use the Interface Segregation Principle

To help avoid framework lock-in, it’s important to insulate yourself from framework changes. This includes protecting your ability to extend framework capabilities and switch frameworks. To achieve this flexibility, I recommend following the Interface Segregation Principal when you code. By doing so, you:

  • Create an interface for the features you consume.
  • Align the interface with what the framework defines.
  • Type-hint against your own interface. 

If possible, try to stick to interfaces defined by the PHP-FIG, because they are designed to decouple your application from specific implementations.

Back to top

#8. Framework Consideration: Maintainability and Testing

Ensuring the integrity of how your application marshals incoming data and populates your responses requires testing of components including MVC controllers, middleware, and request handlers. Some frameworks provide specialized tools for testing the entire request and response lifecycle through an application instance, complete with custom assertions. These kinds of tools can be a huge boon towards understanding how all parts work together, and they can simplify and give structure to your testing strategy. However, they also tie you directly to your framework, at a very fundamental level.

To decouple HTTP request lifecycles from a framework, look for other ways you can test. For example, The PHP League provides a package for validating that an API that consumes PSR-7 HTTP Messages conforms to an OpenAPI specification. This can be used regardless of the framework — though it may require custom scaffolding to work with an application. 

Another library, helmich/phpunit-psr7-assert, provides an easy way to assert against PSR-7 HTTP messages. It can also be used in any application that uses that standard.

To perform framework-independent integration or functional testing, another option is to use external, black-box behavior-testing systems such as Codeception or Selenium. With these tools, you can create tests that define the client conditions and actions, and assert against what they receive in response.

Back to top

#9. Framework Consideration: Performance

For all applications, it’s important to determine its acceptable level of performance. If you have a handful of users, and they all understand what is being done is computationally expensive, your requirements will be quite different than a website that supports hundreds of thousands of customers daily.

Many users turn to their framework’s baseline performance tool as a guide. They can provide metrics that show how fast the framework can return a response. However, the numbers can be misleading. The response-time metrics will be based on a configuration that’s set for minimum conditions. As your application grows, its bootstrapping costs will typically increase, adding to response times.

To maximize performance potential, consider: 

  • Frameworks that provide dependency injection containers usually offer better performance because they only instantiate the services you need for a request.
  • Some frameworks provide pre-compilation tooling to reduce bootstrap overhead from compiling configuration, compiling the DI container, compiling routing definitions, etc…. The downside of using these tools is that deployment becomes more complicated.
  • Some frameworks have internal optimizations that allow performance overhead to plateau at a certain complexity.
  • With the advent of PHP 7.4, some frameworks provide hooks into PHP's opcache preloading capabilities. These can help reduce bootstrap overhead.
  • Adding more machines to the system to support horizontal scaling. Since PHP has a fundamentally shared-nothing architecture, you can add machines behind a load balancer to improve performance. Doing this, however, requires that you architect your application so that sessions, caching, and filesystems work the same way across nodes.
Back to top

#10. Framework Consideration: Deployment

The complexity or simplicity of your PHP deployment will be determined by system complexity. For example, how many systems — such as databases, caching services, and log aggregators — are involved? Will you be scaling horizontally?

Using frameworks adds more questions and potentially, complexity. For example, some frameworks will require you to pre-compile certain pieces of the application before you deploy. Depending on your configuration, you may also need to ensure certain environmental variables are set and present on the target system.

Some frameworks provide tools that help prepare your application for development, including Platform-as-a-Service (PaaS) solutions. It’s helpful to ask the following questions to see if they meet your requirements — or be flexible enough so you can adapt them to meet your needs: 

  • Will you be deploying on local servers you have control over or pushing to the cloud? 
  • Do you have a preferred cloud provider? 
  • What services will your application use — such as databases, caching services, monitoring solutions, and log aggregators? 
Back to top

#11. Framework Consideration: Security

If you are handling any user data, you need to be concerned about security.

PHP provides many security tools and the number and quality increase with each minor release. However, when evaluating a framework’s security capabilities, see if it:

  • Mitigates cross-site scripting (XSS) vulnerabilities.
  • Mitigates cross-site request forgery (CSRF) vulnerabilities.
  • Uses hashing algorithms. Are any by default and what are they?
  • Provides capabilities for end-to-end encryption (E2EE).
  • Gives you tools to determine if a user is authenticated and authorized before managing requests. 
Back to top

#12. Framework Consideration: Support

Many PHP applications support mission-critical websites and web services. Being able to contact a certified PHP expert 24x7x365 may be important to you. If so, check if the framework has support options. 

As a Zend employee, I’ll share that you can get long-term, mission-critical support for Laminas (formerly Zend Framework). The offering also includes consultative guidance for using Laminas as well as ready-to-go adapters for commercial PHP applications.

Learn More About Zend LAminas Support

Additional Resources

Back to top