Upgrading PHP and Laravel: What Could Possibly Go Wrong?
What started as a worldwide PHP security alert turned into a little adventure involving security patches, dependencies, Git branches, compatibility checks and lots of testing. Here's how I approached upgrading different projects, took extra precautions and used AI along the way. The mission? Fix the vulnerabilities without giving production a new problem. ☕
So, What Happened to PHP?
On 21 October 2026, the PHP development team released emergency security updates addressing two critical vulnerabilities, collectively known as PHP2Shell.
The vulnerabilities, identified as CVE-2026-109618 and CVE-2026-109619, could potentially allow attackers to execute malicious code remotely on vulnerable servers without authentication.
Both received a CVSS v4.0 severity score of 9.2.
That's pretty serious.
The PHP team released the following security updates:
| PHP Branch | Fixed Version |
|---|---|
| PHP 8.2 | 8.2.35 |
| PHP 8.3 | 8.3.36 |
| PHP 8.4 | 8.4.27 |
| PHP 8.5 | 8.5.12 |
PHP 8.1 and older versions were already outside upstream security support, making older applications another concern.
The vulnerabilities were found in the PHP runtime itself, not specifically in Laravel application code.
This means that even an application with perfectly working features could still be running on a vulnerable PHP version.
And since I work with several PHP and Laravel projects, this was something I needed to look into.
Suddenly, checking PHP versions became a little more urgent than usual.
For anyone interested in the technical details, here are the official PHP security announcements.
First, Which Projects Are Affected?
The first thing I did was review the PHP versions used across the projects I currently handle.
And here's where things got interesting.
Not every project was running the same version.
Some were already using relatively recent PHP and Laravel versions, making the security update fairly straightforward.
Others were running older combinations, including projects using PHP 8.0 and Laravel 9.
For those older projects, upgrading required more planning because the application dependencies might not support newer PHP or Laravel versions immediately.
I also had to distinguish between two different goals.
Goal 1: Apply the necessary security patches.
Goal 2: Plan a larger PHP and Laravel upgrade.
These are related, but they are not necessarily the same task.
For example, if a project is running PHP 8.2.28, the immediate security priority is to move to a fixed PHP 8.2 release such as PHP 8.2.35.
That doesn't automatically mean the application needs to jump straight to PHP 8.5 and Laravel 13 on the same day.
Of course, a future upgrade is still important, especially when a PHP branch is approaching the end of its support lifecycle.
But for me, separating the security patch from the major framework upgrade made the whole process more manageable.
Not Every Project Needs the Same Upgrade Plan
This was probably one of the biggest things I noticed.
Some projects were relatively easy to update because they weren't too far behind.
Others needed more attention because of older dependencies, deprecated functionality or compatibility requirements.
I also have colleagues handling more complicated upgrade situations.
One of them was dealing with a project using Laravel 8, along with an older PHP version and an Ubuntu environment that also needed upgrading.
That's a much bigger scope.
Fortunately, the projects I was responsible for didn't require that level of infrastructure migration.
My focus was mainly on PHP updates, Laravel compatibility, dependency management and application testing.
Still enough things to check, though. 😆
Before Touching Anything, Check the Current Environment
I started by checking the existing environment.
A few basic commands can tell quite a lot.
php -v
composer --version
php artisan --versionThen I reviewed the project's composer.json and composer.lock files.
These helped me understand the PHP requirements, Laravel version and package dependencies.
I also checked the server environment, especially which PHP version was being used by the web server.
One thing worth remembering is that PHP CLI and PHP FPM are not necessarily using the same version.
Running php -v only tells us which PHP version the command line is using.
For an application served through Nginx, I also need to check the PHP FPM service and its configuration.
Because updating PHP in the terminal doesn't automatically mean the website is using the updated runtime.
Small detail, but an important one.
The Upgrade Plan: One Phase at a Time
Instead of upgrading everything at once, I preferred breaking the work into smaller phases.
For one of the projects, the plan was to first update the existing PHP branch to address the security vulnerabilities.
Once that was stable, we could plan the next phase involving newer PHP and Laravel versions.
The general idea was:
Phase 1: Security updates
Apply the required PHP security patches, verify the runtime and make sure the existing application continues to work.
Phase 2: Compatibility assessment
Review the current Laravel version, Composer dependencies, third party packages and any deprecated functionality.
Phase 3: Major version upgrade
Move towards the target PHP and Laravel versions through supported upgrade paths.
Phase 4: Testing and deployment
Run application tests, fix compatibility issues, verify the staging environment and prepare for production.
The exact upgrade path depends on the project.
For older applications, this might involve several intermediate upgrades instead of jumping directly to the latest version.
I found this approach easier to manage because each phase had a specific objective.
And when something broke, I had a smaller set of changes to investigate.
My Tech Lead Shared an Upgrade Guide
Another helpful part of this process was a Markdown file shared by my tech lead.
The .md file outlined a structured approach to performing the upgrade.
Check the current state.
Understand the requirements.
Upgrade in phases.
Resolve compatibility issues.
Test.
Continue to the next phase.
Having the steps documented made the process more consistent, especially when working across multiple projects.
Instead of figuring out the entire workflow again for every application, I could use the document as a reference.
It also became useful when working with AI, because I could provide a clear set of instructions and expectations rather than simply asking it to upgrade everything.
Testing Locally Before Production
I prefer testing upgrades in my local development environment before applying changes to a live server.
For major upgrades, I work with a dedicated Git branch.
For example:
git checkout -b upgrade/laravel-13This keeps the upgrade changes separate from the main development work.
It also gives me the flexibility to investigate issues, modify dependencies and test changes without affecting the existing application branch.
Before making significant changes, I make sure the current code is committed and that any necessary backups are available.
For dependency upgrades, I also pay attention to the changes made to composer.json and composer.lock.
I don't want to update a package simply because a newer version exists.
I want to understand why the package needs updating and whether the new version is compatible with the application.
Once the upgrade is applied, I start testing.
And by testing, I don't mean just opening the homepage and seeing whether it loads.
Does the Application Actually Still Work?
This part matters a lot.
A successful Composer installation doesn't necessarily mean the application is working correctly.
The same goes for a successful PHP upgrade.
Depending on the project, I need to check things like:
- Authentication and login
- CRUD operations
- Forms and validation
- File uploads
- API integrations
- Scheduled tasks
- Database operations
- PDF and Excel exports
- User roles and permissions
For example, one of the applications I work with includes scheduled processes that update student information based on academic semesters.
Even if the website loads successfully, I still need to make sure background processes like these continue to behave correctly.
I also check the application logs whenever something behaves unexpectedly.
Laravel logs can be particularly helpful for identifying errors that aren't immediately visible in the browser.
For production deployment, I also verify the PHP FPM service, Nginx configuration and application functionality after applying the changes.
The goal is to make sure the application is actually using the intended PHP version and continues operating as expected.
A Few Extra Precautions
Since the security alert involved vulnerabilities in the PHP runtime, I wanted to be more careful about the deployment process.
Some of the precautions I considered important were:
1. Check the current state before making changes
Know the existing PHP version, Laravel version, dependencies and server configuration.
2. Keep backups and rollback options available
Especially before changing server packages or application dependencies.
3. Separate security patches from major upgrades where practical
Apply urgent fixes without unnecessarily expanding the scope of changes.
4. Verify both CLI and web runtime versions
Because the terminal and web server may be using different PHP installations.
5. Test important application features
A working homepage isn't enough.
6. Review logs after deployment
Look for unexpected application errors, PHP warnings and service issues.
7. Keep upgrade changes isolated
Use dedicated branches and only merge changes after testing and approval.
One important lesson from this security incident is that temporary workarounds are not a complete replacement for the official PHP patches.
If a system cannot be patched immediately, its exposure needs to be assessed and appropriate temporary protections considered.
Where AI Came Into the Picture
AI has become a pretty useful part of my development workflow.
And during these upgrades, I found it especially helpful.
Not because I could simply tell AI to upgrade a project and forget about it.
I still needed to understand what was changing.
But AI helped with several parts of the process.
Understanding dependency conflicts
Sometimes Composer would report that certain packages couldn't be installed because of version conflicts.
Instead of spending too much time trying to interpret every dependency error, I could use AI to help identify which packages were blocking the upgrade.
From there, I could check the package requirements and decide what needed to change.
Reviewing the upgrade plan
I could provide the current PHP version, Laravel version and dependency information, then ask AI to help identify a reasonable upgrade path.
This was particularly useful when working with projects that weren't already close to the target version.
Investigating errors
When something failed after an upgrade, I could share the relevant error message and ask AI to explain possible causes.
Sometimes it was a compatibility issue.
Sometimes a missing PHP extension.
Sometimes a configuration problem.
And sometimes, as usual, Linux file permissions decided to join the conversation. 😆
Following a structured process
The Markdown guide shared by my tech lead was useful here too.
By providing AI with a documented workflow, I could ask it to follow specific phases and focus on one task at a time.
That helped keep the process organised.
But I still reviewed the suggested changes and verified the results myself.
AI can make a convincing suggestion that doesn't necessarily fit the actual project.
So I treat it as an assistant, not the final authority.
For me, the workflow is:
Plan → Ask AI → Review → Apply → Test → Verify
And repeat when necessary.
Knowing When to Stop
One thing I learned is that reaching the latest version isn't always something that should happen in a single deployment.
For some projects, applying the required security patch is the immediate priority.
A larger PHP or Laravel upgrade can be planned separately.
For others, moving to a newer supported stack might be the better long term solution, especially if the existing PHP version is already unsupported.
The important part is understanding the difference between what needs to happen urgently and what requires more planning.
This also makes it easier to communicate the work to clients and stakeholders.
Instead of saying:
"We're upgrading everything."
I can explain which phase addresses the immediate security concern and which phase focuses on longer term improvements.
What I Learned From This
This whole experience reminded me that maintaining an application is just as important as building new features.
We spend a lot of time developing new functionality, fixing bugs and improving user experience.
But the underlying technology also needs attention.
A project might be working perfectly from the user's perspective while still running outdated dependencies or a vulnerable runtime.
The recent PHP security incident was a good reminder of that.
For me, the biggest takeaways were:
- Keep track of the versions used across projects.
- Don't wait until a major incident to review outdated dependencies.
- Treat urgent security patches and major framework upgrades as separate decisions.
- Make changes in controlled phases.
- Test locally before moving to staging and production.
- Keep rollback options ready.
- Use AI to support the process, but verify its suggestions.
I'm still learning a lot about deployment, infrastructure and application maintenance.
And while my upgrade tasks weren't as complicated as a complete operating system and server migration, they still gave me valuable practical experience.
Sometimes the most useful lessons don't come from building something new.
They come from maintaining something that already exists.
And making sure it keeps working.
Because updating PHP is one thing. Keeping production happy afterwards? That's the real assignment. ☕
–SA
References
- PHP Official Security Announcements, October 2026
- NACSA Malaysia: Critical Vulnerabilities in PHP
- [CERT EU: Critical Vulnerabilities in PHP](https://cert.europa.eu/publications/security-advisories/2026
