Suppose you need to create a high-load project based on a PHP MVC framework. You would probably use caching wherever possible. Maybe you would build the project in a single file, or maybe even write your own MVC framework with minimal functionality, or rewrite some parts of another framework. While, yes, this works, it’s a little bit tricky, isn’t it? Fortunately, there is one more solution that makes most of these manipulations unnecessary (save for the cache, perhaps), and this solution is called the PhalconPHP framework.
What Is PhalconPHP?
PhalconPHP is an MVC framework for PHP written in C and supplied as a compiled PHP extension. This is what makes it one of the fastest frameworks available (to be completely honest the fastest one is Yaf, but it is a micro framework and has much, much more limited functionality than Phalcon). PhalconPHP doesn’t need any long operations with PHP files and it doesn’t need to be interpreted at every request—it’s loaded into RAM once when your web server is started and consumes very few resources.
MVC frameworks have been considered best practice in web development for a long time—by now it is a sort of a professional standard, so most web developers are familiar with at least one MVC framework for PHP: Symfony, Yii, Laravel, CodeIgniter, Zend Framework, etc. They each have their own advantages and disadvantages, but what do they all have in common? All of them are written in PHP and consist of many included PHP files with a huge amount of logic that has to be run by the interpreter on every request, every single time your code runs. While this makes for great transparency, we pay with performance. Large amounts of code and lots of included files cost a great deal of memory and time, especially in PHP (since it’s interpreted, not compiled). Yes, the situation has become much better in PHP 7, but there is still lots to be improved, and PhalconPHP brings those improvements to the table.
Let’s take a look at some benchmarks.
The official benchmarks are five years old—too old to be valid now, but even then you can see dramatically what distinguishes PhalconPHP. Let’s look at something newer. In a 2016 comparison, Phalcon places top five—an obvious leader among the professional frameworks, and conceding only to raw PHP and some micro frameworks.
So, Phalcon is fast. Raw PHP is also fast, but we need all the facilities that an MVC framework has to offer, and Phalcon rises to the challenge, including components such as:
- Volt template Engine
- Dependency Injection (DI) Container
- Routing systems
- Security block
- Forms module
That’s just to name a few. To cut a long story short, PhalconPHP has everything you need to build a big enterprise application such as a RESTful API for a high-load system.
One more nice thing about Phalcon is its tiny style—just compare Phalcon ORM and enormous Doctrine 2.
Let’s take a look at creating a PhalconPHP project.
Two Types of Phalcon Projects: Full-stack and Micro
Generally, there are two types of MVC frameworks: full-stack frameworks (like Symfony, Yii) and micro frameworks (like Lumen, Slim, Silex).
Full-stack frameworks are a good choice for a big project since they provide more functionality, but they need a bit more qualification and time to run.
Micro frameworks allow you to create lightweight prototypes very quickly, but they lack functionality, and so it’s generally better to avoid using them for large projects. One advantage of micro frameworks, however, is their performance. They are usually much faster than full-stack ones (for example, the Yaf framework is inferior in performance only to raw PHP).
PhalconPHP supports both: You can either create a full-stack or a micro application. Even better, when you develop your project in PhalconPHP as a micro application, you still have access to most of Phalcon’s powerful features and its performance still remains faster than as a full-stack application.
At a past job, my team needed to build a high-load RESTful system. One of the things we did was compare prototype performance between a full-stack application in Phalcon and a Phalcon micro application. We found that PhalconPHP’s micro applications tended to be a lot faster. I can’t show you any benchmarks due to NDA reasons, but in my opinion, if you want to make the most of Phalcon’s performance, use micro applications. While it’s less convenient to code a micro application than a full-stack one, PhalconPHP’s micro applications still have everything that you might need for your project and better performance. To illustrate, let’s write a very simple RESTful micro application in Phalcon.
Building a RESTful API
Almost all permutations of a RESTful application have one thing in common: a
User entity. So, for our example project, we will create a tiny REST application to create, read, update, and delete users, (also known as CRUD).
You can see this project fully completed at my GitLab repository. There are two branches there because I decided to divide this project into two parts: the first branch,
master, contains only basic functionality without any specific PhalconPHP features, while the second one,
logging-and-cache, contains Phalcon’s logging and caching functionality. You can compare them and see how easy it is to implement such functions in Phalcon.
I’m not going to go over installation: You may use any database, any operating system, and any web server that you want. It is described well in the official install documentation, so just follow the instructions depending on your operating system.
Note that your PHP version should not be less than 5.6.
I use Ubuntu 16.10, PostgreSQL 9.5.6, Nginx 1.10.0, PHP 7, and Phalcon 3.0. I’ve included an Nginx config sample and a PostgreSQL dump file in the project, so feel free to use them. If you prefer another configuration, it won’t be difficult to change it.
Project Structure and Configuration
First of all, create the initial project structure.
While Phalcon lets you use any structure you like, the structure I chose for this exercise partly implements an MVC-pattern. We have no views because it’s a RESTful project, but we have controllers and models, each with their own folder, and services. Services are the classes that implement the business logic of the project, dividing the “model” part of MVC into two pieces: data models (which communicate with the database) and business logic models.
index.php, located in the
public folder, is a bootstrap file that loads all the necessary parts and configuration. Note that all our configuration files are placed in the
config folder. We could put these in the bootstrap file (and this is the way shown in the official documentation), but, in my opinion, this gets unreadable in big projects, so I prefer the folder separation from the very beginning.
Our first pass at
index.php will load configuration and autoload classes, then initialize routes, a dependency injection container, and the PhalconPHP micro application. It will then pass control over to that micro application core, which will handle requests according to the routes, run business logic, and returns results.
Let’s take a look at the code:
There are several ways to store configuration files in Phalcon:
- A YAML file
- A JSON file
- An INI file
- A PHP array
Storing your configuration in a PHP array is the fastest option, and since we’re writing a high-load application and don’t need to slow down our performance, this is what we’ll do. Specifically, we’ll use a
\Phalcon\Config object to load our configuration options into the project. We’ll have a very short configuration object:
This file contains two basic configurations, one for the database and one for the application. Obviously, the database configuration is used for connecting to the database, and as for the
application array, we’ll need it later since it’s used by Phalcon’s system tools. You can read about Phalcon configurations in more detail in the official documentation.
Let’s look at our next configuration file,
loader.php file registers namespaces with corresponding directories via the
\Phalcon\Loader object. It’s even simpler:
Now all the classes from these namespaces will be automatically loaded and available. If you want to add a new namespace and directory, just add a line in this file. You can also avoid using namespaces by registering specific directories or specific files. All these possibilities are described in the PhalconPHP loader documentation.
Configuring the Dependency Injection Container
Like many other contemporary frameworks, Phalcon implements a dependency injection (DI) pattern. Objects will be initialized in the DI container and be accessible from it. Likewise, the DI container is connected to the application object, and it will be accessible from all the classes that inherit from the
\Phalcon\DI\Injectable class, such as our controllers and services.
Phalcon’s DI pattern is very powerful. I consider this component to be one of the most important in this framework and I strongly recommend you to read its entire documentation to understand how it works. It provides the key to many of Phalcon’s functions.
Let’s take a look at a few of them. Our
di.php file will look like this:
As you can see, our dependency injection (DI) file is little more complicated and there are some particulars you should be aware of. First off, consider the initialization string:
$di = new \Phalcon\DI\FactoryDefault();. We create a
FactoryDefault object which inherits
\Phalcon\Di (Phalcon allows you to create any DI factory that you want). According to the documentation,
FactoryDefault “automatically registers all the services provided by the framework. Thanks to this, the developer does not need to register each service individually providing a full stack framework.” This means that common services such as
Response will be accessible within the framework classes. You can see the full list of such services in the Phalcon service documentation.
The next important thing is the setting process: There are several ways to register something in the DI container, and all of them are completely described the the PhalconPHP registering documentation. In our project, though, we use three ways: an anonymous function, a variable, and a string.
The anonymous function allows us to do many things while initializing the class. In this project specifically, we first override a
Response object to set the
JSON for all of the project’s responses and then initialize a database adapter, using our configuration object.
As I mentioned before, this project uses PostgreSQL. If you decide to use another database engine, just change the database adapter in the
db set function. You can read more about available database adapters and about the database layer in PhalconPHP's database documentation.
The third thing of note is that I register a
$config variable which implements the
\Phalcon\Config service. While it’s not actually used within our example project, I’ve decided to include it here because it’s one of most commonly-used services; other projects may need access to the configuration almost everywhere.
The final interesting thing here is the actual
setShared method itself. Calling this makes a service “shared,” which means that it starts acting like a singleton. According to the documentation: “Once the service is resolved for the first time the same instance of it is returned every time a consumer retrieves the service from the container.”
The last included file is
routes.php. Let’s leave it empty for now—we’ll fill it alongside our controllers.
Implementing a RESTful Core
What makes a web project RESTful? According to Wikipedia, there are three main parts to a RESTful application: - A base URL - An internet media type that defines state transition data elements - Standard HTTP methods (
DELETE) and standard HTTP response codes (200, 403, 400, 500, etc).
In our project, base URLs will be placed into
routes.php file and other mentioned points will be described now.
We’ll receive request data as
application/x-www-form-urlencoded and send response data as
application/json. Though I don’t believe it’s a good idea to use
x-www-form-urlencoded in a real application (since you’ll struggle to send complex data structures and associative arrays with
x-www-form-urlencoded), I’ve decided to implement this standard for simplicity’s sake.
If you remember, we’ve already set our response JSON header in our DI file:
One of the most important aspects of a RESTful application is correct and informative responses. High-load applications are usually big, and errors of various types can occur everywhere: validation errors, access errors, connection errors, unexpected errors, etc. We want to transform all of these errors into unified HTTP response codes. It can be easily done with the help of the exceptions.
In my project, I’ve decided to use two different kinds of exceptions: there are “local” exceptions- special classes inherited from the
\RuntimeException class, separated by services, models, adapters and so on (such division helps to treat each level of the MVC model as a separate one)- then there are
HttpExceptions, inherited from the
AbstractHttpException class. These exceptions are consistent with HTTP response codes, so their names are
Http500Exception, and so on.
AbstractHttpException class has three properties:
appError. First two properties are overridden in their inheritors and contain basic response information such as
httpCode: 400 and
httpMessage: Bad request. The
appError property is an array of the detailed error information, including the error description.
Our final version of
index.php will catch three types of exceptions:
AbstractHttpExceptions, as described above; Phalcon Request Exceptions, which may occur while parsing a request; and all other unanticipated exceptions. All of them are converted to a pretty JSON format and sent to the client via standard Phalcon Response class:
Creating Models with Phalcon Dev Tools
If you use a contemporary IDE, you’re probably used to code highlighting and completion. Likewise, in a typical PHP framework, you can include a folder with a framework to go to a function declaration just in one click. Seeing as Phalcon is an extension, we don’t get this option automatically. Fortunately, there is a tool which fills this gap called “Phalcon Dev Tools,” which can be installed via Composer (if you still don’t know what it is, the time is now to get to know about this amazing package manager). Phalcon Dev Tools consist of code stubs for all the classes and functions in Phalcon, and provide some code generators with both console and GUI versions, documented on the PhalconPHP website. These tools can help with creating all the parts of the MVC pattern, but we’ll only cover model generation.
OK, let’s install Phalcon Dev Tools via Composer. Our
composer.json file will look like this:
Controllers and Routing
Since our application only CRUDs (creates, reads, updates, and deletes) users, we’ll create only one controller, the
Users controller with the following operations:
- Add user
- Show list of users
- Update user
- Delete user
While controllers can be created with the help of Phalcon Dev Tools, we’ll do it manually and implement
AbstractController and its child,
AbstractController is a good decision for Phalcon because we can place all the necessary classes which we’ll get from dependency injection into the PHPDoc block. This will help with the IDE autocomplete function. We can also program in some error constants that are common to all the potential controllers.
For now, our abstract controller will look like this:
Now it’s time to fill in the
routes.php file. In Phalcon micro applications we create collections, one for each controller, and add all the handled requests as
delete methods, which take a route pattern and a proceeding function as arguments. Note that a proceeding function should either be an anonymous function or a controller’s method name. Here’s what our
routes.php file looks like:
We also set a handling controller and a URI prefix. For our example, a URI will look like
http://article.dev/user/add, and it must be a
post request. If we want to change user data, the URI must be a
put request and will look like
http://article.dev/user/12 to change data for the user with an ID of
12. We also define a not-found URL handler, which throws an error. For more information, refer to the PhalconPHP documentation for routes in a full stack application, and for routes in a micro application.
Let’s move to the controller’s body, and specifically the
addAction method (all the others are similar; you can see them in the application code). A controller method does five things:
- Gets and validates request parameters
- Prepares data for the service method
- Calls the service method
- Handles exceptions
- Sends the response
Let’s walk through each step, starting with validation. While Phalcon has a powerful validation component, it’s much more expedient to validate data in an old-style way in this case, so, our validation block will look like this:
Now let’s look at the results using Postman. Let’s test some trash data first:
Logging and Caching
It’s hard to imagine a high-load application without logging and caching, and Phalcon provides very seductive classes for it. But I’m writing an article here, not a book; I’ve added logging and caching to the sample application, but I’ve placed this code into another branch called
logging-and-cache so you can easily look at it and see the difference in the code. Just like the other Phalcon features, these two are well-documented: Logging and Caching.
As you can see, Phalcon is really cool, but like other frameworks, it has its disadvantages, the first of which is the same as its main advantage—it’s a compiled C extension. That’s why there is no way for you to change its code easily. Well, if you know C, you can try to understand its code and make some changes, run
make and get your own modification of Phalcon, but it is much more complicated than making some tweaks in PHP code. So, generally, if you find a bug inside Phalcon, it won’t be so easy to fix.
This is partially solved in Phalcon 2 and Phalcon 3, which let you write extensions to Phalcon in Zephir. Zephir is a programming language designed to ease the creation and maintainability of extensions for PHP with a focus on type and memory safety. Its syntax is very close to PHP and Zephir code is compiled into shared libraries, same as the PHP extension. So, if you want to enhance Phalcon, now you can.
The second disadvantage is the free framework structure. While Symfony makes developers use a firm project structure, Phalcon has very few strict rules; developers can create any structure they like, though there is a structure that is recommended by its authors. This isn’t a critical disadvantage, but some people may consider it too raw when you write the paths to all the directories in a bootstrap file manually.
PhalconPHP: Not Just For High-load Apps
I hope you’ve enjoyed this brief overview of PhalconPHP’s killing features and the accompanying simple example of a Phalcon project. Obviously, I didn’t cover all the possibilities of this framework since it’s impossible to describe all of them in one article, but fortunately Phalcon has brilliantly detailed documentation with seven marvelous tutorials which help you understand almost everything about Phalcon.
You’ve now got a brand new way to create high load applications easily, and you’ll find, if you like Phalcon, it can be a good choice for other types of applications too.