phpnginxdebianfpm

PHP 7.4 FPM under Nginx serves wrong class - cache mixup?


We have a Debian Linux environment serving a few different applications, all written in PHP 7.4. They are served out of a single Nginx web server using virtual hosts and are all using FPM. Nothing special here.

In our code, we are using a JSON-RPC client class (@package JsonRPC, @author Frederic Guillot), which is a wrapper around PHP cURL. For historical reasons, the Client.php class exists in 2 versions within the different applications. These are obviously loaded from 2 different physical locations (document roots) in the file system, lets say /var/www/appA/classes/Client.php and /var/www/appB/classes/Client.php.

To our big surprise, we have found that FPM sometimes serves the Client.php version that is from app A when the request is really for app B! We found out about it because the app B application threw an exception, and from the stack trace we could see that Client.php was loaded from a path under app A.

To rule out local browser caching issues, or caching elsewhere along the request pipeline, we inserted debug statements in one of the classes only - the class that should have been loaded. These debug messages did not show up, until after the FPM service was restarted.

Further evidence of the problem is that when the FPM service is restarted the problem goes away and the correct version is loaded. For a while at least ... the issue comes back by itself after some hours, or the next day, presumably after the other application has been used.

A hypothesis is that the two applications each receive a request almost simultaneously, and that they are served in sequence by the same FPM worker process, and the cache logic mistakenly finds that the Client.php has already been loaded (however from another path and the wrong version).

The two versions of Client.php are using the same PHP namespace, but differ within the PHP code. Could that be the reason for FPM mixing them up? One would think that there would be a check of hashes on the contents of the file which would detect that the files are indeed different.

Are there any known restrictions or precautions on how to deal with identically named classes but in different document roots when run under FPM?

As a final note, it seems that this could potentially present a serious security issue, but let us save that discussion for another time.


Solution

  • Found the answer myself.

    It turns out that "Global Namespace Collision" is a thing in PHP, that has been known for years. This article philsturgeon.com/global-namespace-class-collisions-in-php talks exactly about the problem I am having.

    The quick fix is to make sure no two classes with the same name are ever loaded into the same PHP instance that is using opcode caching, such as FPM.

    And, sure enough, I just renamed one of the classes, and the problem went away. We learn something new every day ... ;-)