Post-Processors

1.0 version
Maintained

Post-Processors

We already know that filters perform image transformation. This may leave you wondering how post-processors fit into the runtime. To help illustrate the difference between filters and post-processors, it is important to highlight the following.

  • Filters modify the image.
  • Post-processors modify the image binary.

After all filters have run, the result is an image binary. This is then provided to, processed by, and returned from all configured post-processors.

Tip

Post-Processors can be safely chained, even if they operate on different mime-types. This makes them perfect for image-specific optimisation techniques.

Built-in processors

A number of built-in post-processors are provided by default.

Image Optimizers

Post-processors of the image optimizer classification are intended to reduce the final image file size, and therefore improve the load performance of your application's assets.

Custom processors

Just like filters, you can easily define your own, custom post-processors to perform any image binary operations required. Creating a custom post-processor begins by creating a class that implements the PostProcessorInterface interface, as shown below.

1
2
3
4
interface PostProcessorInterface
{
    public function process(BinaryInterface $binary);
}

As defined in PostProcessorInterface, the only required method is one named process, which is provided an instance of BinaryInterface as its singular parameter, and subsequently provides an instance of BinaryInterface in return.

Tip

You may optionally implement ConfigurablePostProcessorInterface in your post-processor to allow it to be configurable.

The following is a template for creating your own post-processor that calls an executable. You must set the EXECUTABLE_PATH class constant to the absolute path of the desired executable. You may also want to change array('image/png') to the supported mime types for your custom post-processor.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
namespace AppBundle\Imagine\Filter\PostProcessor;

use Liip\ImagineBundle\Binary\BinaryInterface;
use Liip\ImagineBundle\Model\Binary;
use Liip\ImagineBundle\Imagine\Filter\PostProcessor\PostProcessorInterface;
use Symfony\Component\Process\Exception\ProcessFailedException;
use Symfony\Component\Process\ProcessBuilder;

class MyCustomPostProcessor implements PostProcessorInterface
{
    const EXECUTABLE_PATH = '/path/to/your/executable';

    /**
     * @param BinaryInterface $binary
     *
     * @return BinaryInterface
     */
    public function process(BinaryInterface $binary)
    {
        // ensure the passed binary is a png
        if (!in_array(strtolower($binary->getMimeType()), array('image/png'))) {
            return $binary;
        }

        // create a temporary input file
        if (false === $input = tempnam($path = sys_get_temp_dir(), 'custom_')) {
            throw new \Exception(sprintf('Error created tmp file in "%s".', $path));
        }

        // populate temporary file with passed file contents
        file_put_contents($input, $binary->getContent());

        // create a process builder, add the input file as argument
        $pb = new ProcessBuilder(array(self::EXECUTABLE_PATH));
        $pb->add($input);

        // get a process instance and run it
        $process = $pb->getProcess();
        $process->run();

        // error out if command returned non-zero
        if (0 !== $process->getExitCode()) {
            unlink($input);
            throw new ProcessFailedException($process);
        }

        // retrieve the result
        $result = new Binary(
            file_get_contents($input),
            $binary->getMimeType(),
            $binary->getFormat()
        );

        // remove temporary file
        unlink($input);

        // return the result
        return $result;
    }
}

Once you have defined your custom post-processor, you must define it as a service and tag it with liip_imagine.filter.post_processor.

Note

For more information on the Service Container, reference the official Symfony Service Container documentation.

To register AppBundle\Imagine\Filter\PostProcessor\MyCustomPostProcessor with the name my_custom_post_processor, you would use the following configuration.

  • YAML
    1
    2
    3
    4
    5
    6
    # app/config/services.yml
    
    app.post_processor.my_custom_post_processor:
        class: AppBundle\Imagine\Filter\PostProcessor\MyCustomPostProcessor
        tags:
            - { name: 'liip_imagine.filter.post_processor', post_processor: 'my_custom_post_processor' }
    
  • XML
    1
    2
    3
    4
    5
    <!-- app/config/services.xml -->
    
    <service id="app.post_processor.my_custom_post_processor" class="AppBundle\Imagine\Filter\PostProcessor\MyCustomPostProcessor">
        <tag name="liip_imagine.filter.post_processor" post_processor="my_custom_post_processor" />
    </service>
    

Now your custom post-processor can be referenced in a filter set using the name assigned via the post_processor tag attribute above (in this example, my_custom_post_processor).

1
2
3
4
5
6
7
# app/config/config.yml

liip_imagine:
    filter_sets:
        my_special_style:
            post_processors:
                my_custom_post_processor: { }

This work, including the code samples, is licensed under a Creative Commons BY-SA 3.0 license.