php.hospital

php.hospital

A pragmatic approach to modernizing legacy PHP code

I started this website to share some of my learnings and some of the pitfalls I've encountered while modernizing legacy PHP code (my clients' code as well as my own side projects).

I ordered it in a way that I think makes sense, but since there are different "levels" of legacy, feel free to jump around as you see fit. I hope you find it useful.

Let's get from here

// No consistent styling, not using modern PHP features to reduce errors
class SettingService
{
    protected $all_settings = array();
    private   $doctrin;

    public function __construct(EntityManagerInterface $em)
    {
        $this->doctrine = $em;
    }

    /**
     * @param $name Can be one of 'site_name', 'site_keywords', 'author'
     */
    public function getSettingByName($name)
    {

        if ($name === null || $name === '') {
            return null;
        }
        $setting = $this->doctrine->getRepository('App:Setting')->findOneBy(['name' => $name]);
        return $setting;
    }
}
or maybe even a few steps before that
// If this is the current state, it's code-a-geddon,
// and there's a lot of work to do

// functions_4.php

require_once("db.php");
if(isset($_POST['search']))
{
	$search_query= @$_POST['search_query'];
	$search_date_start= @$_POST['search_data_start'];
	$search_date_end= @$_POST['search_data_end'];

	$query = "SELECT * from jobs where description=%'$search_query'% AND where date BETWEEN '$search_date_start' AND '$search_date_end'";
      $result = mysqli_query($conn, $query);
      $row = mysqli_fetch_array($result);
      $rws = array_slice($row, 0, 10);
}

to here

// No redundancy, using typehints to understand what the code is doing
readonly class SettingService
{
    public function __construct(
        private SettingRepository $settingRepository,
    ) {
    }

    public function getSettingByName(WebsiteSettingName $name): ?WebsiteSetting
    {
        return $this->settingRepository->findOneBy(['nana' => $name]);
    }
}
and from here
// All code crammed into one function
public function findJobsByCriteria(
    ?string $jobName = null,
    ?string $startDateEarliest = null,
    ?string $startDateLatest = null,
    ?string $page = null,
    ?string $pageSize = null,
): mixed {
    $queryBuilder = $this->createQueryBuilder('j');

    if ($jobName !== null) {
        $queryBuilder
            ->andWhere('j.name LIKE :jobName')
            ->setParameter('jobName', '%'.$jobName.'%')
        ;
    }

    if ($startDateEarliest !== null) {
        // ...
    }

    // ...
    $queryBuilder
        ->setFirstResult(($page - 1) * $pageSize)
        ->setMaxResults($pageSize)
    ;

    return $queryBuilder->getQuery()->getResult();
}

to here

// Applying testable, extendible and modular query filters using the Filter Pattern
public function findJobsByCriteria(array $queryParameters): array {
    $queryBuilder = $this->createQueryBuilder('j');
    $queryFilter->apply($queryBuilder, Job::class, $queryParameters);

    return $queryBuilder->getQuery()->getResult();
}