Wouldn’t describing PHP: Hypertext Preprocessor (PHP) as a programming language be a bit reductive when you think about it? We must face the facts: PHP is not a simple programming language. It’s a complete ecosystem, with a gigantic community, thousands of contributors, and new features being proposed and released regularly. But not only that: millions of libraries and application programming interfaces (API) are written and launched thanks to PHP. Even many command-line tools are entirely developed thanks to the PHP language. PHP is a whole world on its own. Let’s start by looking at the reasons why PHP is not just a language for writing a website.
These are the topics we will cover in this chapter:
This can be seen in several things that we can list together, as follows:
PHP is a language that has a proven reputation. Its robustness and efficiency have made it the language of choice for some of the world’s largest sites. Where alternatives have been implemented in the past or currently, such as Python (which is used by around 1.2% of all websites at the time of writing) for some Google sites, PHP is in the majority. Obviously, many attractive technologies are emerging and taking market share from PHP, such as Node.js or C# and the .NET Framework. PHP still has a good future ahead of it. Knowing how to write a website in PHP ensures that you’ll know how to read the source code of the overwhelming majority of existing sites in the world.
For all these reasons, PHP is an ecosystem. Pushing the reflection a little further... if PHP is not just a programming language and if PHP is not just code, why should we limit clean code to code?
The clean code could also contain, by extension, the right choice of external dependencies and libraries to install on your project. Let’s see why you should choose your dependencies wisely and how to choose them well to limit the risks.
Choosing the right external library to install can be a real challenge. It’s a challenge we’ve all faced or will all face one day. The reason is simple: there is no point in reinventing the wheel. The reason we want to install an external library is usually the same. We have a specific problem that we want to solve as cleanly as possible. Here, two situations arise:
It is then interesting to call upon an external library whose role is to bring us a very specific solution to our case. The advantages are multiple, as outlined here:
We can clearly see that the choice of open source dependencies is quite inevitable. If you want the insurance to not end up with an unusable tool unavailable overnight, open source is made for you because you can store a copy of the source code as long as you want without fear. This is the first excellent way to choose an external library.
A second factor to consider is the frequency of updates to the project. Obviously, if a project is open source but has not been updated for several months or even several years, beware: it may be an abandonment. In this case, it means that the project may not support the next versions of PHP, for example, or that the bugs and security flaws will not be fixed anymore. There are two effortless ways to know if a project is still maintained or not, as set out here:
A third factor to consider is the documentation of the library. You’ll probably want to make sure that the project has minimal and sufficient documentation to set up the basics. If no documentation is provided, you can be sure that using the external library will be a systematic pain. Indeed, all code maintenance will become a battle to remember how the project works, without documentation to help you or to share knowledge. Moreover, this can also be very much related to the community around the technology you want to use. If very few people use the project you want to integrate into yours and the community is quite inactive, or even non-existent, nobody will be able to help you in an optimal way. This can be an effective way to decide whether to use this or that dependency in your code.
A fourth factor is the number of dependencies that the library itself depends on. Generally speaking, we prefer a library that has very few dependencies. Fewer dependencies mean fewer packages to update and fewer third parties, so there is less chance of problems in one of those parties.
Finally, many projects have continuous integration (CI) badges on their main page. These badges allow you to know at a glance the test coverage (as a reminder: the proportion of code covered by tests), the number of tests, the latest version, and so on. Obviously, it is better to choose a project with as many tests as possible and with a high test coverage to limit problems during updates.
Speaking of updates, let’s talk about versioning and—especially—semantic versioning. If the external library you want to use follows the rules of semantic versioning, this could have an incredibly positive and reassuring impact on your developments and updates. Let’s take a look at what this means exactly.
Versioning is simply putting a number on a version of the source code. We are all familiar with versions such as 1.0, 1.5.0, 2.0.0, and so on. The semantic versioning adds a semantic—that is to say, precise meaning to each of these numbers. Let’s take version 2.3.15 as an example. Here is how semantic versioning breaks down this version number:
We can see the advantages of semantic versioning: serenity, logic, and consistency. There are obviously other variations such as Alpha, Beta, Release Candidate, and Golden Master. But these are rarer.
Semantic versioning also includes a particular notation that allows your dependency manager to know how to install new versions and when to update your dependencies. Let’s take as an example this extract of the file that Composer uses to install dependencies (and this is the same principle for many dependency managers out there):
{
"require": {
"php": ">=7.3",
"symfony/dotenv": "3.4.*",
"symfony/event-dispatcher-contracts": "~1.1",
"symfony/http-client": "^4.2.2"
}
}
This snippet describes four dependencies: a minimal version for PHP, as well as three external libraries. It doesn’t really matter what these libraries are. We can note here four separate ways to define the versions we want to accept in our dependencies. Let’s see what they are.
The first way to write the version we observe is by using the >= operator. This one is one of the easiest to understand: we want to accept all versions greater than or equal to the one specified. Here, our application accepts all versions of PHP higher than version 7.3, as well as version 7.3 itself. Of course, dependency managers accept other such operators: =, <, >, and <=. You can also combine these operators to get very precise version constraints—for example, by writing “>=1.2.0 <2.0.0”.
The second operator is quite well known because it is used in many other contexts. It is the wildcard, denoted *. This symbol simply represents the fact that you can replace it with whatever you want. In the preceding example, we accept all the patch versions of the 3.4 version of the dependency. This allows it to benefit only from bug fixes, without updating the minor version. This wildcard can be placed anywhere in the version number. For example, the notation 3.* will benefit from all minor versions of the major version 3.
The following notation is the use of the tilde operator, ~. This operator means that you will only benefit from the patches of the given version. In the example, we will then benefit from all the patch versions of version 1.1 of the dependency (that is, 1.1.0, 1.1.1, 1.1.2, and so on). This is remarkably similar to the wildcard operator, except that the wildcard operator cannot be placed anywhere in the version number and only concerns patches. Also, it is worth noting that Composer interprets the tilde a little differently: it also allows minor versions, not just patches. If you are using Composer and you want to benefit only from the patch versions without the minor versions, you will have to use the wildcard operator.
Finally, the last operator we will see is the caret operator, denoted ^. In the preceding example, the caret operator allows all patch versions as well as minor versions of major version 4 (that is, 4.2.2, 4.2.3, 4.4.0, and so on). If you want to define a minimum version of a dependency while accepting new patches and minor versions but refusing major versions (which may bring breaking changes) automatically during the update of external libraries, this is a particularly good choice. That’s why it’s one of the most popular operators.
The possibilities are endless, and once you have mastered this notation, you can be confident about updating the dependencies of your project. As far as good practices are concerned, it is always a clever idea to accept all new patches and minor versions of a dependency. You should never lock a dependency to an extremely specific version without any conditions or possibility to update. Indeed, if an external library scrupulously respects semantic versioning, you will have no conflict with your existing code. Breaking changes are reserved for major versions. Therefore, you should not automatically accept major versions when updating your dependencies: chances are that you will have to adapt your code to make it work properly.
Let’s finish this chapter with a few words about the most recent versions, but also about trendy external technologies and libraries.
First, let’s talk about the latest versions of external libraries. Of course, we might be tempted to use the latest ones, the ones that were just released a few hours ago. It is worth remembering that bugs may appear, and a new patch version may be released in the near future if this is the case. Or not. And in this case, the bug could persist for a while. So, it’s particularly important to write tests. Imagine the comfort: you update all your dependencies, you run your test suite, and if all the lights are green (and your application is properly tested), you can be fairly sure that everything is fine.
That said, if any tests turn red because you’ve updated an external library, you’ll have to investigate to find out where this is coming from. In any case, you shouldn’t think that you are safe from any problem if your dependencies are well fixed and constrained or you only accept patches and/or minor versions. Patches could also bring bugs—you never know.
As far as Alpha versions are concerned, let’s be clear: these versions are not made for production applications. The different libraries are clear on this point: the code can change from one day to the next, bringing breaking changes without warning. In short, you must be incredibly careful. That said, if you want to evaluate these versions to see for yourself, the developers of the libraries will be delighted to receive your feedback. The Beta versions are supposed to be more stable and not bring any more breaking changes. You should still be incredibly careful when using them.
As a general rule, only use the final, stable versions in production. Reserve the Alpha and Beta versions for development and test environments if you want to be ready on the day of the stable release for production deployment. New features are always exciting things, but they are never worth sacrificing the stability of your application. Your users don’t care about the new features of the external libraries you use: only stability matters—the fact that it just works.
Now, let’s talk about trendy technologies (an external PHP library, a new tool, or even a new programming language). You hear everyone around you talking about a particular technology. This technology is spreading like wildfire, you hear about it everywhere on the internet, huge companies are getting into it, and tech conferences are all about it. You must be wary of this kind of thing. Even if the promises of these technologies can be exciting and revolutionary, think first about what is important: your users.
Will this technology make a real difference to your end users? Is it really worth training on it and taking weeks or even months to figure out how it works? You need to be sure that it will have a real positive impact on your project. You also must keep in mind that innovative technology will have a small community. The impacts are immediate, as outlined here:
Finally, you must make sure that the project is robust so that you don’t end up with a recent technology abandoned without warning. This happens more often than you think, and much (if not all) of your work will have been for naught. So, beware of the latest unproven technologies, and be sure of the robustness and seriousness of the project. Wait until you’ve had some time to think about it. Again, your users will surely be able to do without this technology (which they will not be aware of) until it is mature.
Limiting PHP to the programming language is reductive. We have just seen it—it is a real ecosystem with a rich and active community, and extremely far from burying its favorite language. The developments around PHP are countless, and the language itself has evolved in the most beautiful way in recent years. The contributions of functionalities gave a real second wind to this one, allowing it to claim—still today—first place among the most used programming languages on the server side for a web application.
All this would be nothing without the explosion in the number of external libraries available for the language. You have a problem; there is a solution. We are fortunate that most external libraries are open source. Thousands of developers make available, voluntarily and free of charge, the fruit of hours, weeks, or years of work.
Making a choice from among these libraries can be difficult and challenging. It is important, even mandatory, to do real research work beforehand to be sure to make the right choice. We are not immune to obstacles and incidents, but this chapter has provided you with tools and ready-to-use solutions to limit the risks. Above all, don’t rush into the most fashionable technologies. If you want to attract users and have them continue to use your application more than another, the key words are “robustness” and “stability”!
We have talked a lot about other people’s work, but we should not forget our own achievements. How can you manage to develop good habits to find your way in your code as you manage to find your way effortlessly in the source code of your favorite external libraries when you need to understand its internal workings? We come back to what we said in the first chapters: by having the same habits, we understand each other more easily. This obviously applies to the organization of a project, in the naming of files, the structure of folders, and so on. And this is exactly what we will see in practice in the next chapter.