Upgrading PHP can expose compatibility problems in older Laravel projects. I recently encountered this while running a Laravel 10 application on PHP 8.5.4.
The application was working, but the terminal was filled with deprecation warnings such as:
PHP Deprecated: Monolog\Logger::addRecord():
Implicitly marking parameter $datetime as nullable is deprecated
and:
PHP Deprecated: Illuminate\Log\Logger::__construct():
Implicitly marking parameter $dispatcher as nullable is deprecated
At first, these errors looked like application-level problems. However, the real issue was an outdated dependency stack.
This article explains how I diagnosed and fixed the problem.
The Environment
The project was a Laravel application called samupos.
The initial environment was:
PHP 8.5.4
Laravel 10.25.1
Monolog 3.4.0
HTMLPurifier 4.16.0
The project had originally been built around an older PHP/Laravel ecosystem.
The first important lesson was:
A project can satisfy the PHP version declared in composer.json while still having dependencies that are incompatible with the actual PHP version installed on your machine.
- The Initial Error
When starting the Laravel development server, I received:
PHP Deprecated: Illuminate\Log\Logger::__construct():
Implicitly marking parameter $dispatcher as nullable is deprecated,
the explicit nullable type must be used instead
The warning pointed to:
vendor/laravel/framework/src/Illuminate/Log/Logger.php
I also encountered a similar warning from Monolog:
PHP Deprecated: Monolog\Logger::addRecord():
Implicitly marking parameter $datetime as nullable is deprecated,
the explicit nullable type must be used instead
The important thing here is that both warnings originated from the vendor directory.
That immediately suggested a dependency compatibility problem.
- Check the PHP Version
The first thing to verify was the PHP version:
php -v
The result was:
PHP 8.5.4 (cli)
This was important because newer PHP releases introduce deprecations that older frameworks and packages may not have accounted for.
- Make Sure Composer Is Running in the Correct Directory
Initially, Composer reported:
Composer could not find a composer.json file
The reason was simple: Composer was being executed from:
~/Desktop/Development
instead of the Laravel project directory.
The project was actually located at:
~/Desktop/Development/samupos
So I changed into the project directory:
cd ~/Desktop/Development/samupos
Then verified the project files:
ls
A Laravel project should contain files such as:
artisan
composer.json
composer.lock
app/
bootstrap/
config/
public/
resources/
routes/
vendor/
This is an easy mistake to make, but it is important because Composer searches for composer.json in the current working directory.
- Check the Laravel Version
Next, I checked the installed Laravel version:
composer show laravel/framework | head -15
The result showed:
versions : * v10.25.1
So the project was running Laravel 10.25.1.
- Check the Monolog Version
Next:
composer show monolog/monolog | head -15
The result was:
versions : * 3.4.0
This was another important clue.
The project was using an older Monolog release while running on PHP 8.5.
- Check Platform Requirements
Composer provides a useful command for detecting PHP and extension compatibility problems:
composer check-platform-reqs
Most requirements passed, but PHP itself failed because of an outdated package:
ezyang/htmlpurifier requires php
...
< 8.3.0
This meant that although the project's composer.json allowed PHP 8.5:
"php": "^8.1"
one of the installed dependencies did not support PHP 8.5.
This is a critical distinction.
The root project requirement said:
PHP >= 8.1 < 9.0
but individual packages can have much stricter requirements.
- Find Out Who Requires HTMLPurifier
Instead of guessing, I used Composer to find out which package depended on HTMLPurifier:
composer why ezyang/htmlpurifier
The result was:
phpoffice/phpspreadsheet 1.29.0 requires ezyang/htmlpurifier (^4.15)
So the dependency chain was:
PhpSpreadsheet
↓
HTMLPurifier
↓
PHP version restriction
This showed that HTMLPurifier was not a package I had directly installed. It was an indirect dependency.
- Inspect the Installed HTMLPurifier Version
I then checked:
composer show ezyang/htmlpurifier
The installed version was:
v4.16.0
Its PHP requirements included:
~8.0.0 || ~8.1.0 || ~8.2.0
Therefore PHP 8.5 was outside its declared supported range.
- Find All Packages Blocking PHP 8.5
Composer has another extremely useful command:
composer why-not php 8.5
The result identified several blockers:
ezyang/htmlpurifier v4.16.0
nette/schema v1.2.4
nette/utils v4.0.2
For example:
ezyang/htmlpurifier v4.16.0 requires PHP ... < 8.3
and:
nette/utils v4.0.2 requires PHP >=8.0 <8.4
This gave us a much clearer picture of the dependency problem.
- Don't Immediately Upgrade Laravel
At this point, it would have been tempting to upgrade the entire application from Laravel 10 to a newer major version.
However, that wasn't necessary yet.
The project was already using:
Laravel 10
and its composer.json contained:
"laravel/framework": "^10.23"
The ^10.23 constraint allows Composer to install newer Laravel 10 releases.
Therefore, before performing a major Laravel upgrade, I checked what Composer could already resolve.
- Use Composer's Dry Run
This was one of the most useful steps:
composer update --dry-run
The command doesn't actually modify the project. Instead, it shows what Composer would update.
The output showed that Composer could update Laravel from:
10.25.1
to:
10.50.3
It could also update:
HTMLPurifier 4.16.0 → 4.19.1
PhpSpreadsheet 1.29.0 → 1.30.0
Symfony 6.3.x → 6.4.x
Nette Utils 4.0.2 → 4.1.5
Nette Schema 1.2.4 → 1.3.6
There were also many other dependency updates.
This was exactly what we wanted to see.
It meant the existing Laravel 10 project could move forward without immediately performing a major framework migration.
- Back Up Composer Files
Before making the real changes, I backed up the dependency configuration:
cp composer.json composer.json.backup
cp composer.lock composer.lock.backup
This is a simple but useful precaution when performing a large dependency update.
- Update Dependencies
The actual update was performed with:
composer update -W
The -W option means:
--with-all-dependencies
This allows Composer to update dependencies of your direct dependencies when necessary.
This was important because the problem wasn't isolated to one package.
The dependency tree contained outdated packages throughout the application.
- Why We Didn't Edit the Vendor Files
One of the most important lessons from this debugging process is:
Do not manually edit files inside vendor/.
For example, it might be tempting to open:
vendor/laravel/framework/src/Illuminate/Log/Logger.php
and change the nullable parameter manually.
Similarly, you might modify:
vendor/monolog/monolog/src/Monolog/Logger.php
to silence the warning.
That is not the correct long-term solution.
The next time you run:
composer install
or:
composer update
Composer can overwrite those changes.
The correct approach is to update the dependency that contains the incompatible code.
- Test the Application Again
After updating the dependencies, I ran:
php artisan serve
And the result was:
INFO Server running on [http://127.0.0.1:8000].
Most importantly, the previous deprecation warning was gone.
There was no longer:
Illuminate\Log\Logger::__construct():
Implicitly marking parameter $dispatcher as nullable is deprecated
The application started normally.
- Verify Platform Requirements
After updating the packages, the next step is to run:
composer check-platform-reqs
This verifies that the installed dependencies are compatible with the PHP version and required PHP extensions.
It is a good idea to run this after a major dependency update.
- Check for Security Vulnerabilities
During the dependency update, Composer reported:
Found 92 security vulnerability advisories affecting 22 packages.
That is something that should not be ignored.
The next command to run is:
composer audit
This provides more information about the affected packages and available updates.
Dependency maintenance isn't only about eliminating errors and warnings. Security vulnerabilities are equally important.
- Useful Composer Commands for Debugging
Here are some Composer commands that proved particularly useful during this process.
Check installed package
composer show package/name
Example:
composer show monolog/monolog
Find out why a package is installed
composer why package/name
Example:
composer why ezyang/htmlpurifier
Find what blocks a PHP version
composer why-not php 8.5
Check platform requirements
composer check-platform-reqs
See available updates
composer outdated
Preview an update
composer update --dry-run
Update dependencies
composer update -W
Check security vulnerabilities
composer audit
These commands are extremely useful when debugging Laravel dependency problems.
Conclusion
The original problem wasn't caused by the Laravel application code itself.
The underlying problem was an outdated dependency tree running on a much newer PHP version.
The environment looked like this:
PHP 8.5.4
↓
Laravel 10.25.1
↓
Monolog 3.4.0
↓
HTMLPurifier 4.16.0
↓
PHP compatibility/deprecation problems
After updating the dependencies:
PHP 8.5.4
↓
Updated Laravel 10 dependencies
↓
Updated Monolog
↓
Updated HTMLPurifier
↓
Compatible dependency tree
↓
Laravel application starts normally
The biggest lesson is simple:
When PHP starts reporting deprecation warnings from the vendor directory, don't immediately edit the vendor files. Investigate the dependency versions and let Composer resolve the compatibility problem.
Commands such as:
composer why
composer why-not
composer outdated
composer update --dry-run
composer check-platform-reqs
composer audit
can turn what looks like a mysterious Laravel error into a traceable dependency problem.
Top comments (0)