Welcome to frameworkless PHP where code & user files are stored in the same root and any PHP file requested by a web client is executed by the server.
In most proper frameworks, including PHP ones, the only thing responding to web requests is an entrypoint file (that gets passed the request metadata including URL) and the framework takes it from there. This means that with proper configuration, even requesting a malicious PHP file shouldn't actually execute it and instead hit the framework which will promptly respond with a 404 (of course, with PHP the danger is that in case of misconfiguration the server may still prioritize an exact path match and execute the file rather than defaulting to executing the framework's entrypoint, where as other languages typically don't rely on the webserver to execute the files and couldn't run a malicious file even if they tried).
But these stupid legacy applications are still around and haven't been updated to fix this design flaw, so any flaw in sanitizing uploaded files turns into a persistent RCE. I'm sure some people will pitch in and say this isn't a design flaw and you're using it wrong, and while I agree that it can probably be made secure with enough effort, why leave such a loaded footgun around when this is essentially a solved problem in all other languages?
In other languages a malicious file being uploaded to the web root will at best result in a stored XSS which can be further mitigated by having your file uploads on a separate domain, but in PHP it's fatal.
> the server may still prioritize an exact path match and execute the file rather than defaulting to executing the framework's entrypoint
This is properly solved by frameworks having this entrypoint be in a ‘public’ folder and that also being the webroot, so only index.php and nothing else is available for a direct match (unless /../ in the url works, which would be a huge security hole).
In most proper frameworks, including PHP ones, the only thing responding to web requests is an entrypoint file (that gets passed the request metadata including URL) and the framework takes it from there. This means that with proper configuration, even requesting a malicious PHP file shouldn't actually execute it and instead hit the framework which will promptly respond with a 404 (of course, with PHP the danger is that in case of misconfiguration the server may still prioritize an exact path match and execute the file rather than defaulting to executing the framework's entrypoint, where as other languages typically don't rely on the webserver to execute the files and couldn't run a malicious file even if they tried).
But these stupid legacy applications are still around and haven't been updated to fix this design flaw, so any flaw in sanitizing uploaded files turns into a persistent RCE. I'm sure some people will pitch in and say this isn't a design flaw and you're using it wrong, and while I agree that it can probably be made secure with enough effort, why leave such a loaded footgun around when this is essentially a solved problem in all other languages?
In other languages a malicious file being uploaded to the web root will at best result in a stored XSS which can be further mitigated by having your file uploads on a separate domain, but in PHP it's fatal.