Updating legacy code to php 8.x
Published: 2024-04-30 14:41:22

When you have really old code, while it might work on php 7.4, getting it ready for php 8 is a daunting task. 

But if you take it in small steps, you can do it. 

I have a code base that was originally written 20+ years ago, there was no classes, OOP, Frameworks, or any of the tools now at our disposal, so this code was old procedural code for the most part, that by some miracle actually worked on PHP 7.4.

Knowing where to start is hard, but you have to start somewhere, so start somewhere simple. 

## Basic Cleanup 

I started with short open tags. 

Back in the early days of PHP, short open tags were the standard, until issues arose with XML, which is why I still had a bunch of these in this code. 

I updated these manually, and the reason I did this manually was that I wanted to see what gremlins I would find in the process. 

To find these short open tags I used regex to find them. 
```PHP
<\?(?!php|xml|=)
```

This was a helpful process, because I did notice some other things while doing this. 

While its tempting to try and fix these, don't, but make notes on them, if you get distracted in what you are trying to fix, you will end up going down rabbit holes and never finishing what you started. 

Once all the short open tags were converted, I found that I a lot of places where I had 

```PHP
<?php echo "something"?>
```
While this isn't a deal breaker, this process is a chance to clean up your code. and <?php echo can be neater written as <?= so why not make the change? 

Now that the open tags are consistent, the ECHOs are easy to identify and update with a simple find and replace across the project.

## Code Cleanup

Now that some basics are out of the way, its time to clean up the code, while this clean up doesn't get us any closer to PHP 8, having neat well laid out code will be a big benefit in this process.

For this I set up my phpcs.xml rules with just a couple of rules

```PHP
<rule ref="PSR12" />
<rule ref="Generic.Arrays.DisallowLongArraySyntax"/> 
```

This will use the latest PSR standard for the most part with one additional rule to convert array() into []. You may feel differently about this, but I prefer the shorter array definition these days. 

## Random Errors

It was in this code clean up process where I started running into trouble. I started getting errors. 

This was a great outcome. the beautifier was finding places in my code that had had little bugs for years, some of them I am still wondering why the pages worked at all, but here we are. 

This allowed me to fix these minor issues as they got picked up. There weren't many of these, but it was great to find them. 

## Secondary Cleanup

After the beautifier had run, the code looked neat, but there were places where I had multiple blank lines, probably from removing code over time. 

I'm not sure if code sniffer has a rule for this, I couldn't find one in a quick search , but rather than spending time hunting it down, I thought I would just do another regex across the project to find these unnecessary gaps.

Using the following regex did the job nicely.
```PHP
^\n{1,}$
```
Finally a clean codebase! 

## Updating to PHP 8

For the next phase of updates, I installed [PHP Rector](https://github.com/rectorphp/rector). 

There is a temptation to configure rector and set it to the latest version of PHP 8. 

With how old the code base is, and how much has changed, this lead to an overwhelming amount of changes.

In total, doing it this way ended up at over 1000 files changed and it was too many to cope with.

As great as rector is, its not a tool that you can just let it do whatever it wants with your code, it will end very badly.

## Light Bulb Moment

During this process I realized that just because code ran on PHP 7.4, didn't mean it was PHP 7.4 code. This code had been through every version of PHP since PHP 2.0. There was a lot of old coding in it, and Rector wanted to fix it all. 

## Back Tracking

To make this task easier, I pushed the Rector PHP version back down as far as I could to PHP 5.3. 

With rector targeted at PHP 5.3, a couple of minor issues were picked up, 3 files affected, easy changes. 

When Rector got a clean bill of health on 5.3, I increased it to PHP 5.4, again a couple of minor things on 2 files.

It wasn't until PHP 5.6 that I caught anything mildly interesting, but what going back to 5.3 did was ensure I was fixing things in small bites, this is really significant to this process. 

## Updating to PHP 7.4

Each time I did a version update in Rector, it found new things to fix, most of them were able to be automated, and those that weren't, I could fix the code quickly.

In the process I found bunches of little code issues that might end up with warnings in PHP 7.4 but errors in PHP 8. 

Rector with PHP 7.1 brought about the first really odd results, and these odd results is why blindly allowing Rector to fix everything is a bad idea. 

Hidden amongst the changes was this really odd change:

```PHP
if ($show_time_out == 0)
```

was changed to:

 ```PHP
if (0 == 0)
```

A look at the code revealed that there were variables that were initialized as the wrong type. It also appeared that this change was triggered by variables that were set but never updated, probably from old code that had been removed but remanents remained.

One thing to note about Rector is that you can filter rules out. While long term you are going to want to keep most of them in, when you are in a situation where you end up with 150 files to update, automating what is safe to update will make the job of dealing with the others less daunting, in my case I had a total of 143 files with issues with PHP 7.1, but there were a lot that were safe to update and I was able to filter out the more difficult rule violations to update manually. 

## Updating to PHP 8 

Finally the moment, to update to PHP 8. When I first started out on this update, the update to PHP 8 was daunting, there was a lot of issues. Too many to deal with, but because I did the changes incrementally the actual change for PHP 8 was only 25 files instead of 1000+, and most of those were changes in string manipulation changes that could be fixed automatically.

Both PHP 8.0 and 8.1 had changes in them, but once I reached PHP 8.1, no further changes to code was found. I pushed Rector up to 8.4 to check this.  

## Left Overs

Rector can't do everything for you, there are going to be left overs, and for me those left overs were related to type mixing.

```PHP
$var = 0;
if($var == "") 
```

While I am still working through and testing to find these gremlins, there are only a few in the code base that were not corrected in some way by Rector during the process. 

As a result, the site itself is working, It still needs to be put through the usual testing phase, but the outcome was been better than expected.

## Final Thoughts

Take your time with the process, rushing it will end with broken code. 

Commit code regularly, you want to be able to back track if needed, and try again without having to go too far back. Committing after each PHP version bump made sense to me. 

While I would love to update this code base into a framework, getting it to PHP 8 was a higher priority. The code works, even though it's not the best code. 

I will move it to a framework in stages, but having a working code base to work from will make that transition a much easier one.