📜 ⬆️ ⬇️

Pseudo-encapsulation of legacy include when there is no time to refactor

Today I want to consider the migration of code from the distant past to the modern framework.

The most common situation that I can cite as an example is str_repeat ('very-', 20) old code that does not even know classes is planned to be transferred or partially used in a modern framework, but there is no time to rewrite thousands of lines and dozens of dependencies. This happens when the customer suddenly decides to significantly upgrade or develop a project that has been working without changes for 10+ years, and one parttime-oldschool programmer has sorted it off occasionally rebooting a couple of services and recovering passwords.

I should note that this article was prompted to me by the description of the “Garbage Wrapper” from search in the comments to my previous article.

So, imagine that you have already come out of depression after seeing the code, the coffee is over, and now the moment has come when you are ready to start and have even installed your favorite suitable framework, but ...
')
After a short debug, it turns out that the whole project is built on hundreds of chains of inclodes and it is impossible to “pull out” the necessary piece of code to make a service / model out of it.

Take for example the classic file of that wonderful noPSR-era:

// legacy_lib.php include("settings.inc.php"); require("functions.php"); require_once("database.connection.php"); define('SOME_CONST', 'value'); $var1 = funcName(CONST_2); function get_Var2A($param1, $param2) { return functionFromAnotherInclude($param2, $param1); } class myClass { var $data = ''; function getData() { global $var1; // do somethig return get_Var2A($var1, SOME_CONST); } } include_once("specialCode.php"); function needThis() { $obj = new myClass(); return unknownFunctionFromInclude() + $obj->getData(); } $var2 = needThis(); printr('{"param":' . $var2 . '; "var": ' . $var1 . '}'); 

In fact, such a file can often reach 1000+ lines and there are many times more dependencies.

You can try to spread this code into classes, services, and so on. But the likelihood that it will work the same way will go to zero.

We need a quick solution, which will give the opportunity to start the task and make the refactor “smoother” well, or at all to score to postpone it for some time.

I will not apply canonical design patterns here because in such situations it is very subjective. I will propose only to use the approaches of these two: the flyweight and the adapter.

We will need the adapted tool for launching and pseudo-encapsulating the legacy code, and the adapter for universal access to it.

I deliberately do not use (term | template) s: "facade", "mapper", "decorator" and so on. Undoubtedly, depending on what the legacy file (s) has and which structure it has - some or other (term | template) s may be more suitable.

I aim at relative universality, so I mean that I will “adapt” the result of the adapter to the needs of the service / model.

Now more about each.

The tasks of the opportunist in my case are as follows:

  1. Change if necessary directory inclodes;
  2. Connect the required file;
  3. Buffer the result;
  4. Encapsulate global variables;
  5. Pseudo-encapsulate global functions;
  6. Provide access to all of the above.

Adapter Tasks:

  1. Customize and create a fitter;
  2. To give the opportunity to work with the opportunist as with a normal object;
  3. Give the ability to override any methods and properties;
  4. To be a super class for “facade”, “mapper”, “decorator” and other structural patterns

What we get as a result:

 class MyLib extends LegacyAbstractAdapter { /** * Configure flyweight */ protected function configure() { $this ->setLegacyFile('legacy_lib.php') ->setLegacyPath('/path/to/includes') ; } } $myLib = new MyLib(); //   $var1 = $myLib->var1; $var2 = $myLib->var2; //   $myLib->var1 = 'some new value'; //    $res1 = $myLib->get_Var2A($param1, $param2); $res2 = $myLib->needThis(); //     $content = $myLib->getFlyweight()->getContent(); 

Now we also have the opportunity to decorate, make compositions and so on.

 class MyLib extends LegacyAbstractAdapter { /** * Configure flyweight */ protected function configure() { $this ->setLegacyFile('legacy_lib.php') ->setLegacyPath('/path/to/includes') ; } //   public function needThis() { return 'dummy value'; } //   public function get_Var2A($param1, $param2) { return '<font>' . $this->getFlyweight()->call('get_Var2A', [$param1, $param2]); . '</font>'; } //  . } 

And, in my opinion, only depending on the content of the final class “MyLib” - “adapter” can be called something more appropriate.

It is also possible to access the class declared inside the file: creating an instance, getting constants and calling its static methods.

Although this can be done directly by referring to it "by name" - this possibility is present for abstraction. In the event that such a class ceases to exist after the refactor, it will be sufficient only to replace one method of access to it, and not all calls.

And, of course, there are a number of shortcomings, which should be said:


Summary: if you do not have a few months on the refactor, but there is a small performance margin - I think you might find this useful: github

Thanks for attention.

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


All Articles