journey from “Hello World” to frameworks

PHP is a fairly popular programming language. For many years on the Internet you can hear the statement that PHP is dying. However, the language is still alive and actively used. If you're looking to choose a language to learn in 2024, you might want to consider other programming languages; the language has many worthy and more popular competitors. But if you finally made up your mind and started learning PHP, then this material is for you.

PHP is primarily used for developing web applications. To be precise, their server part, which is usually called the backend. This means that your code will run on the server. This makes things a little easier for you, since you only need to worry about ensuring that your application only works with certain versions of PHP that are available on your server, rather than adapting your code to multiple browser versions, which is what front-end developers are forced to do.

But even despite the narrow specialization of the language, it can still be used in other areas, although not as effectively. For example, it is quite possible to write applications for the desktop or simply write scripts for the command line.

PHP has always attracted the attention of beginners due to its low entry threshold. PHP code forgives many errors that other languages ​​do not. You can spend hours figuring out why your first application doesn't want to launch if you write in Java or Python. In PHP, beginners usually have much fewer problems. This is both an advantage and a disadvantage of the language. You will be able to start developing your applications very quickly, but later, you may encounter a lot of errors in the code as your project grows and you will still have to delve into learning the basics, which, in the same Java and Python, are simply required from day one.

Object Oriented Approach

So. You've already written your first “Hello, World” in PHP and may even be writing some long scripts that do some magical calculations. The more data, the more confusing your code becomes. You are constantly forced to copy pieces of the same code to perform the same action in another place.

Gradually, an understanding of what a function is comes and you begin to transfer repeated pieces of code into them. Now instead of

echo 4 * 6 + 7;
...
echo 12 * 3 + 20;
...
echo 22 * 24 + 100;

In our code something like this appears:

calc(4, 6, 7);
...
calc(12, 3, 20);
...
calc(22, 24, 100);
...
function calc($a, $b, $c) {
    echo $a * $b + $c;
}

And let your more experienced colleagues scold you now that you wrote bad code, but now, at least, you won’t have to run through the entire code, look for your formula and correct it if we go to do something else instead of these operations with three values. And if you have something more complicated than simple arithmetic operations, then you no longer need to constantly copy this same piece of code throughout the program, but simply call your function.

We move on and see that your code is overgrown with huge structures that we repeat over and over again. If we need to transfer information about a person somewhere, then we use arrays, for example, like this:

$user = array(
    "name" => "Саша",
    "birthday" => "2001-03-08",
    "contacts" => array(
        "phone" => "555-55-55",
        "email" => "sasha@example.org"
    )
);

Everything seems to be clear. But these structures strive to grow in volume. New fields appear in them every now and then, and you have to run through the entire code again and again and look for where similar structures exist and edit them. There are also multi-level arrays, when in order to get to the necessary information you need to specify several square brackets after the array name to get to the necessary data. You forgot one level and you don’t have the necessary data. Even worse, all sorts of warnings start popping up. Life is slowly turning into hell.

And then you learn about miraculous classes, where you can not only store all our valuable data, no worse than in arrays, but also transfer your functions that are associated with this data into them. For example, to calculate age, you find a solution on some StackOverflow, make the getAge function, and you get something like this:


$contacts = new Contacts;
$contacts->phone = "555-55-55";
$contacts->email = "sasha@example.org";
$user = new User;
$user->name = "Саша";
$user->birthday = "2001-03-08";
$user->contacts = $contacts;

class User
{
    public $name;
    public $birthday = null;
    public $contacts;

    public function getAge()
    {
        if ($this->birthday) {
            $birthday = new \DateTime($this->birthday);
            $interval = $birthday->diff(new \DateTime());
            return $interval->y;
        }
        return false;
    }
}

class Contacts
{
    public $phone;
    public $email;
}

Quite verbose, but now our IDE is starting to tell us what it’s called and what it’s called. You no longer need to keep the entire data structure in memory; you just need to use the hints. If we need a phone number, then you just write

$phone = $user->contacts->phone;

Your senior colleagues are telling us something about encapsulation, about a black box, setters, getters… But your problem has been solved and the code works.

Now you know that there are classes. That in these classes you can store your variables, which are called properties, and nest functions, which are called methods. Now, instead of a string of function or method parameters, you simply pass an object of the desired class, which contains all the necessary data.

Next, you may learn about static properties and methods of classes. Studying inheritance…

Almighty composer

You are successfully developing your projects, but you are beginning to understand. The number of classes is growing at incredible speed. You yourself are already confused about what and where to connect. The most logical thing you could come up with is to put one class in a separate file. But there were so many of them that from the string require dazzles in the eyes. We need to do something about this.

On the Internet you learn about autoloading classes, about a certain “composer”. Probably something complicated. We are looking for how to automatically load classes. PHP could always load any file from the directories specified to it into include_path. There is also a description that you can write your own function for autoloading. We investigate the issue a little and get the following code, which we place in a file located in the directory along with other classes.

set_include_path(__DIR__);
spl_autoload_extensions('.php');
spl_autoload_register();

Here set_include_path specifies the path where files with classes will be searched, spl_autoload_extensions specifies a tail for the file name that will be appended to the class name, spl_autoload_register registers autoloading of classes. Unfortunately, this code allows you to load classes only if all the characters in the class name are converted to lowercase. How can this be fixed? Write your own autoload function.

Here you will learn about namespaces (namespace) and your class autoloading code turns from a simple class connection into some kind of fairly decent-sized function. And this code will have to be copied everywhere.

You are not the first to think about this, and for quite some time PHP programmers got together and came up with a tool for themselves – composer. This is a script written in PHP. All you need to run it is a php component to run applications from the command line. Those who use Linux are luckier because they just need to install the appropriate package on their system and then use the command composer from the command line. This is a little more difficult to do on Windows. But even if you don't want to install anything, you can simply download the file composer.phar and run it from the Linux command line – ./composer.phar (don't forget about the +x flag) or Windows – php.exe composer.pharof course, indicating all the necessary options.

What does this script do? It simply queries one or more databases to see if they have a package with a particular name. The main database is the website https://packagist.org/. Anyone can post their packages on this site. Naturally, in this case, all packages will be openly available to any PHP developer. If you want to close packages with your code, then you need to study the documentation composer and create your own private repository with the source code that you will use. Creating proprietary code is beyond the scope of this article, so we will assume that you have nothing to hide and you only use publicly available repositories. In this case, in addition to installing composer to your PC, no additional settings are required from us.

To add composer in your project, just run the command composer init. The first time you don’t need to think too much about the answers. Use the default values ​​and simply press Enter for all questions until the script finishes running. All entered values ​​can be easily corrected even by simply editing one file. As a result of executing your command, two folders will appear in the project (if they did not already exist) src/ And vendor/and also the file composer.jsonapproximately as follows:

{
    "name": "fsa/composer",
    "autoload": {
        "psr-4": {
            "Fsa\\Composer\\": "src/"
        }
    },
    "authors": [
        {
            "name": "Sergei Fedotov",
            "email": "fsa@tavda.info"
        }
    ],
    "require": {}
}

This file is in standard JSON format. As you can see, if you ran the command yourself, the file contains the same values ​​that you entered or accepted by default at the initialization stage. The contents of this file may change after the command is executed composer. In addition, the file can be edited manually. But I don't recommend doing this when dating, because it can have side effects and you might just break the structure of the JSON file. With sufficient experience and understanding of how it works composerediting this file can become your routine activity.

Let's consider the most obvious option for beginners. You don't want to publish anything either composer needed only to use ready-made code libraries to solve your problems. In this case, there will be very little interesting for you in the file composer.json.

  • name — package name. Only used when publishing your package or application.

  • autoload — setting up autoloading of classes. It's worth paying attention to this!

  • authors — information about the authors. May only be useful if you are publishing your package or application.

  • require is a list of third-party packages that you will need.

Except require the file may contain a parameter require-dev. Essentially, this is a single list of third-party packages used. However, you may only need some packages during the development phase of your project. These are the packages that will be located in the section require-dev. When it's time to deploy the application to the server, you can simply say composer use packages only from the section require.

Folder src/ at the root of the project. This is nothing more than a folder for storing your classes that will need to be loaded. There is a similar folder in almost all packages that are provided by composer. It is indicated in the section autoload and at any time can be called by a different name or moved to a completely different location on the disk. If you delete it, nothing bad will happen. Just composer will no longer be able to automatically load the classes you need from there. If the folder was created automatically, it is empty.

Folder vendor/ at the root of the project. This is the main directory where composer stores everything necessary for his work. And, perhaps, the only file that you need to pay attention to at the stage of getting acquainted with composer – file vendor/autoload.php. It is this that you will need to include inside your PHP file for autoloading of classes to start working for you. This folder can almost always be painlessly deleted from your disk and created again, but this will require Internet access (or by some miracle the necessary data will end up in the cache on your PC). It is strictly not recommended to make any changes inside this folder, because they can easily be lost. Small spoiler. By setting composer you can optimize the class search process and make it faster. This can be done on production servers to speed up request processing. When developing, it is better to use the default file provided to you, because in this case you will not have unexpected side effects after editing your classes.

After composer was initialized inside your project and you received a file composer,jsonyou can start installing the necessary packages. For example, let's install a package from the author of this text, which makes it easier to work with the Telegram Bot API:

composer require fsa/telegram-bot-api

At the same time in the file composer.json changes will appear in the section immediately require:

    "require": {
        "fsa/telegram-bot-api": "^0.4.1"
    }

In our case composer accessed the database on the website https://packagist.org/found from a user fsa package with name telegram-bot-api. From this information I found the repository address https://github.com/fsa/php-telegram-bot-api and downloaded from there the latest, currently stable version of the package (0.4.1). If desired, you can install other versions. To do this, you must explicitly specify the required package version. In this case, not the exact version, but a template may even be indicated. For example, if you specify 1.0.*then the oldest available version will be downloaded, starting with 1.0.. Or you can specify ^0.4then the latest version from the branch will be downloaded 0.4. Read more about the template format on the website composer. You can even specify * as the version number. But it is highly not recommended to do this, because some incompatible changes may appear in new versions of libraries and your program code may simply stop working. But there is one exception to this rule. In section require the necessary components of the PHP language can be specified. For example, if you want to use the curl functionality in PHP, you can explicitly indicate this in your composer.json:

composer require ext-curl "*"

Of course this won't force composer install the necessary packages on your operating system, but when you try to deploy the application, you will be informed that something is missing on your machine. And you won't have to think about what went wrong. This code works on one computer, but not on another.

Note that the version number may contain characters that are interpreted differently on the command line. In this case, they must be escaped, either by including the entire version string in quotes, or each individual special character using the slash character (composer require ext-curl \*).

You may notice a warning that appears in the console when you try to run the command composer:

Composer could not detect the root package (fsa/composer) version, defaulting to '1.0.0'. See <https://getcomposer.org/root-version>

In the initial stages, you can simply ignore this warning. It means that composer can't figure out your package version. You can read more about this at the given link. But without going into details, this warning will disappear as soon as you initialize the version control system in your project (for example, git) and make the first commit. In this case composer will detect its use and will obtain version information from it. There are other ways to specify a version number, but in 2024 I wouldn't recommend using them, especially since not having a version number without putting your package somewhere public or restricted does not hinder your work in any way.

After installing the package, you can create a simple php file, create a bot in Telegram and send yourself a message through it:

<?php

require_once 'vendor/autoload.php';

$api = new FSA\Telegram\TelegramBotApi;
$query = new FSA\Telegram\TelegramBotQuery('TOKEN');
$result = $query->httpPostJson(
    $api->sendMessage(123456789, 'Hello, World!')
);
var_dump($result);

Where 'TOKEN' is an access token for your Telegram bot, and 123456789 — user account ID or private chat, or the text name of your public chat. If you actually try this library, then do not forget that your user must be subscribed to the bot, and when sending a message to a group, the bot must have rights in this group for the appropriate actions.

As you have read earlier, in composer.json there is a separate section require-devwhich is intended for packages that are needed by the application only during the development phase. For example, this could be a package phpunit. In this case, when connecting such a package, a key is added --dev or -D:

composer require phpunit/phpunit --dev

Such packages will be installed by default, but can be ignored when installed on a production server.

Store folder vendor/ in each of your projects in the repository it is wasteful. Moreover, it is always easy to reproduce. Therefore, in the vast majority of projects, this folder is on the ignore list. If you want to use someone else's project, then you need to run one of two commands

composer install

or

composer update

As a result of executing both commands, the settings will be analyzed composer and the appropriate packages are installed. The key difference between these commands is that when called install in the root of your project the presence of the file is checked composer.lock and, if this file is found, then exactly those versions of packages that are listed in this file will be installed. Thanks to this, it is possible to completely reproduce the state of the folder vendor/which was on the developer's machine.

If the file composer.lock will not be detected, then the command install will behave the same way the team will behave update. It will analyze the contents of the file composer.jsonwill determine the required package versions and install them in the folder vendor/ and will create or update (if you did update) file composer.lockso you can pass it on to your colleagues or clients if necessary.

If you are editing a file composer.json manually, then after changing it you must execute one of the commands. Of course, if you changed package versions, then this should be composer upwhich is short for the command composer update.

If you have previously worked with npm for JS or other package managers for other programming languages, you may notice that composerin terms of operating logic, they have a lot in common.

Composer packages and namespaces

The main thing you need to understand is that composer does not impose any restrictions on where your classes will be located, both physically on disk and in what namespaces. When you first get acquainted with OOP, you may not even be aware of namespaces. By default, your classes are always located in the main space. But you can easily change this simply by adding a directive namespace before the class declaration, for example:

<?php

namespace Fsa\Composer;

class Example {}

Previously there was an example that in the configuration file composer.json you may find the following lines:

    "autoload": {
        "psr-4": {
            "Fsa\\Composer\\": "src/"
        }
    },

This means that composer will look for classes that have a namespace Fsa\Composer in a folder named usr\ (relative to the folder where the file itself is located composer.json). Which namespace to choose is up to you. You don't even need to use a namespace for your application, and the configuration file will look like this:

    "autoload": {
        "psr-4": {
            "": "src/"
        }
    },

But on the Internet you can find comments that such a configuration can negatively affect performance. Yes, such a configuration works, but there is no point in checking it. In addition, many frameworks offer their own default namespaces. For example, Symfony uses App:

    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    },

If you ever plan to publish your libraries publicly, be sure to study the best practices for choosing a namespace for your package. Nothing bad will happen even if there is an intersection of names with another package. But if someone wants to use your package and the conflicting one, then they will not be able to do so.

Thanks to different namespaces, you can have several classes with exactly the same names in your project. The main thing is that they do not end up in the same namespace. In order not to write the full name of the class every time, as was previously the case with the Telegram Bot API – FSA\Telegram\TelegramBotApiyou can specify it once at the beginning of your file via use which class you want to use, and also give it a new alias, for example:

<?php

require_once 'vendor/autoload.php';

use FSA\Telegram\TelegramBotApi;
use FSA\Telegram\TelegramBotQuery as Query;

$api = new TelegramBotApi;
$query = new Query('TOKEN');
$result = $query->httpPostJson(
    $api->sendMessage(123456789, 'Hello, World!')
);
var_dump($result);

When using use you will always be able to freely replace one class with another, for example, if the library you are using was abandoned, but someone else forked it and also made it publicly available. You just need to change one line in the file and your code will use a different library. The main thing is that its behavior is exactly what your code expects.

Putting things in order in the project folder

When joining a project composerthen a lot of files and folders appear in the project root. If your old project consisted entirely of only PHP files that could be hosted on a web server, then you should consider moving them to a separate folder within your project. A good practice is to create a folder public/. This is where the root section of the web server is located.

Let's move our example of sending a message to Telegram from the project root to the folder public/then the code will take a slightly different form. Let’s fix a potential file search problem in one go autoload.php:

<?php

require_once __DIR__.'/../vendor/autoload.php';

use FSA\Telegram\TelegramBotApi;
use FSA\Telegram\TelegramBotQuery as Query;

$api = new TelegramBotApi;
$query = new Query('TOKEN');
$result = $query->httpPostJson(
    $api->sendMessage(123456789, 'Hello, World!')
);
var_dump($result);

In this case, only one line was changed with require_once. Constant __DIR__ always points to the directory where the file where this directive is used is located. Next, we indicate the rise to the directory above and, then, the path to the file autoload.php. Even though the web server cannot display files from a folder higher than public/the php interpreter can always connect, read and use any files on the server file system to which it is granted access.

Thanks to the fact that the web server root directory is located above the project root section, there is now no need to be afraid that some developer will use the library vasya/superliba which you use will upload a hacking file into your php project and will be able to execute your code simply by calling the file https://example.com/vendor/vasya/superliba/hack_you.php from your server. He simply won’t be able to do this, due to the fact that the folder vendor/ simply not accessible via a web server. Of course, the hacker Vasya still has options for hacking your server, so approach the choice of libraries with caution and do not install dubious or unpopular libraries.

The last step – frameworks

Looks like you have one last step left to take. You are evolving. You use it to the fullest composer. You constantly have to copy your same code from project to project. You get tired of this and start writing your own application framework, placing it on https://packagist.org. Problems come one after another. It’s not clear how to do it here, it’s not clear here. Numerous object creations via new. We need to get rid of this. You study design patterns, initially nothing is clear at all, but you figure it out. Find that there is some kind of DI container. Some kind of routing is needed. We need adequate answers, including for errors in the code. Sooner or later you may end up with your own framework. It is quite possible that it will even work, and will use good programming practices that you learned while writing it. However, over time, maintaining your framework becomes more and more difficult. New knowledge, rewritten code. And you again rewrite your applications that use this code.

And before you start inventing your own framework, my advice to you is to use a ready-made one!!! No. I don’t discourage you from writing your own framework in order to figure out what and how it works. This is great practice. But there are a lot of programmers who have already solved similar problems and they got together and made it convenient. Frameworks are essentially ready-made application skeletons that solve most of the issues that you previously solved manually. Large frameworks like Symfony or Laravel are maintained by people with good knowledge of the programming language and experience of good coding practices. If a new version of the framework may break your code, then, as a rule, there are tools that will warn you in advance, even before the breaking version is released, about those parts of the code that may break.

Well, the most important advantage of using high-quality frameworks is that by studying the framework you are simultaneously learning the best programming practices. It will be easy for you to navigate the code of other projects that are written in the same framework, and even in the code of applications on a different framework.

Of course, learning PHP doesn't end there. New versions of the language and new versions of frameworks are being released, which make the life of a developer even easier. But that's a completely different story.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *