mailwal

🚚 php2shell cleanup, Marko beats Laravel, the cache that ignores cache:flush

Mark at M.academy

Read by 11,000+ Magento developers. Send to a friend.

October 27, 2026

PHP: Now what?

Hey John,

This last week of PHP was crazy. When php2shell dropped, it felt like the entire world finally found out what PHP was 😂

But the question showed up right away: now that PHP is patched... what do you do next? You need to know what to rotate and look for after you patch.

That's below, along with a new benchmark from my Marko framework that showed it being faster than Laravel on a real app, along with a Magento caching faux pas you may not know about 👇

Cheers,

Mark Shust

Teacher / M.academy

Say hi 👋 on LinkedIn & YouTube

Security

php2shell: what to do after patching

php2shell could potentially let attackers run code on your servers without logging in, aka RCE -- "remote code execution". This was a really horrible exploit.

I wrote a new blog article about what you need to know about php2shell. It covers how the exploit works, and steps to mitigate the vulnerability. I hope that by now, all of your installs have been patched for days (they have been... right? right!?).

Soon after php2shell got announced, Nick asked a great question over on Campus: what do we do after all of our servers are patched up? Do we need to rotate keys or creds like we did with the StyleSmuggler exploit, or do anything else? This is a great question, as Adobe really hasn't put out much guidance here either.

Well, patching closes the door, but it doesn't kick out anyone who already got in when your servers were still exposed. So it comes down to how exposed you were, and how you handled the update:

  • mod_php is the setup where full remote code execution was confirmed, so treat it as very high risk
  • luckily, php-fpm is lower risk -- but it's still listed as affected
  • Cloudflare's WAF rules helped a ton and were deployed before the PHP patch was released, but only worked if your origin refused traffic that didn't come through Cloudflare

If your server was reachable and unpatched in that window, the standard security viewpoint is to assume it was compromised. That's not really the answer any of us want to hear, as it's typically quite a bit of work to stand up a new server and rotate all of the credentials around. But, we won't really know how many sites were truly exposed for some time, and it was rumored that some sites that likely got exploited were trojan-horsed with a script that may not execute until a later point in time.

I personally think not many sites were hacked, especially ones which were patched quickly. But this doesn't mean that your site wasn't hit. So, I'd likely recommend a pragmatic, due diligence process here:

  • Grep around your server to see if there's anything new, like a random PHP file in pub/, files that aren't versioned, or new db records you didn't create like admin users, crons, or updates to cms blocks (classic skimming attacks)
  • Also look around the server for non-PHP files. Hackers are smarter now, so there may be a random python or shell script laying around -- all it takes is a single file laying dormant someplace that can do damage
  • Since this is a full RCE attack, the worst kind of damage would be SSH access, which provides a hacker with full system access, so checking ~/.ssh/authorized_keys and possibly rotating existing SSH key access are some low-hanging fruit items that likely reap the largest benefits, as an attacker would be able to remotely access your server this way -- even if no files were left around on it

If you're in a max-security environment, I don't think you have any other choice but to stand up new servers, pass your db through a full auditing process, and ensure everything is patched from day 1 and according to best practices.

Given that Black Friday is only a few weeks away, this is very unfortunate timing -- but I'm not sure we are in the clear yet. There have been so many hacks lately on all languages and platforms, not just PHP, so be prepared to work overtime leading up to and perhaps even over the holidays, as an attack is very likely to happen right before or during Black Friday week.

It's a great time to be a developer... amiright? 🫠

I also wrote up what the bug actually was in plain PHP terms, plus the hardening steps for after you update. It’s worth going through it even if your servers look clean:

Side project

Marko is now faster than Laravel

Back in April, I shipped version 0.1 of M̶a̶g̶e̶n̶t̶o̶ ̶3̶ Marko, a brand new PHP framework. It takes the best parts of Magento and Laravel, and drops everything we hate about them.

Well, that was months ago, and Marko is now at version 0.11. It's the same framework, but also very different.

A well-known Magento and PHP developer just ported over their real Laravel 13 app to Marko, and they benchmarked the two side-by-side on the same server specs: PHP 8.5, Postgres, and Redis. Here are the results:

  • 1.2 to 1.3x more requests per second
  • about 2/3 of the CPU time per request
  • about half the memory (35 MiB vs 66 MiB)
  • a CLI that starts 6x faster (130ms vs 774ms)
  • a vendor dir that is 16 MB vs 60 MB

On 0.8.5, that same app was twice as slow as Laravel. But route and module cache both shipped in 0.9, and it closed a huge gap. Now that Marko is faster than Laravel, they're moving their site over to it! 🏆

All that said: why should you care as a Magento developer? Well, it's because Marko is innovating and shipping, and other frameworks are stagnant and going stale. It's also because you already know how Marko works with the concepts from Magento such as app hierarchy fallback layer, plugins, and database schemas. Marko builds on top of these concepts, but takes it all a step further by dropping the entire XML layer, making things simpler and easier to understand, and also includes first-class AI agent support out of the box.

If you looked at it in April as a fun project and then just moved on from it, it may be time for another look. It's maturing fast, and I'll soon be building an entire eCommerce framework on top of it (Marko is sort of like the "Zend" part of Magento, so it still needs the "Magento" layer).

Watch the intro to Marko on YouTube, or check out the docs to learn all about it:

Under the hood

Old code still being served after deployment?

You’ve probably hit this one: you deploy, run bin/magento cache:flush, ...and the site keeps serving the old code.

That’s because the cache actually running your PHP isn't a Magento cache at all, and cache:flush never touches it. It comes from OPcache.

Every time PHP runs a file, it compiles it into opcodes first, and OPcache keeps those in a shared memory, so PHP-FPM doesn’t recompile your code on every request.

By default, PHP checks your files for changes every 2 seconds. But Adobe’s own performance recommendations tell you to set opcache.validate_timestamps=0 in production to disable this check. Maybe that's the right call for optimal speed, but it also means that PHP never looks at your files for updates again. You can deploy all day long, and FPM will keep serving the code it already compiled until the server is restarted, or OPcache is manually reset.

It gets sneakier with symlink deploys (with deploys in directories like releases/20261027-01). PHP also caches the resolved path of that symlink for 120 seconds by default (realpath_cache_ttl), so FPM can keep serving the OLD release for a couple of minutes, even after you swap out the link.

And running opcache_reset() from the terminal doesn’t help either. The CLI has its own memory, completely separate from FPM. It’s the same trap we got in with php2shell with checking if PHP is upgraded with "php -v". That command will show you the CLI version, not the version that actually serves up your pages.

Here’s what to do after every production deploy:

  • reload PHP-FPM after the new code lands, so it drops the old compiled code
  • if you deploy with symlinks, pass $realpath_root as SCRIPT_FILENAME in your nginx config, so every request resolves the new release
  • check your OPcache settings from a real web request (opcache_get_status() in a temp file you delete right after), not "php -i", because the CLI can load a different php.ini than FPM
  • then run bin/magento cache:flush, because Magento’s cache and OPcache are two different things, and a deploy needs both cleared

M.academy update

AI development workflow course

I'm heads-down building out my next course, which is about how to create a custom AI-driven development workflow for Magento. My own process has gotten so good that I haven't written any code myself in about 18 months, and haven't even looked at code in about 5 months -- while consistently shipping to production and not breaking anything (my custom course platform is used by 16,000+ developers, and everything that I ship to it runs through this workflow).

It seems illogical to ship code to prod without reviewing it, but this is very, very possible today, as long as the proper workflow is created with a checks-and-balances system that contains orchestrated steps that guide code through the entire development process. This is how I see everyone working in 2027 and beyond, and I'd love to teach you how to do this too.

This is what I'm working on right now. If you'd like to see anything in this course, let me know. I don't yet have an ETA on when it will be released, but I'm hoping to share a progress update with everyone really soon. As always, it will be a part of the University membership, so if you are a University student, you'll get access to this course right when it comes out at no extra charge.

The University

Get unrestricted access to everything Magento

Includes all 20 Magento courses available, and every new course as it ships. Plus the Magento AI tutor... and Campus. All in one subscription, and you'll always know the next thing to learn.

If you are a developer, how have you been dealing with php2shell? Any gotchas?

Just hit reply and let me know -- it may wind up in either a future issue or a lesson in a course.

M.academy
8940 Sassafras Cir, N. Royalton, OH 44133