Matt Gibson
Matt Gibson

Reputation: 38238

Stream from a temporary file, then delete when finished?

I'm writing a temporary file by running a couple of external Unix tools over a PDF file (basically I'm using QPDF and sed to alter the colour values. Don't ask.):

// Uncompress PDF using QPDF (doesn't read from stdin, so needs tempfile.)
$compressed_file_path = tempnam(sys_get_temp_dir(), 'cruciverbal');
file_put_contents($compressed_file_path, $response->getBody());  
$uncompressed_file_path = tempnam(sys_get_temp_dir(), 'cruciverbal');
$command = "qpdf --qdf --object-streams=disable '$compressed_file_path' '$uncompressed_file_path'";
exec($command, $output, $return_value);

// Run through sed (could do this bit with streaming stdin/stdout)
$fixed_file_path = tempnam(sys_get_temp_dir(), 'cruciverbal');
$command = "sed s/0.298039215/0.0/g < '$uncompressed_file_path' > '$fixed_file_path'";
exec($command, $output, $return_value);

So, when this is done I'm left with a temporary file on disk at $fixed_file_path. (NB: While I could do the whole sed process streamed in-memory without a tempfile, the QPDF utility requires an actual file as input, for good reasons.)

In my existing process, I then read the whole $fixed_file_path file in as a string, delete it, and hand the string off to another class to go do things with.

I'd now like to change that last part to using a PSR-7 stream, i.e. a \Guzzle\Psr7\Stream object. I figure it'll be more memory-efficient (I might have a few of these in the air at once) and it'll need to be a stream in the end.

However, I'm not sure then how I'd delete the temporary file when the (third-party) class I'd handed the stream off to is finished with it. Is there a method of saying "...and delete that when you're finished with it"? Or auto-cleaning my temporary files in some other way, without keeping track of them manually?

I'd been vaguely considering rolling my own SelfDestructingFileStream, but that seemed like overkill and I thought I might be missing something.

Upvotes: 1

Views: 1989

Answers (1)

mpen
mpen

Reputation: 282865

Sounds like what you want is something like this:

<?php

class TempFile implements \Psr\Http\Message\StreamInterface {

    private $resource;

    public function __construct() {
        $this->resource = tmpfile();
    }

    public function __destruct() {
        $this->close();
    }

    public function getFilename() {
        return $this->getMetadata('uri');
    }

    public function getMetadata($key = null) {
        $data = stream_get_meta_data($this->resource);
        if($key) {
            return $data[$key];
        }
        return $data;
    }

    public function close() {
        fclose($this->resource);
    }

    // TODO: implement methods from https://github.com/php-fig/http-message/blob/master/src/StreamInterface.php
}

Have QPDF write to $tmpFile->getFilename() and then you can pass the whole object off to your Guzzle/POST since it's PSR-7 compliant and then the file will delete itself when it goes out of scope.

Upvotes: 1

Related Questions