0

I have a php server with Symfony (and API Platform) and I've just created a file upload system with Vich Uploader bundle. I have an Assert attribute on my File property that restricts its maximum size to 5MB.

The problem is that if I enter a file larger than 5MB but also larger than the post_max_size value in php.ini, the server can't process the request and returns a 500 error.

[Web Server ] PHP Warning: POST Content-Length of 27052288 bytes exceeds the limit of 8388608 bytes in Unknown on line 0 [Web Server ] :00","message":"/api/documents"} [Web Server ] Fatal error: Allowed memory size of 268435456 bytes exhausted (tried to allocate 81153520 bytes) in C:[...]vendor\symfony\http-kernel\Profiler\FileProfilerStorage.php on line 150 [Web Server ] PHP Stack trace: [many lines of the stack trace]

I looked for a solution but the only one I found was to increase the value of post_max_size. But I think it's a workaround to do that, since all you have to do is enter a file heavier than this limit to bug the server.

So does anyone have a real solution (that doesn't involve increasing values in php.ini) that return a php error (422?) without having memory problems and so on?

For now, I've created an EventListener to return an 422 error :

namespace App\EventListener;

use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\RequestEvent;

class MaxPostSizeListener
{
    public function onKernelRequest(RequestEvent $event): void
    {
        if (!isset($_SERVER['CONTENT_LENGTH'])) {
            return;
        }

        $contentLength = (int) $_SERVER['CONTENT_LENGTH'];
        if ($contentLength > $this->getPostMaxSize()) {
            $errorMessage = 'La taille du fichier est trop grande.';
            $response = new Response(json_encode(['message' => $errorMessage]), Response::HTTP_UNPROCESSABLE_ENTITY);
            $response->headers->set('Content-Type', 'application/json');
            $event->setResponse($response);
        }
    }

    private function getPostMaxSize(): int
    {
        $maxSize = ini_get('post_max_size');
        if (!preg_match('/^(\d+)(.)$/', $maxSize, $matches)) {
            return 0;
        }

        $value = (int) $matches[1];
        switch (strtoupper($matches[2])) {
            case 'G':
                $value *= 1024;
            // no break
            case 'M':
                $value *= 1024;
            // no break
            case 'K':
                $value *= 1024;
        }

        return $value;
    }
}

But I still have the memory problem and the multiple lines in the terminal.

Stov
  • 29
  • 5
  • If you don't limit `post_max_size`, then PHP needs to parse the full request, before it could decide whether individual parts of your multipart request might be larger than the upload file size limit you want to impose. The point of this option is to let PHP reject the request in a really early phase, before it starts to do any "heavy lifting." Parsing the request and populating $_POST / $_FILES happens before your own custom code even runs. – CBroe Jul 04 '23 at 09:49
  • @CBroe Yes, I understand that PHP blocks the request. But I don't understand why I then have this problem of allowed memory exhausted and an incoherent response. Isn't there a way to avoid this memory problem and return a custom error? – Stov Jul 04 '23 at 09:59
  • Ok, but why wasn't the request blocked? Because I got this message at the beginning: "PHP Warning: POST Content-Length of 27052288 bytes exceeds the limit of 8388608 bytes in Unknown on line 0". – Stov Jul 04 '23 at 11:47
  • Looks like I was wrong, PHP does not cancel the request per default, but only issues a warning. That fatal error you got initially, must have been only tangentially related then. – CBroe Jul 04 '23 at 11:52
  • I think PHP truncates POST data here, which causes my code to bug. How can I get around this? – Stov Jul 04 '23 at 12:37
  • 1
    You should not get "partial" data in such a case, but rather nothing at all - https://www.php.net/manual/en/ini.core.php#ini.post-max-size: _"If the size of post data is greater than post_max_size, the $_POST and $_FILES superglobals are empty. This can be tracked in various ways, e.g. by passing the $_GET variable to the script processing the data, i.e.
    , and then checking if $_GET['processed'] is set."_
    – CBroe Jul 04 '23 at 12:39
  • Ok thanks. So I just have to check if the superglobal $_POST is empty and if it is, I return a JsonResponse and do exit(). – Stov Jul 04 '23 at 12:55
  • ^ No, if the file size exceeds the allowed size then this would be traceable in the errors inside [$_FILES](https://www.php.net/manual/en/features.file-upload.errors.php). So you would need the check this part of the array in order to determine if the file was to big. The fatal error you are receiving has nothing to do with the upload though – DarkBee Jul 04 '23 at 12:56
  • But $_FILES is empty so i can't check parts of it. – Stov Jul 04 '23 at 13:12
  • Did you set the `enctype` attribute on the form? `
    `?
    – DarkBee Jul 04 '23 at 13:17
  • I don't have a frontend because it's an API, but I did create a form-data using Postman. But I've found a better solution, I get the Content-Length from the header of the request, then I get the value of post_max_size in php.ini. I set the two values to the same unit and compare them. If the Content-Length value is greater than the post_max_size value, I return a 422 and press exit(). – Stov Jul 04 '23 at 13:32

0 Answers0