📜 ⬆️ ⬇️

DevConf: from shawarma to symfony or legacy migration

Toward the end of last year’s DevConf, Artem Degtyar and Pavel Stepanets told how they migrated an ERP system written in PHP5.3, working on Windows, into Symfony + PHP7, and based on it a b2b cloud service. The video is available on the report link . And I will present the text, a little compressed, option.


We worked on a large system that allowed us to create applications and change statuses, plus billing, accounting for goods and materials, and a lot of things. Today we will tell how to refactor this system, migrated it to symfony. The system was originally written in pure PHP, and had many “features”. For example, this five-level ternarnik on the slide worked in a very original way with the date that came from the user.


')
Another example of intricacy. Not the best way to secure $ _GET & $ _POST. Let us turn to more objective metrics. PhpMetrics showed that there is a lot of code, but few files, and the “maintainability” of the code was very low.
The previous programmer left the project and we inherited it in this state:



Large server server, 400 users, huge controllers. We started with the fact that using PhpMetrics we built a dependency graph and found the key nodes of the system. They covered them with unit tests and started reworking them. We cleaned out the “originality” and by tests we saw that nothing broke.
I wanted to work with the database more conveniently than with pure SQL. Included in the project Doctrine ORM. She tuned up pretty easily. We generated an XML config from an existing database, and according to it, classes of entities with annotations. But not everything was smooth. There was no foreign key in the database. When we added connections between entities, the doctrine tried to create these connections. But the data at that time in the database was inconsistent and any attempt to create the keys caused base errors.



Do not use the doctrine without migrations! We used DoctrineMigrationBunde. It allows you to calculate the difference in the schemes between the database and the doctrine config and generate migration. Non-consistency was removed by the merciless delete from ... left join (by foreign relations) where the foreign field is null in migrations.
There was one moment when the doctrine code worked well on LAN, but refused to work in production. It turned out that the lexer of annotations of the doctrine fell when I met Cyrillic comments there. Do not use them! (I would advise to avoid using Cyrillic at all except for localization files. Adelf note )



The next step was the introduction of HttpFoundation. A small task of modifying the form from POST to GET, which is not the most pleasant task if the global $ _GET & $ _POST arrays are used. I decided to integrate HttpFoundation from symfony. And this process was almost painless. In a code which caused the controller, the symphony requester object simply began to be transferred.

The logical continuation was the complete processing of the front controller. Previously, it was a huge file that did everything. I connected the dependency files, initialized a bunch of global (yes-yes) variables, such as $ DB, $ USER, authentication, search, routing, logging, error checking and calling controllers. The result was the integration of HttpKernel, a component of the symphony, which allows you to fully control the process of performing an HTTP request. It has an EventDispatcher in its dependencies and it raises a bunch of useful events there. Front controller greatly simplified.



Creating a request object. Call HttpKernel to receive a response (response), which we send, and then we complete the work. But here the problem was revealed. The project's shawarma controllers could return a string, but could return nothing (null or false) and make echo themselves.



I had to expand the standard HttpKernel, adding to it what is highlighted on the slide. All this happened without a feature freeze stage. Tasks came constantly. But the implementation of these components without breaking Bacward compatibility allowed implementing features simultaneously with refactoring.

The next step was the introduction of DependencyInjection. The system contained a large number of service classes that were difficult to bootstrap. The DependencyInjection component allowed to flexibly configure all these services, providing a single access mechanism to them.

In the old system there was a certain system of plug-ins based on global variables and routing depended on them as well. We decided to switch to standard symfony routing. Inside it, they used an old resolver, thus allowing legacy routes to work too.



The day came when we decided to completely switch to symfony. In a separate branch of the gita we allocated all our shawarma into a separate bundle. However, they retained the entire physical structure of the files in order to avoid a bunch of conflicts when merge / rebase with the main branch. As I already described, we rewrote HttpKernel, however at this stage we decided to do it without affecting the kernel. We have added the so-called DefaultController, which included the whole scheme with the processing of old controllers. However, all the routes fall under this pattern, so this route must go the last.



Shawarma stubbornly resisted. She had her own auto-loader. To do this, a call to the old autoloader was added to the AppKernel :: initializeContainer: spl_register_autoload ('oldAutoload');

Initialization of global variables is not lost anywhere. She was carried to listener onKernelRequest.

As a result, we currently have a project in which there is still a lot of legacy, but we can already call it Symfony-based and implement all new features in Symfony-style. And we did it without the feature freeze, so the business was pleased.

Finally, a small refactoring plan for translating the project to symfony.





I considered this a good material for an evening-Friday post. Come May 18th to DevConf. I think there will be many similar stories to be heard. For example, Andrei Bryukhanov wants to make a report " Rewrite the project and survive ." And for Habr's readers there is a special registration with a discount .

Source: https://habr.com/ru/post/354546/


All Articles