Migrating a legacy PHP application to Laravel

When I started working at Zonneplan in 2016, I was tasked with the goal to migrate our legacy PHP architecture to a standardized solution.

The application itself was running just fine, but to allow the team to scale, we needed to make sure that the architecture could scale with it. Since I had previous experience with Laravel, we choose to adopt it as our framework-of-choice.

At the time, the main application that had to be migrated contained about 100.000 lines of code, mostly written in a non-MVC manner combining both HTML and PHP in loose files.

Rewriting this application into another framework would have taken months (at least) and would probably have resulted in a feature-freeze and dozens of bugs in the process.

In the end, we did not rewrite the application, but simply "wrapped" it in a Laravel container. In this article, I will show you we did it.

Heads up: this is not going to be pretty, but it works oh-so-well 🙈

First steps: get your directory structure in order

  • Create an empty Laravel application Choose whatever method you wish to create a new Laravel application, as long as you end up with an empty skeleton.
  • Create a new folder to contain your legacy application We called it legacy-app to make the distinction between the regular app folder clear while still showing it's purpose.
  • Copy your legacy application into that folder
The entire legacy application contained in one top-level folder

Create a fallback route

In your router file (usually web.php) create a route like this:

Route::any('{all}', ['uses' => 'LegacyController@index'])

Create a layout file wrapper

{-- legacy.index.blade.php --}
@extends('layouts.app')
@section('content')<?php require_once $includePath; ?>@endsection
@section('title', 'Legacy')

Create a fallback controller

Last, we created a fallback controller that acts like a proxy between Laravel and our legacy application.

class LegacyController extends Controller
{
    public function index(Request $request)
    {
        $path = base_path('/legacy-app/') . $request->path();

        // If the path does not end with PHP, assume we're requesting an index file
        if (substr($path, -3) != 'php') {
            $path .= '/index.php';
        }

        return $this->renderPath($path);
    }

    protected function renderPath($path)
    {
        view()->addNamespace('legacy', base_path('legacy-app/'));

        if (file_exists($path)) {
            return view('legacy.index', ['includePath' => $path]);
        } else {
            abort(404);
        }
    }
}

Conclusion

  • Migrating a legacy app into Laravel does not always have to be as hard as it looks.
  • This method of wrapping your application into a Laravel container allows you to keep developing new features using new techniques while maintaining backwards compatibility.
  • This sure ain't "pretty code", but it gets the job done very well.

This code is still powering our codebase as to this day and basically has not changed since I have written it.

If you're tasked with the job of migrating a legacy application into Laravel, I hope this technique will make things easier for you.