Search code examples
phplaminas

How can I output a php image resource using Laminas MVC?


I currently output an image like this (inside a controller class):

    ob_start();
    header("Content-Type: image/gif");
    imagegif($image_resource);
    imagedestroy($image_resource);
    $content_string = ob_get_clean();
    $this->getResponse()->setContent($content_string);

Is there a better way without actually capturing the output buffer? since this is not very testable...


Solution

  • The Only way to capture the image without output-buffer is to write the image to a file:

    $tmpFile = tmpfile();
    $path = stream_get_meta_data($tmpFile)['uri'];
    imagegif($image_resource, $tmpFile);
    $content_string = file_get_contents($path);
    imagedestroy($image_resource);
    fclose($tmpFile);
    
    $this->getResponse()->setContent($content_string);
    

    I created a temporary file with the tmpfile() method, and got the filename via stream_get_metadata() function. Then I loaded the content of the file into the string variable and closed the filehandle to the tmpfile so it gets removed.

    A more optimized way from the comments proposed writing to a php://memory file instead. The is a small explanation in another question on when to use php://memory and php://temp

    // open in memory file
    $handle = fopen('php://memory', 'r+');
    imagegif($im, $handle);
    // reset file handle
    fseek($handle, 0);
    
    // get filesize
    $stat = fstat($handle);
    $size = $stat['size'];
    
    // read the created file into variable
    $fileContent = fread($handle, $size);
    fclose($handle);
    
    // write filecontent to the response object
    $this->getResponse()->setContent($content_string);
    

    Personally i would use the output buffer but move the code into a separate function or class. That way you can isolate and mock its behavior for testing purposes:

    /** @var GDImage|resource $image */
    function getImage($image): string
    {
       ob_start();
       imagegif($image);
       $returnData = ob_get_contents();
       ob_end_clean();
       imagedestroy($image);
       return $returnData;
    }
    $content_string = getImage($image_resource);
    $this->getResponse()->setContent($content_string);
    

    In any way i would advise you to set the header on the response object instead of writing it manually:

    $this->getResponse()->getHeaders()->addHeaders([
        'Content-Type' => 'image/gif',
    ]);
    $this->getResponse()->setContent($content_string);
    

    This way the headers get output to the browser by the framework at the correct time and you will not have any problems with output buffering.