Create a New Writer How to write new writer classes

Introduction

The SetaPDF 2 core component already ships several writer classes for nearly all possible situations.

Anyhow, situations could occur where an individual implementation of a writer could be a smarter solution. For example a writer could directly write to a response object of a PHP framework which already handles the management of response headers.

Write Your Own Writer Class

Nearly all method names of the SetaPDF_Core_Writer_WriterInterface are self-explanatory. There's a start()-method which will be called when the writing process starts. A finish()-method which will be called when the writing process ends, a getStatus()-method which returnes status codes and a getPos()-method to received the current byte offset position of the writer.

A simple writer, that forwards the output to a response object of type MyResponse could look like this:

PHP
class MyWriter implements \SetaPDF_Core_Writer_Interface
{
    /**
     * The response object
     *
     * @var MyResponse 
     */
    protected $_response;
    
    /**
     * The current position
     *
     * @var int
     */
    protected $_pos = 0;
    
    /**
     * The current status
     *
     * @var int
     */
    protected $_status = \SetaPDF_Core_Writer::INACTIVE;
    
    /**
     * Let's pass the response object to the writer
     *
     * @param MyResponse $response 
     */
    public function __construct(MyResponse $response)
    {
        $this->_response = $response;
    }
    
    /**
     * The output starts
     */
    public function start()
    {
        // Let's clean the output buffer
        $this->_response->cleanBody();
        // Set the status
        $this->_status = \SetaPDF_Core_Writer::ACTIVE;
    }
    
    /**
     * Write content to the response object
     *
     * @param string $s 
     */
    public function write($s)
    {
        // Add the new string to the response body
        $this->_response->appendBody($s);
        // Let's update the position
        $this->_pos += strlen($s);
    }
    
    /**
     * Implement the finish()-method
     */
    public function finish()
    {
        $this->_status = \SetaPDF_Core_Writer::FINISHED;
    }
    
    /**
     * Implement the getStatus()-method
     *
     * @return int
     */
    public function getStatus()
    {
        return $this->_status;
    }
    
    /**
     * Implement the getPos() method
     *
     * @return int
     */
    public function getPos()
    {
        return $this->_pos;   
    }
}

The HTTP response headers, for example, have to be set in the controller then. Anyhow this also could be integrated into the writer class as well:

PHP
class MyWriterWithHeaders extends MyWriter
{
    /**
     * The filename
     *
     * @var string 
     */
    protected $_filename = 'document.pdf';
    
    /**
     * Let's pass the response object to the writer
     *
     * @param MyResponse $response 
     * @param string $filename 
     */
    public function __construct(MyResponse $response, $filename = 'document.pdf')
    {
        parent::__construct($response);
        $this->_filename = $filename;
    }
    
    /**
     * Implement the finish()-method which also sets the headers
     */
    public function finish()
    {
        $filename = SetaPDF_Core_Writer_Http::encodeFilenameForHttpHeader($this->_filename);
        
        // Let's add the headers
        $this->_response
            ->setHeader('Content-Type', 'application/download')
            ->setHeader('Content-Disposition', 'attachment; ' . $filename)
            ->setHeader('Content-Length', $this->getPos())
            ->setHeader('Expires', '0')
            ->setHeader('Cache-Control', 'must-revalidate, post-check=0, pre-check=0')
            ->setHeader('Pragma', 'public');
        
        parent::finish();
    }
}

These examples are written from scratch. For sure an existing writer could be used and extended, which will result in less code:

PHP
class MyWriter 
extends \SetaPDF_Core_Writer_Echo
{
    /**
     * The response object
     *
     * @var MyResponse 
     */
    protected $_response;
    
    /**
     * The filename
     *
     * @var string 
     */
    protected $_filename = 'document.pdf';
    
    /**
     * Let's pass the response object to the writer
     *
     * @param MyResponse $response 
     * @param string $filename 
     */
    public function __construct(MyResponse $response, $filename = 'document.pdf')
    {
        $this->_response = $response;
        $this->_filename = $filename;
    }
    
    /**
     * The output starts
     */
    public function start()
    {
        // Let's clean the output buffer
        $this->_response->cleanBody();
        
        parent::start();
    }
    
    /**
     * Write content to the response object
     *
     * @param string $s 
     */
    public function write($s)
    {
        // Add the new string to the response body
        $this->_response->appendBody($s);
        // Let's update the position
        $this->_pos += strlen($s);
    }
    
    /**
     * Implement the finish()-method which also sets the headers
     */
    public function finish()
    {
        $filename = SetaPDF_Core_Writer_Http::encodeFilenameForHttpHeader($this->_filename);
        
        // Let's add the headers
        $this->_response
            ->setHeader('Content-Type', 'application/download')
            ->setHeader('Content-Disposition', 'attachment; ' . $filename)
            ->setHeader('Content-Length', $this->getPos())
            ->setHeader('Expires', '0')
            ->setHeader('Cache-Control', 'must-revalidate, post-check=0, pre-check=0')
            ->setHeader('Pragma', 'public');
        
        parent::finish();
    }
}