Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

glennutter's avatar

How to upgrade very old Laravel gradually

The company I'm working for have a very outdated Laravel 3 application still in production. They want to upgrade Laravel to the latest version.

They use a normal release process where all devs create feature branches => pull requests => reviews => merge into main => automatic deployment.

I'm seeking advice on how to gradually perform this upgrade. It's not possible to put everything else on hold and just focus on upgrading Laravel. The team still has to work on new features for the existing app to some degree.

Our first idea was to create a new Laravel installation from scratch hosted in a subfolder of the repo and then gradually move logic to the new app. This would allow us to continue pulling in changes from the main branch but would result in the mother of all merges at some point.

Our second idea was to have a new Laravel installation "in front of" the old one and have a wildcard route at the bottom of web.php that somehow forwards the request to the old app if the new one hasn't implemented it yet. We don't know if that's at all possible.

We are extremely thankful for any ideas on how to tackle this problem.

0 likes
45 replies
glennutter's avatar

I'm afraid there's zero tests (I know). This company is on a journey to modernize their entire dev team and their overall processes working with development.

Sinnbeck's avatar

@glennutter Ouch that makes it a lot harder. My suggestion was to make an upgrade branch. Then upgrade 1 version fully. Then rebase from main, and update the changes done since the branch was made. I did this myself on a quite huge project. But there was tests for almost everything, so I was sure it all worked before merging back into master.

glennutter's avatar

@Sinnbeck Yes no tests makes it a bit sketchy since it's a quite large application. That's why the team wants to gradually ease into the upgrade so changes can be somewhat scoped.

glennutter's avatar

@Sinnbeck Yes Laravel 3, which means that stuff like LaravelShift isn't an option. We haven't found any official documentation, just a couple of blog posts talking about upgrading smaller applications to 4.

Sinnbeck's avatar

@glennutter Ok. Maybe the old laravel 3 can be the one in a sub folder? I dont exactly know how it works in laravel 3. And can you run php 8 in laravel 3 (I would assume not)

Unless that is possible I dont think there is any way around my original suggestion of upgrading everything in a branch, and then later merge the newest back in

cwhite's avatar

Our second idea was to have a new Laravel installation "in front of" the old one and have a wildcard route at the bottom of web.php that somehow forwards the request to the old app if the new one hasn't implemented it yet. We don't know if that's at all possible.

This is essentially what we do as we still have some very old PHP monolithic files that need to be cleaned up (it's a work in progress).

We have a catchall route that fowards request to a LegacyController that then handles the legacy code.

        if (is_legacy_request()) {
            Route::any('{catchall}', [LegacyController::class, 'handle'])
                ->where('catchall', '^(?!nova.*$).*')
                ->name('catchall');
        }
Sinnbeck's avatar

@cwhite interesting. How do you handle this with two different laravel versions? Multiple composer files and vendor directories?

cwhite's avatar

@Sinnbeck, so it's just a single Laravel install (and we're actually on 9.x) so pretty up-to-date. The legacy stuff is still served on .php pages through the LegacyController.

cwhite's avatar

@Tray2, thanks, I've looked at that, but the legacy stuff is ancient monolithic php pages so Shift isn't going to be able to magically MVC those pages

glennutter's avatar

@cwhite The catch all route is the easy part, but how would one pass the request over to the other laravel application? Can't simply require index.php because it will throw an error complaining that constants like LARAVEL_START has already been defined etc because the "outer" Laravel application is still up and running.

Sinnbeck's avatar

@cwhite Dont think this would work either as they are going to have to require two vendor folders (two laravel versions) and perhaps even two different php versions

glennutter's avatar

@Sinnbeck The idea was to go for Laravel 8 that has support for PHP 7.4 which is the current version the system is using. Laravel 3 doesn't use composer, so there's no vendor folder in the old app. The Laravel core files is included in the repo of the entire app.

Sinnbeck's avatar

@glennutter Ah ok that might make it a bit easier then! Does it have a public folder or is the index.php just in the root ?

Sinnbeck's avatar

@glennutter Ok. Might need a bit of "fixes" then. I dont have a laravel 3 install so I cannot really test. But the way I would go about it then, would be to copy the laravel 3 app into a laravel 8 app in a subfolder. Then try and wire it up like @cwhite suggested. Might take a bit of rewriting of the old laravel 3 core to get working, but as it isnt in vendor, that should be possible.

I dont suppose you have the installation files for laravel 3 lying around ?

glennutter's avatar

@Sinnbeck I did a poor attempt to do this last night.

I added this catch all route at the bottom of web.php in Laravel 8: Route::any('{path}', Laravel3Controller::class)->where('path', '.*');

And in the Laravel3Controller I added an __invoke() method to catch the request. But I'm not sure on how to fire up Laravel 3 from here. I required it's public/index.php but got lots of errors about constants like LARAVEL_START already being defined etc.

Is there a better way to fire up Laravel 3 from my Laravel3Controller, or should I follow down the path of tweaking it until it starts?

Sinnbeck's avatar

@glennutter Thats the problem, I have never seen the laravel 3 codebase, so I am having a hard time giving any ideas on how to fix it. I dont suppose you have the original installation files for it ?

glennutter's avatar

@Sinnbeck I understand and thank you for your input. I don't have the original files, but I can strip out business logic from the existing app and provide you with it?

Sinnbeck's avatar

@glennutter It would be fun to take a look. But just be sure to leave in a route I can actually test (a page that says "hello world" is fine) :)

cwhite's avatar

@glennutter,

And in the Laravel3Controller I added an __invoke() method to catch the request. But I'm not sure on how to fire up Laravel 3 from here. I required it's public/index.php but got lots of errors about constants like LARAVEL_START already being defined etc.

I would think you could add most of the stuff in the Laravel3 index.php into the Laravel3Controller::__invoke() method, then you have no need for the Laravel3 index.php file.

Eg. (note that I'm using the Laravel 9 index.php as an example because I don't know how Laravel 3 operates)

<?php

namespace <>;

use Illuminate\Http\Request;
use Illuminate\Routing\Controller;

class Laravel3Controller extends Controller
{
    public function __invoke(Request $request): void
    {
         $kernel = $app->make(Kernel::class);
         $response = $kernel->handle($request)->send();
         $kernel->terminate($request, $response);
    }
}
Sinnbeck's avatar

@glennutter OK have a gotten a small start. But the big issue is that there are naming collssions in laravels helpers.php. They exist in both, and the names clash: So the laravel3 version needs to have all of its helpers renamed :/

glennutter's avatar

@cwhite This is what index.php looks like from Laravel 3:

define('LARAVEL_START', microtime(true));
$web = true;
require '../paths.php';
unset($web);
require path('sys').'laravel.php';

Pulling that into the Laravel3Controller produces errors that constants and functions has already been defined.

Sinnbeck's avatar

Ok here is what I did to get it started

Put all files in /laravel3

Make a new file in that folder named app.php

$web = true;
require 'paths.php';
unset($web);
require path('sys').'laravel.php';

Then in a legacy controller

require base_path('laravel3/app.php');
Sinnbeck's avatar

Ok easy fix. Add namespace Laravel; at the top of the helpers.php. And next remove most aliases in config/app.php

Sinnbeck's avatar
Sinnbeck
Best Answer
Level 102

Got it working 👍 i removed all aliases and just added the missing use function Laravel/array_get; where it complained

glennutter's avatar

@Sinnbeck You are amazing. Removed Aliases from Laravel 8 or 3? When I remove from 3 I get:

Undefined class constant 'loader'

From:

Event::listen(View::loader, function ($bundle, $view) {

in start.php

glennutter's avatar

@Sinnbeck You are a genius sir. Thank you VERY much for your help. I had to add some null checks in Cookie::get and somewhere else but now the application boots. THANK YOU!

Sinnbeck's avatar

@glennutter ah yes forgot about that one. Did it in the train on my way home, so typed the explanation on my phone 😅

Happy to have helped 👍

1 like
blaceydev's avatar

@Tray2 I was about to suggest this myself as it takes aot o tbe pain away from upgrading legacy code. My approach would be coding up tests for the current working code then upgrade using Laravel Shift and rerun the tests, fixing any issue before merging and deploying to production.

jlrdw's avatar

@glennutter no matter how you proceed back up your data first because if you go doing migrations wrong you could lose all your data, it's happened to I don't know how many people on this forum.

You should be able to piggyback an old application inside a new application working on changing code over a little at a time, I have done this in the past.

1 like
glennutter's avatar

@jlrdw The old Laravel 3 app isn't using migrations, that's something we're looking to add to the project once it's been upgraded.

Not sure what you mean by piggybacking, but a lot of the written code refers to facades and methods on facades that are no longer available in newer Laravel, so it's impossible to just lift in the "business logic" into a newer app without rewriting large chunks of it. That's why we - in a dream world - would be able to run both instances of Laravel at the same time for a while.

1 like
m0stwan1ed's avatar

It will be hard to do, but still it is possible.

Every Laravel version has an "Upgrade guide" chapter, so you can check, if you miss any changes. The best way to do this is to upgrade from version to version (3 -> 3.1 -> 3.2 -> 4.0 -> 4.1 -> 4.2 -> 5.0 -> ... -> 8 -> 9 -> 10) to update the project according to all breaking changes and check if everything is still working.

The hardest thing will be the PHP version update: the latest Laravel 10 version requires PHP 8.1. So, you will need to update the whole application to replace all deprecations and outdated code (from PHP 5.3 to 8.1)

Or you can just rewrite your project on a latest version almost from scratch.

Please or to participate in this conversation.