Imbo

Imbo is an image “server” that can be used to add/get/delete images using a RESTful HTTP API. There is also support for adding meta data to the images stored in Imbo. The main idea behind Imbo is to have a place to store high quality original images and to use the API to fetch variations of the images. Imbo will resize, rotate and crop (amongst other transformations) images on the fly so you won’t have to store all the different variations.

Imbo is an open source (MIT license) project written in PHP and is available on GitHub. If you find any issues or missing features please add an issue in the Imbo issue tracker. If you want to know more feel free to join the #imbo channel on the Freenode IRC network (chat.freenode.net) as well.

Installation guide

Requirements

Imbo requires a web server (for instance Apache, Nginx or Lighttpd) running PHP >= 5.6 and the Imagick PECL extension extension for PHP (with at least ImageMagick 6.3.8).

You will also need a backend for storing image information, like for instance MongoDB or MySQL. If you want to use MongoDB as a database and/or GridFS for storage, you will need to install the MongoDB PECL extension and the MongoDB PHP library, and if you want to use a RDBMS like MySQL, you will need to install the Doctrine Database Abstraction Layer.

Installation

To install Imbo on the server you should use Composer. Create a composer.json file for your installation, and install Imbo and optional 3rd party plug-ins via the composer binary. First, you will need the following directory structure:

/path/to/install/composer.json
/path/to/install/config/

where the composer.json file can contain:

{
  "name": "yourname/imbo",
  "require": {
    "imbo/imbo": "^3.0"
  }
}

and the config/ directory contains one or more configuration files that will be merged with the default configuration. Imbo will load all .php files in this directory, and the ones returning an array will be used as configuration.

If you want to install 3rd party plug-ins and/or for instance the Doctrine DBAL library and the MongoDB PHP library simply add these to the require object in your composer.json file:

{
  "name": "yourname/imbo",
  "require": {
    "imbo/imbo": "^3.0",
    "rexxars/imbo-hipsta": "dev-master",
    "imbo/imbo": "^3.0",
    "doctrine/dbal": "^2.5",
    "mongodb/mongodb": "^1.1"
  }
}

If some of the 3rd party plug-ins provide configuration files, you can link to these in the config/ directory to have Imbo automatically load them:

cd /path/to/install/config
ln -s ../vendor/rexxars/imbo-hipsta/config/config.php 01-imbo-hipsta.php

To be able to control the order that Imbo uses when loading the configuration files you should prefix them with a number, like 01 in the example above. Lower numbers will be loaded first, meaning that configuration files with higher numbers will override settings set in configuration files with a lower number.

When you have created the composer.json file you can install Imbo with Composer:

composer install -o --no-dev

After composer has finished installing Imbo and optional dependencies the Imbo installation will reside in /path/to/install/vendor/imbo/imbo. The correct web server document root in this case would be /path/to/install/vendor/imbo/imbo/public.

If you later want to update Imbo you can bump the version number you have specified in composer.json and run:

composer update -o --no-dev

Regarding the Imbo version you are about to install you can use dev-master for the latest released version, or you can use a specific version if you want to (recommended). Head over to Imbo on Packagist to see the available versions. If you’re more of a YOLO type of person you can use dev-develop for the latest development version. If you choose to use the dev-develop branch, expect things to break from time to time.

Imbo strives to keep full BC in minor and patch releases, but breaking changes can occur. The most secure way to install one or more Imbo servers is to specify a specific version (for instance 3.0.0, or by using semver: ^3.0) in your composer.json file. Read the Imbo ChangeLog and the Upgrading Imbo chapter before doing an upgrade.

Web server configuration

After installing Imbo you will have to configure the web server you want to use. Imbo ships with sample configuration files for Apache, Nginx and Lighttpd that can be used with a few minor adjustments. All configuration files assume the httpd runs on port 80. If you use Varnish or some other HTTP accelerator simply change the port number to the port that your httpd listens to.

Apache

You will need to enable mod_rewrite if you want to use Imbo with Apache. Below is an example on how to configure Apache for Imbo:

<VirtualHost *:80>
    # Servername of the virtual host
    ServerName imbo

    # Define aliases to use multiple hosts
    # ServerAlias imbo1 imbo2 imbo3

    # Document root where the index.php file is located
    DocumentRoot /path/to/install/vendor/imbo/imbo/public

    # Logging
    # CustomLog /var/log/apache2/imbo.access_log combined
    # ErrorLog /var/log/apache2/imbo.error_log

    # Rewrite rules that rewrite all requests to the index.php script
    <Directory /path/to/install/vendor/imbo/imbo/public>
        RewriteEngine on
        RewriteCond %{REQUEST_FILENAME} !-f
        RewriteRule .* index.php
    </Directory>
</VirtualHost>

You will need to update ServerName to match the host name you will use for Imbo. If you want to use several host names you can update the ServerAlias line as well. You must also update DocumentRoot and Directory to point to the public directory in the Imbo installation. If you want to enable logging update the CustomLog and ErrorLog lines. RewriteCond and RewriteRule should be left alone.

Nginx

Below is an example on how to configure Nginx for Imbo. This example uses PHP via FastCGI:

server {
    # Listen on port 80
    listen 80;

    # Define the server name
    server_name imbo;

    # Use the line below instead of the server_name above if you want to use multiple host names
    # server_name imbo imbo1 imbo2 imbo3;

    # Path to the public directory where index.php is located
    root /path/to/install/vendor/imbo/imbo/public;
    index index.php;

    # Logs
    # error_log /var/log/nginx/imbo.error_log;
    # access_log /var/log/nginx/imbo.access_log main;

    location / {
        try_files $uri $uri/ /index.php?$args;
        location ~ \.php$ {
            fastcgi_pass 127.0.0.1:9000;
            fastcgi_index index.php;
            fastcgi_param SCRIPT_FILENAME /path/to/install/vendor/imbo/imbo/public/index.php;
            include fastcgi_params;
        }
    }
}

You will need to update server_name to match the host name you will use for Imbo. If you want to use several host names simply put several host names on that line. root must point to the public directory in the Imbo installation. If you want to enable logging update the error_log and access_log lines. You must also update the fastcgi_param SCRIPT_FILENAME line to point to the public/index.php file in the Imbo installation.

Lighttpd

Below is an example on how to configure Lighttpd for Imbo. Running PHP through FastCGI is recommended (not covered here).

# Use the line below instead of the next one if you want to use multiple host names
# $HTTP["host"] =~ "^(imbo|imbo1|imbo2|imbo3)$" {

$HTTP["host"] == "imbo" {
    # Listen on port 80
    server.port = 80

    # Path to the public directory where index.php is located
    server.document-root = "/path/to/install/vendor/imbo/imbo/public"

    # Logs
    # server.errorlog = "/var/log/lighttpd/imbo.error_log"
    # accesslog.filename = "/var/log/lighttpd/imbo.access_log"

    # Rewrite all to index.php
    url.rewrite-if-not-file = ("^/[^\?]*(\?.*)?$" => "index.php/$1")
}

You will need to set the correct host name(s) used with $HTTP["host"] and update the server.document-root to point to the correct path. If you want to enable logging remove the comments on the lines with server.errorlog and accesslog.filename and set the correct paths. If you want to specify a custom access log path you will need to enable the mod_accesslog module.

This example requires the mod_rewrite module to be loaded.

Varnish

Imbo strives to follow the HTTP Protocol, and can because of this easily leverage Varnish.

The only required configuration you need in your VCL is a default backend:

backend default {
    .host = "127.0.0.1";
    .port = "81";
}

where .host and .port is where Varnish can reach your web server.

If you use the same host name (or a sub-domain) for your Imbo installation as other services, that in turn uses Cookies, you might want the VCL to ignore these Cookies for the requests made against your Imbo installation (unless you have implemented event listeners for Imbo that uses Cookies). To achieve this you can put the following snippet into your VCL file:

sub vcl_recv {
    if (req.http.host == "imbo.example.com") {
        unset req.http.Cookie;
    }
}

or, if you have Imbo installed in some path:

sub vcl_recv {
    if (req.http.host ~ "^(www.)?example.com$" && req.url ~ "^/imbo/") {
        unset req.http.Cookie;
    }
}

if your Imbo installation is available on [www.]example.com/imbo.

Database setup

If you choose to use a RDBMS to store data in, you will need to manually create a database, a user and the tables Imbo stores information in. Below you will find schemas for different RDBMSs. You will find information regarding how to authenticate against the RDBMS of you choice in the Configuration topic.

MySQL
SQLite
CREATE TABLE IF NOT EXISTS imageinfo (
    id INTEGER PRIMARY KEY NOT NULL,
    user TEXT NOT NULL,
    imageIdentifier TEXT NOT NULL,
    size INTEGER NOT NULL,
    extension TEXT NOT NULL,
    mime TEXT NOT NULL,
    added INTEGER NOT NULL,
    updated INTEGER NOT NULL,
    width INTEGER NOT NULL,
    height INTEGER NOT NULL,
    checksum TEXT NOT NULL,
    originalChecksum TEXT NOT NULL,
    UNIQUE (user,imageIdentifier)
);

CREATE TABLE IF NOT EXISTS metadata (
    id INTEGER PRIMARY KEY NOT NULL,
    imageId KEY INTEGER NOT NULL,
    tagName TEXT NOT NULL,
    tagValue TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS shorturl (
    shortUrlId TEXT PRIMARY KEY NOT NULL,
    user TEXT NOT NULL,
    imageIdentifier TEXT NOT NULL,
    extension TEXT,
    query TEXT NOT NULL
);

CREATE INDEX shorturlparams ON shorturl (
    user,
    imageIdentifier,
    extension,
    query
);

CREATE TABLE IF NOT EXISTS imagevariations (
    user TEXT NOT NULL,
    imageIdentifier TEXT NOT NULL,
    width INTEGER NOT NULL,
    height INTEGER NOT NULL,
    added INTEGER NOT NULL,
    PRIMARY KEY (user,imageIdentifier,width)
);

Upgrading Imbo

Upgrading Imbo can be done quite easily. All you need to do is to bump the version number in your composer.json file and run composer update. See Installation on how to install Imbo.

From time to time Imbo will introduce new features or fix bugs that might require you to update the contents of the database you choose to use. This chapter will contain all information you need to keep your installation up to date. Each of the following sections include the necessary steps you need to execute when upgrading to the different versions.

Imbo-3.0.0

Below are the changes you need to be aware of when upgrading to Imbo-3.0.0.

Global images endpoint returns images for all users

If the users filter is excluded when making requests against the Global images resource - /images resource, images from all users will be returned if the public key used has access to all users in the result set.

MD5 image identifier generator has been removed

The Imbo\Image\Identifier\Generator\Md5 image identifier generator has been removed from Imbo-3. Use Imbo\Image\Identifier\Generator\RandomString (default) or Imbo\Image\Identifier\Generator\Uuid instead.

Removed storage and database adapters

The following storage and database adapters have been removed from Imbo-3:

  • Imbo\Database\Mongo
  • Imbo\Storage\Doctrine
  • Imbo\Auth\AccessControl\Adapter\Mongo
  • Imbo\EventListener\ImageVariations\Database\Mongo
  • Imbo\EventListener\ImageVariations\Storage\Doctrine
MongoDB and GridFS adapters have been updated

The following adapters have been updated to use the new MongoDB PECL extension and the MongoDB PHP library:

  • Imbo\Database\MongoDB
  • Imbo\Storage\GridFS
  • Imbo\Auth\AccessControl\Adapter\MongoDB
  • Imbo\EventListener\ImageVariations\Database\MongoDB
  • Imbo\EventListener\ImageVariations\Storage\GridFS

Because of this the configuration parameters for the adapters have also been updated. Refer to the Configuration section for more information on how to configure the adapters.

XML-support has been removed

Imbo-3 no longer supports XML output. The only supported response format is JSON.

auth is removed from the configuration

Imbo-3 no longer provides support for using the auth part of the configuration file. You will need to use the Access Control adapters from now on.

S3 Library Upgraded

The dependency on aws-sdk-php has been upgraded to the most recent version at the time of release. The updated library requires the region parameter to be supplied when connecting to S3. This also applies to the Image Variations support if you’re storing the variations in S3.

Example
new Imbo\Storage\S3([
    'key' => '<aws access key>'
    'secret' => '<aws secret key>',
    'bucket' => 'my-imbo-bucket',
    'region' => 'eu-central-1',
]);

Imbo-2.0.0

Below are the changes you need to be aware of when upgrading to Imbo-2.0.0.

Public key is an authentication detail

Versions prior to 2.0.0 had a 1:1 correlation between what a user and a public key was. In 2.0.0, a user is the entity which images belong to, while a public key is one part of the authentication scheme. Public keys each has their own set of permissions, which can grant them access to different resources within different users.

Imbo provides some level of backwards compatibility with the old auth part of the configuration file, but no longer supports multiple private keys per public key (such as read-only/read+write). If you are using this, Imbo will throw an exception on startup. We recommend moving away from the auth setup and onto the new Access Control adapters as soon as possible.

Prior to 2.0.0, both the database and HTTP endpoints used a field name of publicKey to identify the user. Going forward, apart from when working with authentication, user will be the new field name. This requires some database changes - see below.

Doctrine

If you use the Doctrine database adapter you’ll need to rename the publicKey fields to user. The field has been updated in the Database setup section. The field should be renamed while there are no write operations pending, as a write could fail before upgrading Imbo itself.

Postgres/Other
ALTER TABLE imageinfo RENAME COLUMN publicKey to user;
ALTER TABLE shorturl RENAME COLUMN publicKey to user;
ALTER TABLE imagevariations RENAME COLUMN publicKey to user;
MySQL
ALTER TABLE imageinfo CHANGE `publicKey` `user` varchar(255) COLLATE utf8_danish_ci NOT NULL;
ALTER TABLE shorturl CHANGE `publicKey` `user` varchar(255) COLLATE utf8_danish_ci NOT NULL;
ALTER TABLE imagevariations CHANGE `publicKey` `user` varchar(255) COLLATE utf8_danish_ci NOT NULL;

If you use the Doctrine storage adapter for images and/or image variations, you will have to rename fields in those databases too:

Postgres/Other
ALTER TABLE storage_images RENAME COLUMN publicKey to user;
ALTER TABLE storage_image_variations RENAME COLUMN publicKey to user;
MySQL
ALTER TABLE storage_images CHANGE `publicKey` `user` varchar(255) COLLATE utf8_danish_ci NOT NULL;
ALTER TABLE storage_image_variations CHANGE `publicKey` `user` varchar(255) COLLATE utf8_danish_ci NOT NULL;

Note

The imagevariations and storage_image_variations table might not be present in your database unless you previously upgraded to 1.2.4. In this case, skip the queries affecting those tables and instead follow the instructions specified in the Database setup section.

MongoDB

If you use the MongoDB adapter, you will need to rename the publicKey field with the following queries:

db.image.update({}, { $rename: { 'publicKey': 'user' } }, { multi: true })
db.shortUrl.update({}, { $rename: { 'publicKey': 'user' } }, { multi: true })
db.imagevariation.update({}, { $rename: { 'publicKey': 'user' } }, { multi: true })

Note

The imagevariation collection might not be present in your database unless you previously upgraded to 1.2.4. In this case, skip the last query and instead follow the instructions specified in the Database setup section.

GridFS

If you use the GridFS adapter, you will need to rename the publicKey field with the following query:

db.fs.files.update({}, { $rename: { 'publicKey': 'user' } }, { multi: true } )

Note

The default database names for the GridFS adapters are imbo_storage and imbo_imagevariation_storage. The query specified should be run on both databases. If the imbo_imagevariation_storage database does not exist, run the query on imbo_storage and follow the instructions specified in the Database setup section to create the appropriate indexes for the imbo_imagevariation_storage database.

Image identifiers are no longer MD5-sums

Previously, Imbo used the MD5 algorithm to generate the image identifier for an image. In Imbo 2.0.0 and onwards, image identifiers are simply randomly generated strings. This means that the same image can exist multiple times within the same user. If this is not what you want, you can check if the image already exists by querying the images resource and specifying the MD5-sum of the image as an originalChecksum-filter. Most Imbo-clients implement this already, as imageExists() or similar.

To accommodate the new image identifiers and the possibility of future changes in how they are represented, databases should be able to store an image identifier of up to 255 characters. If you are using the Doctrine database adapter with the suggested schema on a MySQL database, this will require some changes:

ALTER TABLE imageinfo MODIFY imageIdentifier varchar(255) COLLATE utf8_danish_ci NOT NULL;
ALTER TABLE shorturl MODIFY imageIdentifier varchar(255) COLLATE utf8_danish_ci NOT NULL;
ALTER TABLE imagevariations MODIFY imageIdentifier varchar(255) COLLATE utf8_danish_ci NOT NULL;

If you use the Doctrine storage adapter for images and/or image variations, you will have to rename fields in those databases too:

ALTER TABLE storage_images MODIFY imageIdentifier varchar(255) COLLATE utf8_danish_ci NOT NULL;
ALTER TABLE storage_image_variations MODIFY imageIdentifier varchar(255) COLLATE utf8_danish_ci NOT NULL;

Note

The imagevariations and storage_image_variations table might not be present in your database unless you previously upgraded to 1.2.4. In this case, skip the queries affecting those tables and instead follow the instructions specified in the Database setup section.

Imbo-1.2.4

A new Image Variations event listener was introduced. It is disabled by default, and to use it you will have to configure a database and storage adapter for it - depending on your choice of adapters, you might need to modify your database. See the Database setup section.

Imbo-1.2.0

Below are the changes you need to be aware of when upgrading to Imbo-1.2.0.

Response to metadata write operations

Versions prior to 1.2.0 contained the image identifier in the response to HTTP POST/PUT/DELETE against the metadata resource. Starting from Imbo-1.2.0 the response to these requests will contain the metadata attached to the image instead. Read more about the different responses in the metadata resource section.

Original checksum

Imbo-1.2.0 includes a new feature that lets you filter images based on the original checksum of the image when querying the images resource. For this to work you need to add a field to your database. You can also populate this field for all images if you want, but this is not required. If you have event listeners that update incoming images, the values already stored in the database under the checksum field (which is used to populate the originalChecksum field in the following examples) might not be the checksum of the original image. If you don’t have such event listeners added to your configuration you should be able to update the data as explained below and end up with 100% correct results.

Doctrine

If you use the Doctrine database adapter you’ll need to add the new originalChecksum field to the table. The field has also been added to the Database setup section. The field should be added while there are no write operations pending, as a write could fail before upgrading Imbo itself.

ALTER TABLE imageinfo ADD COLUMN `originalChecksum` char(32) COLLATE utf8_danish_ci NOT NULL;

When you have added the field to your database you can run the following query to update all rows in the database:

UPDATE `imageinfo` SET `originalChecksum` = `checksum`

This query will simply copy the value of the existing checksum field over to originalChecksum. If you have a lot of images this operation might take a while.

MongoDB

If you use the MongoDB adapter all you need to do is to update all entries in the image collection:

db.image.find().forEach(
    function (elem) {
        db.image.update(
            { _id: elem._id },
            { $set: { originalChecksum: elem.checksum }}
        );
    }
)
Short image URLs

In versions prior to Imbo-1.2.0 short image URLs were created automatically whenever a user agent requested the image resource (with or without transformations), and sent in the response as the X-Imbo-ShortUrl header. This no longer done automatically. Refer to the Short URLs resource - /users/<user>/images/<image>/shorturls section for more information on how to generate short URLs from this version on.

Configuration

Imbo ships with a default configuration file that will be automatically loaded. You will have to create one or more configuration files of your own that will be automatically merged with the default configuration by Imbo. You should never have to edit the default configuration file provided by Imbo.

The configuration file(s) you need to create should simply return arrays with configuration data. All available configuration options are covered in this chapter.

Using callables in configuration

Imbo supports callables for certain configuration options: accessControl, database, eventListeners[name], resource, storage and transformationPresets. The callable receives two arguments ($request and $response) which map to the active request and response objects.

By using a callable you can extend Imbo with custom logic for most configuration options (or provide the $request or $response objects to your implementing class), such as switching storage modules based on which user performed the request. Using callables will also delay the instantiation of the classes until they are first accessed, instead of when the configuration files are loaded.

Imbo access control - accessControl

Imbo catalogs stored images under a user. To add an image to a given user, you need a public and private key pair. This pair is used to sign requests to Imbo’s API and ensures that the API can’t be accessed without knowing the private key.

Multiple public keys can be given access to a user, and you can also configure a public key to have access to several users. It’s important to note that a user does not have to be created in any way - as long as a public key is defined to have access to a given user, you’re ready to start adding images.

Public keys can be configured to have varying degrees of access. For instance, you might want one public key for write operations (such as adding and deleting images) and a different public key for read operations (such as viewing images and applying transformations to them). Access is defined on a resource basis - which basically translates to an API endpoint and an HTTP method. To retrieve an image, for instance, you would give access to the image.get resource.

Specifying a long list of resources can get tedious, so Imbo also supports resource groups - basically just a list of different resources. When creating access rules for a public key, these can be used instead of specifying specific resources.

For the private keys you can for instance use a SHA-256 hash of a random value. The private key is used by clients to sign requests, and if you accidentally give away your private key users can use it to delete all your images (given the public key it belongs to has write access). Make sure not to generate a private key that is easy to guess (like for instance the MD5 or SHA-256 hash of the public key). Imbo does not require the private key to be in a specific format, so you can also use regular passwords if you want. The key itself should never be a part of the payload sent to/from the server.

Imbo ships with a small command line tool that can be used to generate private keys for you using PHP’s openssl_random_pseudo_bytes function. The tool is located in the bin directory of the Imbo installation:

$ ./bin/imbo generate-private-key
3b98dde5f67989a878b8b268d82f81f0858d4f1954597cc713ae161cdffcc84a

Both the public and private key can be changed whenever you want as long as you remember to change them in both the server configuration and in the client you use. The user can not be changed easily as database and storage adapters use it when storing/fetching images and metadata.

Access control is managed by adapters. The simplest adapter is the Imbo\Auth\AccessControl\Adapter\SimpleArrayAdapter, which has a number of trade-offs in favor of being easy to set up. Mainly, it expects the public key to have the same name as the user it should have access to, and that the public key should be given full read+write access to all resources belonging to that user.

Note

It’s not recommended that you use the same public key for both read and write operations. Read on to see how you can create different public keys for read and read/write access.

The adapter is set up using the accessControl key in your configuration file:

<?php
return [
    // ...

    'accessControl' => new Imbo\Auth\AccessControl\Adapter\SimpleArrayAdapter([
        'some-user' => 'my-super-secret-private-key',
        'other-user' => 'other-super-secret-private-key',
    ]),

    // ...
];

It’s usually a good idea to have separate public keys for read-only and read+write operations. You can achieve this by using a more flexible access control adapter, such as the Imbo\Auth\AccessControl\Adapter\ArrayAdapter:

<?php
return [
    // ...

    'accessControl' => new Imbo\Auth\AccessControl\Adapter\ArrayAdapter([
        [
            'publicKey' => 'some-read-only-pubkey',
            'privateKey' => 'some-private-key',
            'acl' => [[
                'resources' => Imbo\Resource::getReadOnlyResources(),
                'users' => ['some-user']
            ]]
        ],
        [
            'publicKey' => 'some-read-write-pubkey',
            'privateKey' => 'some-other-private-key',
            'acl' => [[
                'resources' => Imbo\Resource::getReadWriteResources(),
                'users' => ['some-user']
            ]]
        ]
    ]),

    // ...
];

As you can see, the Imbo\Auth\AccessControl\Adapter\ArrayAdapter is much more flexible than the Imbo\Auth\AccessControl\Adapter\SimpleArrayAdapter. The above example only shows part of this flexibility. You can also provide resource groups and multiple access control rules per public key. The following example shows this more clearly:

<?php
return [
    // ...

    'accessControl' => new Imbo\Auth\AccessControl\Adapter\ArrayAdapter([
        [
            // A unique public key matching the following regular expression: [A-Za-z0-9_-]{1,}
            'publicKey' => 'some-pubkey',

            // Some form of private key
            'privateKey' => 'some-private-key',

            // Array of rules for this public key
            'acl' => [
                [
                    // An array of different resource names that the public key should have
                    // access to - see AdapterInterface::RESOURCE_* for available options.
                    'resources' => Imbo\Resource::getReadOnlyResources(),

                    // Names of the users which the public key should have access to.
                    'users' => ['some', 'users'],
                ],

                // Multiple rules can be applied in order to make a single public key have
                // different access rights on different users
                [
                    'resources' => Imbo\Resource::getReadWriteResources(),
                    'users' => ['different-user'],
                ],

                // You can also specify resource groups instead of explicitly setting them like
                // in the above examples. Note that you cannot specify both resources and group
                // in the same rule.
                [
                    'group' => 'read-stats',
                    'users' => ['user1', 'user2']
                ]
            ]
        ]
    ], [
        // Second argument to the ArrayAdapter being the available resource groups
        // Format: 'name' => ['resource1', 'resource2']
        'read-stats' => ['user.get', 'user.head', 'user.options'],
    ]),

    // ...
];

Imbo also ships with a MongoDB access control adapter, which is mutable. This means you can manipulate the access control rules on the fly, using Imbo’s API. The adapter uses the MongoDB PECL extension and the MongoDB PHP library. The following parameters are supported:

databaseName
Name of the database to use. Defaults to imbo.
server
The server string to use when connecting. Defaults to mongodb://localhost:27017.
options
Options passed to the underlying adapter. Defaults to ['connect' => true, 'timeout' => 1000]. See the manual for the MongoDB PHP library for more information.
<?php
return [
    // ...

    'accessControl' => new Imbo\Auth\AccessControl\Adapter\MongoDB(),

    // ...
];

When using a mutable access control adapter, you will need to create an initial public key that can subsequently be used to create other public keys. The easiest way to create public keys when using a mutable adapter is to utilize the add-public-key command provided by the CLI tool that Imbo is shipped with.

Database configuration - database

The database adapter you decide to use is responsible for storing metadata and basic image information, like width and height for example, along with the generated short URLs. Imbo ships with some different database adapters that you can use. Remember that you will not be able to switch the adapter whenever you want and expect all data to be automatically transferred. Choosing a database adapter should be a long term commitment unless you have migration scripts available.

In the default configuration file the MongoDB database adapter is used. You can choose to override this in your configuration file by specifying a different adapter. You can either specify an instance of a database adapter directly, or specify a closure that will return an instance of a database adapter when executed. Which database adapter to use is specified in the database key in the configuration array:

<?php
return [
    // ...

    'database' => new Imbo\Database\MongoDB(),

    // ...
);

Below you will find documentation on the different database adapters Imbo ships with.

Doctrine

This adapter uses the Doctrine Database Abstraction Layer. The options you pass to the constructor of this adapter is passed to the underlying classes, so have a look at the documentation for Doctrine DBAL. When using this adapter you need to create the required tables in the RDBMS first, as specified in the Database setup section.

Example
<?php
return [
    // ...

    'database' => new Imbo\Database\Doctrine([
        'dbname' => 'database',
        'user' => 'username',
        'password' => 'password',
        'host' => 'hostname',
        'driver' => 'pdo_mysql'
    ]),

    // ...
];
MongoDB

This adapter use the MongoDB PECL extension and the MongoDB PHP library to store data in MongoDB. The following parameters are supported:

databaseName
Name of the database to use. Defaults to imbo.
server
The server string to use when connecting. Defaults to mongodb://localhost:27017.
options
Options passed to the underlying adapter. Defaults to ['connect' => true, 'timeout' => 1000]. See the manual for the MongoDB PHP library for more information.
Examples
  1. Connect to a local MongoDB instance using the default databaseName:
<?php
return [
    // ...

    'database' => new Imbo\Database\MongoDB(),

    // ...
];
  1. Connect to a MongoDB replica set:
<?php
return [
    // ...

    'database' => new Imbo\Database\MongoDB([
        'server' => 'mongodb://server1,server2,server3/?replicaSet=nameOfReplicaSet',
    ]),

    // ...
];
Custom database adapter

If you need to create your own database adapter you need to create a class that implements the Imbo\Database\DatabaseInterface interface, and then specify that adapter in the configuration:

<?php
return [
    // ...

    'database' => new My\Custom\DatabaseAdapter(),

    // ...
];

You can read more about how to achieve this in the Implement your own database and/or storage adapter chapter.

Storage configuration - storage

Storage adapters are responsible for storing the original images you put into Imbo. As with the database adapter it is not possible to simply switch the adapter without having migration scripts available to move the stored images. Choose an adapter with care.

In the default configuration file the GridFS storage adapter is used. You can choose to override this in your configuration file by specifying a different adapter. You can either specify an instance of a storage adapter directly, or specify a closure that will return an instance of a storage adapter when executed. Which storage adapter to use is specified in the storage key in the configuration array:

<?php
return [
    // ...

    'storage' => new Imbo\Storage\Filesystem([
        'dataDir' => '/path/to/images',
    ]),

    // ...
];

Below you will find documentation on the different storage adapters Imbo ships with.

Amazon Simple Storage Service

This adapter stores your images in a bucket in Amazon S3. The parameters are:

key
Your AWS access key.
secret
Your AWS secret key.
bucket
The name of the bucket you want to store your images in. Imbo will not create this for you automatically.
region
The name of the region your bucket resides in.

This adapter creates subdirectories in the bucket in the same fashion as the Filesystem storage adapter stores the files on the local filesystem.

Examples
<?php
return [
    // ...

    'storage' => new Imbo\Storage\S3([
        'key' => '<aws access key>',
        'secret' => '<aws secret key>',
        'bucket' => 'my-imbo-bucket',
        'region' => 'eu-central-1',
    ]),

    // ...
];
Backblaze B2 Cloud Storage

This adapter stores your images in a bucket in the Backblaze B2 Cloud Storage. The parameters are:

accountId
Your B2 account ID.
applicationKey
Your B2 application key.
bucket
The name of the bucket you want to store your images in.
bucketId
The ID of the bucket.
Examples
<?php
return [
    // ...

    'storage' => new Imbo\Storage\B2([
        'accountId' => '<b2 account ID>',
        'applicationKey' => '<b2 application key>',
        'bucket' => 'my-imbo-bucket',
        'bucketId' => '<bucket ID>',
    ]),

    // ...
];
Filesystem

This adapter simply stores all images on the file system. It has a single parameter, and that is the base directory of where you want your images stored:

dataDir
The base path where the images are stored.

This adapter is configured to create subdirectories inside of dataDir based on the user and the checksum of the images added to Imbo. The algorithm that generates the path simply takes the three first characters of the user and creates directories for each of them, then the complete user, then a directory of each of the first characters in the image identifier, and lastly it stores the image in a file with a filename equal to the image identifier itself. For instance, an image stored under the user foobar with the image identifier 5c01e554-9fca-4231-bb95-a6eabf259b64 would be stored as <dataDir>/f/o/o/foobar/5/c/0/5c01e554-9fca-4231-bb95-a6eabf259b64.

Examples
  1. Store images in /path/to/images:
<?php
return [
    // ...

    'storage' => new Imbo\Storage\Filesystem([
        'dataDir' => '/path/to/images',
    ]),

    // ...
];
GridFS

The GridFS adapter is used to store the images in MongoDB using GridFS. This adapter has the following parameters:

databaseName
The name of the database to store the images in. Defaults to imbo_storage.
server
The server string to use when connecting to MongoDB. Defaults to mongodb://localhost:27017.
options
Options passed to the underlying adapter. Defaults to ['connect' => true, 'timeout' => 1000]. See the manual for the MongoDB PHP library for available options.
Examples
  1. Connect to a local MongoDB instance using the default databaseName:
<?php
return [
    // ...

    'storage' => new Imbo\Storage\GridFS(),

    // ...
];
  1. Connect to a replica set:
<?php
return [
    // ...

    'storage' => new Imbo\Storage\GridFS([
        'server' => 'mongodb://server1,server2,server3/?replicaSet=nameOfReplicaSet',
    ]),

    // ...
];
Custom storage adapter

If you need to create your own storage adapter you need to create a class that implements the Imbo\Storage\StorageInterface interface, and then specify that adapter in the configuration:

<?php
return [
    // ...

    'storage' => new My\Custom\StorageAdapter(),

    // ...
];

You can read more about how to achieve this in the Implement your own database and/or storage adapter chapter.

Image identifier generation - imageIdentifierGenerator

By default, Imbo will generate a random string of characters as the image identifier for added images. These are in the RegExp range [A-Za-z0-9_-] and by default, the identifier will be 12 characters long.

You can easily change the generation process to a different method.

Random string

The default, as stated above. This generator has the following parameters:

length
The length of the randomly generated string. Defaults to 12.

Usage:

<?php
return [
    // ...

    'imageIdentifierGenerator' => new Imbo\Image\Identifier\Generator\RandomString(),

    // ...
];
UUID

Generates 36-character v4 UUIDs, for instance f47ac10b-58cc-4372-a567-0e02b2c3d479. This generator does not have any parameters.

Usage:

<?php
return [
    // ...

    'imageIdentifierGenerator' => new Imbo\Image\Identifier\Generator\Uuid(),

    // ...
];
Custom generators

To create your own custom image identifier generators, simply create a class that implements Imbo\Image\Identifier\Generator\GeneratorInterface and ensure that the identifiers generated are in the character range [A-Za-z0-9_-] and are between one and 255 characters long.

HTTP cache headers - httpCacheHeaders

Imbo ships with reasonable defaults for which HTTP cache header settings it sends to clients. For some resources, however, it can be difficult to figure out a good middle ground between clients asking too often and too rarely. For instance, the images resource will change every time a new image has been added - but whether that happens once a second or once a year is hard to know.

To ensure that clients get fresh responses, Imbo sends max-age=0, must-revalidate on these kind of resources. You can however override these defaults in the configuration. For instance, if you wanted to set the max-age to 30 seconds, leave it up to the client if it should re-validate and tell intermediary proxies that this response is private, you could set the configuration to the following:

<?php
return [
    // ...

    'httpCacheHeaders' => [
        'maxAge' => 30,
        'mustRevalidate' => false,
        'public' => false,
    ],

    // ...
];

Content negotiation for images - contentNegotiateImages

By default, Imbo will do content negotiation for images. In other words, if a request is sent for an image with the Accept-header image/jpeg, it will try to deliver the image in JPEG-format.

If what you want is for images to be delivered in the format they were uploaded in, you can set contentNegotiateImages to false in the configuration. This will also ensure Imbo does not include Accept in the Vary-header for image requests, which will make caching behind reverse proxies more efficient.

You are still able to convert between formats by specifying an extension when requesting the image (.jpg, .png, .gif etc).

Rethrow any non-handled general exceptions - rethrowFinalException

If an exception occurs internally while Imbo is processing a request, the exception will be caught by the main application entry point and an appropriate error will be generated. This does however hide implementation details that can be useful if you’re doing actual development on Imbo. This value is false by default.

Setting this value to true will make Imbo rethrow the exception instead of swallowing the original exception and triggering an error. In the latter case the actual stack trace will be lost, and seeing which part of the code that actually failed will be harder in a log file.

<?php
return [
    // ...

    'rethrowFinalException' => true,

    // ...
];

Trusted proxies - trustedProxies

If you find yourself behind some sort of reverse proxy (like a load balancer), certain header information may be sent to you using special X-Forwarded-* headers. For example, the Host HTTP-header is usually used to return the requested host. But when you’re behind a proxy, the true host may be stored in an X-Forwarded-Host header.

Since HTTP headers can be spoofed, Imbo does not trust these proxy headers by default. If you are behind a proxy, you should manually whitelist your proxy. This can be done by defining the proxies IP addresses and/or using CIDR notations. Example:

<?php
return [
    // ...

    'trustedProxies' => ['192.0.0.1', '10.0.0.0/8'],

    // ...
];

Note

Not all proxies set the required X-Forwarded-* headers by default. A search for X-Forwarded-Proto <your proxy here> usually gives helpful answers to how you can add them to incoming requests.

Authentication protocol - authentication

Imbo generates access tokens and authentication signatures based on the incoming URL, and includes the protocol (by default). This can sometimes be problematic, for instance when Imbo is behind a load balancer which does not send X-Forwarded-Proto header, or if you want to use protocol-less image URLs on the client side (//imbo.host/users/some-user/images/img).

Setting the protocol option under authentication allows you to control how Imbo’s authentication should behave. The option has the following possible values:

incoming
Will try to detect the incoming protocol - this is based on $_SERVER['HTTPS'] or the X-Forwarded-Proto header (given the trustedProxies option is configured). This is the default value.
both
Will try to match based on both HTTP and HTTPS protocols and allow the request if any of them yields the correct signature/access token.
http
Will always use http as the protocol, replacing https with http in the incoming URL, if that is the case.
https
Will always use https as the protocol, replacing http with https in the incoming URL, if that is the case.

Example usage:

<?php
return [
    // ...

    'authentication' => [
        'protocol' => 'both',
    ],

    // ...
];

Event listeners - eventListeners

Imbo support event listeners that you can use to hook into Imbo at different phases without having to edit Imbo itself. An event listener is simply a piece of code that will be executed when a certain event is triggered from Imbo. Event listeners are added to the eventListeners part of the configuration array as associative arrays. If you want to disable some of the default event listeners simply specify the same key in your configuration file and set the value to null or false. Keep in mind that not all event listeners should be disabled.

Event listeners can be configured in the following ways:

  1. A string representing a class name of a class implementing the Imbo\EventListener\ListenerInteface interface:
<?php
return [
    // ...

    'eventListeners' => [
        'accessToken' => 'Imbo\EventListener\AccessToken',
    ],

    // ...
];
  1. Use an instance of a class implementing the Imbo\EventListener\ListenerInterface interface:
<?php
return [
    // ...

    'eventListeners' => [
        'accessToken' => new Imbo\EventListener\AccessToken(),
    ],

    // ...
];
  1. A closure returning an instance of a class implementing the Imbo\EventListener\ListenerInterface interface:
<?php
return [
    // ...

    'eventListeners' => [
        'accessToken' => function() {
            return new Imbo\EventListener\AccessToken();
        },
    ],

    // ...
];
  1. Use a class implementing the Imbo\EventListener\ListenerInterface interface together with an optional user filter:
<?php
return [
    // ...

    'eventListeners' => [
        'maxImageSize' => [
            'listener' => new Imbo\EventListener\MaxImageSize(1024, 768),
            'users' => [
                'whitelist' => ['user'],
                // 'blacklist' => ['someotheruser'],
            ],
            // 'params' => [ ... ]
        ],
    ],

    // ...
];

where listener is one of the following:

  1. a string representing a class name of a class implementing the Imbo\EventListener\ListenerInterface interface
  2. an instance of the Imbo\EventListener\ListenerInterface interface
  3. a closure returning an instance Imbo\EventListener\ListenerInterface

The users element is an array that you can use if you want your listener to only be triggered for some users. The value of this is an array with two elements, whitelist and blacklist, where whitelist is an array of users you want your listener to trigger for, and blacklist is an array of users you don’t want your listener to trigger for. users is optional, and per default the listener will trigger for all users.

There also exists a params key that can be used to specify parameters for the event listener, if you choose to specify the listener as a string in the listener key:

<?php
return [
    // ...

    'eventListeners' => [
        'maxImageSize' => [
            'listener' => 'Imbo\EventListener\MaxImageSize',
            'users' => [
                'whitelist' => ['user'],
                // 'blacklist' => ['someotheruser'],
            ],
            'params' => [
                'width' => 1024,
                'height' => 768,
            ]
        ],
    ],

    // ...
];

The value of the params array will be sent to the constructor of the event listener class.

  1. Use a closure directly:
<?php
return [
    // ...

    'eventListeners' => [
        'customListener' => [
            'callback' => function(Imbo\EventManager\EventInterface $event) {
                // Custom code
            },
            'events' => ['image.get'],
            'priority' => 1,
            'users' => [
                'whitelist' => ['user'],
                // 'blacklist' => ['someotheruser'],
            ],
        ],
    ],

    // ...
];

where callback is the code you want executed, and events is an array of the events you want it triggered for. priority is the priority of the listener and defaults to 0. The higher the number, the earlier in the chain your listener will be triggered. This number can also be negative. Imbo’s internal event listeners uses numbers between 0 and 100. users uses the same format as described above. If you use this method, and want your callback to trigger for multiple events with different priorities, specify an associative array in the events element, where the keys are the event names, and the values are the priorities for the different events. This way of attaching event listeners should mostly be used for quick and temporary solutions.

All event listeners will receive an event object (which implements Imbo\EventManager\EventInterface), that is described in detail in the The event object section.

Listeners added by default

The default configuration file includes some event listeners by default:

as well as event listeners for image transformations:

Read more about these listeners (and more) in the Customize your Imbo installation with event listeners and Transforming images on the fly chapters. If you want to disable any of these you could do so in your configuration file in the following way:

<?php
return [
    // ...

    'eventListeners' => [
        'accessToken' => null,
        'auth' => null,
        'statsAccess' => null,
    ],

    // ...
];

Warning

Do not disable the event listeners used in the example above unless you are absolutely sure about the consequences. Your images can potentially be deleted by anyone.

Warning

Disabling image transformation event listeners is not recommended.

Event listener initializers - eventListenerInitializers

Some event listeners might require custom initialization, and if you don’t want to do this in-line in the configuration, Imbo supports event listener initializer classes. This is handled via the eventListenerInitializers key. The value of this element is an associative array where the keys identify the initializers (only used in the configuration itself), and the values are strings representing class names, or implementations of the Imbo\EventListener\Initializer\InitializerInterface interface. If you specify strings the classes you refer to must also implement this interface.

The interface has a single method called initialize and receives instances of event listeners implementing the Imbo\EventListener\ListenerInterface interface. This method is called once for each event listener instantiated by Imbo’s event manager. Example:

<?php
// Some event listener
class Listener implements Imbo\EventListener\ListenerInterface {
    public function setDependency($dependency) {
        // ...
    }

    // ...
}

class OtherListener implements Imbo\EventListener\ListenerInterface {
    public function setDependency($dependency) {
        // ...
    }

    // ...
}

// Event listener initializer
class Initializer implements Imbo\EventListener\Initializer\InitializerInterface {
    private $dependency;

    public function __construct() {
        $this->dependency = new SomeDependency();
    }

    public function initialize(Imbo\EventListener\ListenerInterface $listener) {
        if ($listener instanceof Listener || $listener instanceof OtherListener) {
            $listener->setDependency($this->dependency);
        }
    }
}

// Configuration
return [
    'eventListeners' => [
        'customListener' => 'Listener',
        'otherCustomListener' => 'OtherListener',
    ],

    'eventListenerInitializers' => [
        'initializerForCustomListener' => 'Initializer',
    ],
];

In the above example the Initializer class will be instantiated by Imbo, and in the __construct method it will create an instance of some dependency. When the event manager creates the instances of the two event listeners these will in turn be sent to the initialize method, and the same dependency will be injected into both listeners. An alternative way to accomplish this by using Closures in the configuration could look something like this:

<?php
$dependency = new SomeDependency();

return [
    'eventListeners' => [
        'customListener' => function() use ($dependency) {
            $listener = new Listener();
            $listener->setDependency($dependency);

            return $listener;
        },
        'otherCustomListener' => function() use ($dependency) {
            $listener = new OtherListener();
            $listener->setDependency($dependency);

            return $listener;
        },
    ],
];

Imbo itself includes an event listener initializer in the default configuration that is used to inject the same instance of Imagick to all image transformations.

Note

Only event listeners specified as strings (class names) in the configuration will be instantiated by Imbo, so event listeners instantiated in the configuration array, either directly or via a Closures, will not be initialized by the configured event listener initializers.

Image transformation presets - transformationPresets

Through the configuration you can also combine image transformations to make presets (transformation chains). This is done via the transformationPresets key:

<?php
return [
    // ...

    'transformationPresets' => [
        'graythumb' => [
            'thumbnail',
            'desaturate',
        ],
        // ...
    ],

    // ...
];

where the keys are the names of the transformations as specified in the URL, and the values are arrays containing other transformation names (as used in the eventListeners part of the configuration). You can also specify hard coded parameters for the presets if some of the transformations in the chain supports parameters:

<?php
return [
    // ...

    'transformationPresets' => [
        'fixedGraythumb' => [
            'thumbnail' => [
                'width' => 50,
                'height' => 50,
            ],
            'desaturate',
        ],
        // ...
    ],

    // ...
];

By doing this the thumbnail part of the fixedGraythumb preset will ignore the width and height query parameters, if present. By only specifying for instance 'width' => 50 in the configuration the height of the thumbnail can be adjusted via the query parameter, but the width is fixed.

Note

The URLs will stay the same if you change the transformation chain in a preset. Keep this in mind if you use for instance Varnish or some other HTTP accelerator in front of your web server(s).

Custom resources and routes - resources and routes

Warning

Custom resources and routes is an experimental and advanced way of extending Imbo, and requires extensive knowledge of how Imbo works internally. This feature can potentially be removed in future releases, so only use this for testing purposes.

If you need to create a custom route you can attach a route and a custom resource class using the configuration. Two keys exists for this purpose: resources and routes:

<?php
return [
    // ...

    'resources' => [
        'users' => new ImboUsers();

        // or

        'users' => function() {
            return new ImboUsers();
        },

        // or

        'users' => 'ImboUsers',
    ],

    'routes' => [
        'users' => '#^/users(\.(?<extension>json))?$#',
    ],

    // ...
];

In the above example we are creating a route for Imbo using a regular expression, called users. The route itself will match the following three requests:

  • /users
  • /users.json

When a request is made against any of these endpoints Imbo will try to access a resource that is specified with the same key (users). The value specified for this entry in the resources array can be:

  1. a string representing the name of the resource class
  2. an instance of a resource class
  3. an anonymous function that, when executed, returns an instance of a resource class

The resource class must implement the Imbo\Resource\ResourceInterface interface to be able to response to a request.

Below is an example implementation of the ImboUsers resource used in the above configuration:

<?php
use Imbo\Resource\ResourceInterface,
    Imbo\EventManager\EventInterface,
    Imbo\Model\ListModel;

class ImboUsers implements ResourceInterface {
    public function getAllowedMethods() {
        return ['GET'];
    }

    public static function getSubscribedEvents() {
        return [
            'users.get' => 'get',
        ];
    }

    public function get(EventInterface $event) {
        $event->getResponse()->setModel(
            new ListModel('users', ['someuser', 'someotheruser']
        );
    }
}

This resource informs Imbo that it supports HTTP GET, and specifies a callback for the users.get event. The name of the event is the name specified for the resource in the configuration above, along with the HTTP method, separated with a dot.

In the get() method we are simply creating a list model for Imbo’s response formatter, and we are supplying a list of users to the model. When formatted as JSON the response looks like this:

{
  "users": [
    "someuser",
    "someotheruser"
  ]
}

Feel free to experiment with this feature. If you end up creating a resource that you think should be a part of Imbo, send a pull request on GitHub.

Redirect the index route - indexRedirect

The index resource (Index resource - /) simply lists some URLs related to the Imbo project. If you would rather the index resource redirect the client to some specific URL, set the indexRedirect configuration option to that URL:

<?php
return [
    // ...

    'indexRedirect' => 'https://github.com/imbo',

    // ...
];

Input loaders - inputLoaders

The inputLoaders configuration element is an associative array of input loaders. The values in the array must be strings representing a FQCN of an input loader, or an instance of an input loader. In both cases the class specified must implement the Imbo\Image\InputLoader\InputLoaderInterface interface.

The default configuration includes a “basic” input loader, that supports the following image types:

  • image/png
  • image/jpeg
  • image/gif
  • image/tiff

To add more input loaders, simply specify them in the configuration file:

<?php
return [
    // ...

    'inputLoaders' => [
        'custom-loader' => My\Custom\Loader::class,
        'another-custom-loader' => new My\Other\Custom\Loader(),
    ],

    // ...
];

The keys in the array are not used for anything in Imbo, but enables you to override default settings. The basic input loader uses the basic key as seen in the default configuration file.

Output converters - outputConverters

The outputConverters configuration element is an associative array of output converters. The values in the array must be strings representing a FQCN of an output converter, or an instance of an output converter. In both cases the class specified must implement the Imbo\Image\OutputConverter\OutputConverterInterface interface.

The default configuration includes a “basic” output converter, that supports the following image types:

  • image/png
  • image/jpeg
  • image/gif

To add more output converters, simply specify them in the configuration file:

<?php
return [
    // ...

    'outputConverters' => [
        'custom-converter' => My\Custom\Converter::class,
        'another-custom-converter' => new My\Other\Custom\Converter(),
    ],

    // ...
];

The keys in the array are not used for anything in Imbo, but enables you to override default settings. The basic output converter uses the basic key as seen in the default configuration file.

Customize your Imbo installation with event listeners

Imbo ships with a collection of event listeners for you to use. Some of them are enabled in the default configuration file. Image transformations are also technically event listeners, but will not be covered in this chapter. Read the Transforming images on the fly chapter for more information regarding the image transformations.

Access token

This event listener enforces the usage of access tokens on all read requests against user-specific resources. You can read more about how the actual access tokens work in the Access tokens part of the Imbo’s API chapter.

To enforce the access token check this event listener subscribes to the following events:

  • user.get
  • user.head
  • images.get
  • images.head
  • image.get
  • image.head
  • metadata.get
  • metadata.head

This event listener has a single parameter that can be used to whitelist and/or blacklist certain image transformations, used when the current request is against an image resource. The parameter is an array with a single key: transformations. This is another array with two keys: whitelist and blacklist. These two values are arrays where you specify which transformation(s) to whitelist or blacklist. The names of the transformations are the same as the ones used in the request. See Transforming images on the fly for a complete list of the supported transformations.

Use whitelist if you want the listener to skip the access token check for certain transformations, and blacklist if you want it to only check certain transformations:

[
    'transformations' => [
        'whitelist' => [
            'border',
        ],
    ],
]

means that the access token will not be enforced for the border transformation.

[
    'transformations' => [
        'blacklist' => [
            'border',
        ],
    ],
]

means that the access token will be enforced only for the border transformation.

If both whitelist and blacklist are specified all transformations will require an access token unless it’s included in whitelist.

This event listener is included in the default configuration file without specifying any transformation filters:

<?php
return [
    // ...

    'eventListeners' => [
        'accessToken' => 'Imbo\EventListener\AccessToken',
    ],

    // ...
];

Disable this event listener with care. Installations with no access token check is open for DoS attacks.

Custom Access Token Generators

You can customize how the access token is generated and which URL parameter that contain the access token. The default implementation uses a SHA256 HMAC generated from the private key and the URL of the request.

To switch to a different access token generator provide a new generator in the accessTokenGenerator parameter. This generator must implement the AccessTokenInterface interface, available under EventListener\AccessToken\AccessTokenInterface. An abstract class (AccessTokenGenerator) is included that you can subclass to quickly implement an alternative signature algorithm.

To use a different algorithm, provide it as a key in the parameter array when creating the access token event listener:

[
    'accessTokenGenerator' => new AccessToken\SHA256(),
]

You can also provide a list of URL argument names for the access token, if it’s not available through the regular accessToken argument.

[
    'accessTokenGenerator' => new AccessToken\SHA256(['argumentKeys' => ['foo', 'accessToken']]),
]

which would allow the token to be present under either foo or accessToken in the URL arguments.

Imbo uses a SHA256 HMAC as the default signature generation algorithm, available under EventListener\AccessToken\SHA256.

If you want to use multiple access token generators, you can use the bundled MultipleAccessTokensGenerators generator, which will pick a generator based on the available URL parameters (in sequence, the first match will be used).

[
    'accessTokenGenerator' => new AccessToken\MultipleAccessTokenGenerators([
        'generators' => [
            'accessToken' => new AccessToken\SHA256(),
            'dummy' => new DummyImplementation(),
            ...
        ],
    ]),
]

This will cause the SHA256 implementation to be used if an accessToken parameter is available, but if only the dummy parameter is present, the DummyImplementation generator will be used.

Authenticate

This event listener enforces the usage of signatures on all write requests against user-specific resources. You can read more about how the actual signature check works in the Signing write requests section in the Imbo’s API chapter.

To enforce the signature check for all write requests supported by Imbo this event listener subscribes to the following events:

  • images.post
  • image.delete
  • metadata.put
  • metadata.post
  • metadata.delete

This event listener does not support any parameters and is enabled per default like this:

<?php
return [
    // ...

    'eventListeners' => [
        'auth' => 'Imbo\EventListener\Authenticate',
    ],

    // ...
];

Disable this event listener with care. User agents can delete all your images and metadata if this listener is disabled.

Auto rotate image

This event listener will auto rotate new images based on metadata embedded in the image itself (EXIF).

The listener does not support any parameters and can be enabled like this:

<?php
return [
    // ...

    'eventListeners' => [
        'autoRotateListener' => 'Imbo\EventListener\AutoRotateImage',
    ],

    // ...
];

If you enable this listener all new images added to Imbo will be auto rotated based on the EXIF data. This might also cause the image identifier sent in the response to be different from the one used in the URI when storing the image. This can happen with all event listeners which can possibly modify the image before storing it.

CORS (Cross-Origin Resource Sharing)

This event listener can be used to allow clients such as web browsers to use Imbo when the client is located on a different origin/domain than the Imbo server is. This is implemented by sending a set of CORS-headers on specific requests, if the origin of the request matches a configured domain.

The event listener can be configured on a per-resource and per-method basis, and will therefore listen to any related events. If enabled without any specific configuration, the listener will allow and respond to the GET, HEAD and OPTIONS methods on all resources. Note however that no origins are allowed by default and that a client will still need to provide a valid access token, unless the Access token listener is disabled.

Here is an example on how to enable the CORS listener:

<?php
return [
    // ...

    'eventListeners' => [
        'cors' => [
            'listener' => 'Imbo\EventListener\Cors',
            'params' => [
                'allowedOrigins' => ['http://some.origin'],
                'allowedMethods' => [
                    'image'  => ['GET', 'HEAD'],
                    'images' => ['GET', 'HEAD', 'POST'],
                ],
                'maxAge' => 3600,
            ],
        ],
    ],

    // ...
];

Below all supported parameters are listed:

allowedOrigins
is an array of allowed origins. Specifying * as a value in the array will allow any origin.
allowedMethods
is an associative array where the keys represent the resource (shorturl, status, stats, user, images, image and metadata) and the values are arrays of HTTP methods you wish to open up.
maxAge
specifies how long the response of an OPTIONS-request can be cached for, in seconds. Defaults to 3600 (one hour).

EXIF metadata

This event listener can be used to fetch the EXIF-tags from uploaded images and adding them as metadata. Enabling this event listener will not populate metadata for images already added to Imbo.

The event listener subscribes to the following events:

  • images.post
  • db.image.insert

and the parameters given to the event listener supports a single element:

allowedTags
The tags you want to be populated as metadata. Defaults to exif:*. When specified it will override the default value, so if you want to register all exif and date tags for example, you will need to specify them both.

and is enabled like this:

<?php
return [
    // ...

    'eventListeners' => [
        'exifMetadata' => [
            'listener' => 'Imbo\EventListener\ExifMetadata',
            'params' => [
                'allowedTags' => ['exif:*', 'date:*', 'png:gAMA'],
            ],
        ],
    ],

    // ...
];

which would allow all exif and date properties as well as the png:gAMA property. If you want to store all tags as metadata, use ['*'] as filter.

Image transformation cache

This event listener enables caching of image transformations. Read more about image transformations in the Transforming images on the fly section.

To achieve this the listener subscribes to the following events:

  • image.get
  • response.send
  • image.delete

The parameters for the event listener supports a single element:

path
Root path where the cached images will be stored.

and is enabled like this:

<?php
return [
    // ...

    'eventListeners' => [
        'imageTransformationCache' => [
            'listener' => 'Imbo\EventListener\ImageTransformationCache',
            'params' => [
                'path' => '/path/to/cache',
            ],
        ],
    ],

    // ...
];

Note

This event listener uses a similar algorithm when generating file names as the Filesystem storage adapter.

Warning

It can be wise to purge old files from the cache from time to time. If you have a large amount of images and present many different variations of these the cache will use up quite a lot of storage.

An example on how to accomplish this:

$ find /path/to/cache -ctime +7 -type f -delete

The above command will delete all files in /path/to/cache older than 7 days and can be used with for instance crontab.

Image transformation limiter

Allows you to limit the number of transformations a user can apply to a single resource in a request. Read more about image transformations in the Transforming images on the fly section.

The listener accepts a single parameter, the number of transformations a user can apply:

(int) limit
The number of transformations to allow (count > limit will be denied with a 403 response code).
<?php
return [
    // ...

    'eventListeners' => [
        'imageTransformationLimiter' => [
            'listener' => 'Imbo\EventListener\ImageTransformationLimiter',
            'params' => [
                'limit' => 2,
            ],
        ],
    ],

    // ...
];

Image variations

This event listener can be used to generate multiple variations of incoming images so that a more suitable size can be used when performing scaling transformations. This will increase the amount of data stored by Imbo, but it will also improve performance. In cases where the original images are very large and the requested transformed images are significantly smaller, the difference will be quite drastic.

The event listener has two roles, one is to generate the variations when new images are added, and the other is to pick the most fitting image variation when clients request an image with a set of transformations applied that will alter the dimensions of the image, for instance resize or thumbnail.

Imbo ships with MongoDB and Doctrine adapters for storing metadata about these variations. If you want to use a different database, you can implement the Imbo\EventListener\ImageVariations\Database\DatabaseInterface interface and set the name of the class in the configuration of the event listener.

In the same way, Imbo ships two different adapters for storing the actual image variation data (the downscaled images): GridFS and Filesystem. See examples of their configuration below.

The event listener supports for following configuration parameters:

(boolean) lossless
Have Imbo use a lossless format to store the image variations. This results in better quality images, but converting between formats can be slower and will use more disk space. Defaults to false.
(boolean) autoScale
Have Imbo automatically figure out the widths of the image variations (based on other parameters). Defaults to true.
(float) scaleFactor
The factor to use when scaling. Defaults to 0.5 which basically generates variants half the size of the previous one.
(int) minDiff
When the difference of the width in pixels between two image variations fall below this limit, no more variants will be generated. Defaults to 100.
(int) minWidth
Do not generate image variations that ends up with a width in pixels below this level. Defaults to 100.
(int) maxWidth
Don’t start to generate image variations before the width of the variation falls below this limit. Defaults to 1024.
(array) widths
An array of widths to use when generating variations. This can be used together with the auto generation, and will ignore the rules related to auto generation. If you often request images with the same dimensions, you can significantly speed up the process by specifying the width here. Defaults to [].
(array) database

The database adapter to use. This array has two elements:

  • (string) adapter: The class name of the adapter. The class must implement the Imbo\EventListener\ImageVariations\Database\DatabaseInterface interface.
  • (array) params: Parameters for the adapter (optional).
(array) storage

The storage adapter to use. This array has two elements:

  • (string) adapter: The class name of the adapter. The class must implement the Imbo\EventListener\ImageVariations\Storage\StorageInterface interface.
  • (array) params: Parameters for the adapter (optional).

Examples:

  1. Automatically generate image variations

    Given the following configuration:

    return [
        // ...
    
        'eventListeners' => [
            'imageVariations' => [
                'listener' => 'Imbo\EventListener\ImageVariations',
                'params' => [
                    'database' => [
                        'adapter' => 'Imbo\EventListener\ImageVariations\Database\MongoDB',
                    ],
                    'storage' => [
                        'adapter' => 'Imbo\EventListener\ImageVariations\Storage\GridFS',
                    ],
                ],
            ],
        ],
    
        // ...
    ];
    

    when adding an image with dimensions 3082 x 2259, the following variations will be generated:

    • 770 x 564
    • 385 x 282
    • 192 x 140

    When later requesting this image with for instance ?t[]=resize:width=500 as transformation (read more about image transformations in the Transforming images on the fly chapter), Imbo will choose the image which is 770 x 564 pixels and downscale it to 500 pixels in width.

  2. Specify image widths:

    Given the following configuration:

    return [
        // ...
    
        'eventListeners' => [
            'imageVariations' => [
                'listener' => 'Imbo\EventListener\ImageVariations',
                'params' => [
                    'database' => [
                        'adapter' => 'Imbo\EventListener\ImageVariations\Database\MongoDB',
                    ],
                    'storage' => [
                        'adapter' => 'Imbo\EventListener\ImageVariations\Storage\GridFS',
                    ],
                    'autoScale' => false,
                    'widths' => [1000, 500, 200, 100, 50],
                ],
            ],
        ],
    
        // ...
    ];
    

    when adding an image with dimensions 3082 x 2259, the following variations will be generated:

    • 1000 x 732
    • 500 x 366
    • 200 x 146
    • 100 x 73
    • 50 x 36

    As you can see the minDiff and minWidth parameters are ignored when using the width parameter.

  3. Configuring database and storage adapters:

    As stated earlier, there are several different adapters to choose from when storing the variations metadata as well as the actual variation image files.

    The default adapter for the metadata database is MongoDB, and the default storage adapter is GridFS. They have the same configuration parameters:

    return [
        // ...
    
        'eventListeners' => [
            'imageVariations' => [
                'listener' => 'Imbo\EventListener\ImageVariations',
                'params' => [
                    'database' => [
                        'adapter' => 'Imbo\EventListener\ImageVariations\Database\MongoDB',
                        'params' => [
                            'databaseName' => 'imbo',
                            'server'  => 'mongodb://localhost:27017',
                            'options' => ['connect' => true, 'connectTimeoutMS' => 1000],
                        ]
                    ],
                    'storage' => [
                        'adapter' => 'Imbo\EventListener\ImageVariations\Storage\GridFS',
                        'params' => [
                            'databaseName' => 'imbo_storage',
                            'server'  => 'mongodb://localhost:27017',
                            'options' => ['connect' => true, 'connectTimeoutMS' => 1000],
                        ]
                    ],
                ],
            ],
        ],
    
        // ...
    ];
    

    The Doctrine adapter is an alternative for storing metadata. This adapter uses the Doctrine Database Abstraction Layer. When using this adapter you need to create the required tables in the RDBMS first, as specified in the Database setup section. Example usage:

    return [
        // ...
    
        'eventListeners' => [
            'imageVariations' => [
                'listener' => 'Imbo\EventListener\ImageVariations',
                'params' => [
                    'database' => [
                        'adapter' => 'Imbo\EventListener\ImageVariations\Database\Doctrine',
                        'params' => [
                            'dbname'    => 'imbo',
                            'user'      => 'imbo_rw',
                            'password'  => 'imbo_password',
                            'host'      => 'localhost',
                            'driver'    => 'mysql',
                            'tableName' => 'imagevariations', // Default value, can be omitted
                        ]
                    ],
                    'storage' => [
                        'adapter' => 'Imbo\EventListener\ImageVariations\Storage\GridFS',
                    ],
                ],
            ],
        ],
    
        // ...
    ];
    

    The other option for the storage adapter is the Filesystem adapter. It’s fairly straightforward and uses a similar algorithm when generating file names as the Filesystem storage adapter. Example usage:

    return [
        // ...
    
        'eventListeners' => [
            'imageVariations' => [
                'listener' => 'Imbo\EventListener\ImageVariations',
                'params' => [
                    'storage' => [
                        'adapter' => 'Imbo\EventListener\ImageVariations\Storage\Filesystem',
                        'params' => [
                            'dataDir' => '/path/to/image-variation-storage'
                        ]
                    ],
    
                    // Use any database adapter you want
                    'database' => [
                        'adapter' => 'Imbo\EventListener\ImageVariations\Database\Doctrine',
                    ],
                ],
            ],
        ],
    
        // ...
    ];
    

    Note

    When using the Filesystem adapter, the dataDir must be specified, and must be writable by the web server.

Imagick

This event listener is required by the image transformations that is included in Imbo, and there is no configuration options for it. Unless you plan on exchanging all the internal image transformations with your own (for instance implemented using Gmagick or GD) you are better off leaving this as-is.

Max image size

This event listener can be used to enforce a maximum size (height and width, not byte size) of new images. Enabling this event listener will not change images already added to Imbo.

The event listener subscribes to the following event:

  • images.post

and the parameters includes the following elements:

width
The max width in pixels of new images. If a new image exceeds this limit it will be downsized.
height
The max height in pixels of new images. If a new image exceeds this limit it will be downsized.

and is enabled like this:

<?php
return [
    // ...

    'eventListeners' => [
        'maxImageSizeListener' => [
            'listener' => 'Imbo\EventListener\MaxImageSize',
            'params' => [
                'width' => 1024,
                'height' => 768,
            ],
        ],
    ],

    // ...
];

which would effectively downsize all images exceeding a width of 1024 or a height of 768. The aspect ratio will be kept.

Stats access

This event listener controls the access to the stats resource by using white listing of IPv4 and/or IPv6 addresses. CIDR-notations are also supported.

This listener is enabled per default, and only allows 127.0.0.1 and ::1 to access the statistics:

<?php
return [
    // ...

    'eventListeners' => [
        'statsAccess' => [
            'listener' => 'Imbo\EventListener\StatsAccess',
            'params' => [
                'allow' => ['127.0.0.1', '::1'],
            ],
        ],
    ],

    // ...
];

The event listener also supports a notation for “allowing all”, simply by placing '*' somewhere in the list:

<?php
return [
    // ...

    'eventListeners' => [
        'statsAccess' => [
            'listener' => 'Imbo\EventListener\StatsAccess',
            'params' => [
                [
                    'allow' => ['*'],
                ]
            ],
        ],
    ],

    // ...
];

The above example will allow all clients access to the statistics.

Varnish HashTwo

This event listener can be enabled if you want Imbo to include HashTwo headers in responses to image requests. These headers can be used by Varnish for more effective cache invalidation strategies. The listener, when enabled, subscribes to the following events:

  • image.get
  • image.head

The parameters supports a single element:

headerName
Set the header name to use. Defaults to X-HashTwo.
<?php
return [
    // ...

    'eventListeners' => [
        'hashTwo' => 'Imbo\EventListener\VarnishHashTwo',
    ],

    // ...
];

or, if you want to use a non-default header name:

<?php
return [
    // ...

    'eventListeners' => [
        'hashTwo' => [
            'listener' => 'Imbo\EventListener\VarnishHashTwo',
            'params' => [
                'headerName' => 'X-Custom-HashTwo-Header-Name',
            ],
        ],
    ],

    // ...
];

The header appears multiple times in the response, with slightly different values:

X-HashTwo: imbo;image;<publicKey>;<imageIdentifier>
X-HashTwo: imbo;user;<publicKey>

Command line tool

Imbo ships with a command line tool which can aid you when setting up Imbo and keeping it up to date. This chapter lists all available commands and what they are capable of.

The binary can be found in one of two places, depending on the Installation method you chose. If you installed Imbo via Composer, the binary can be found in vendor/bin/imbo, and if you used git clone as the installation method it can be found in bin/imbo.

Add a public key - add-public-key

When using a mutable access control adapter (usually meaning it’s backed by a database or similar), this command helps you with adding public/private key pairs and associated rules. It’s an alternative to using Imbo’s public REST API for this purpose, and is the only way to add an initial public key with access to create and modify other public keys.

Example:

./bin/imbo add-public-key somePublicKey

The above command will start an interactive session that will guide you through creating a public key with the name somePublicKey, given it does not already exist.

Generate a private key - generate-private-key

The script that was earlier called scripts/generatePrivateKey.php is now included in the CLI tool. This commands does not support any arguments.

Example:

./bin/imbo generate-private-key

The above command will simply output a secret key that can be used as a private key for an Imbo user.

Help - help

Use this command to get a detailed description of another command along with available arguments and their effect on the command.

Example:

./bin/imbo help generate-private-key

The above command will provide a description of the generate-private-key command.

List commands - list

This command can be used to simply list all commands along with their short description. This is the default command that is executed when running ./bin/imbo with no arguments.

End user guide

Imbo’s API

In this chapter you will learn more about how Imbo’s API works, and how you as a user are able to read from and write to Imbo. Most examples listed in this chapter will use cURL, so while playing around with the API it’s encouraged to have cURL easily available. For the sake of simplicity the access tokens and authentication information is not used in the examples. See the Access tokens and Signing write requests sections for more information regarding this.

Resources/endpoints

In this section you will find information on the different resources Imbo’s RESTful API expose, along with their capabilities:

Index resource - /

The index resource shows some URL’s related to the Imbo project.

curl -H"Accept: application/json" http://imbo

results in:

{
  "site": "http://imbo.io",
  "source": "https://github.com/imbo/imbo",
  "issues": "https://github.com/imbo/imbo/issues",
  "docs": "http://docs.imbo.io"
}

The index resource does not require any authentication per default.

If you want the client to be redirected to some URL instead of displaying the above information, use the indexRedirect configuration option (Redirect the index route - indexRedirect).

Typical response codes:

  • 200 Hell Yeah
  • 307 Temporary Redirect

Note

The index resource is not cache-able.

Stats resource - /stats

Imbo provides an endpoint for fetching simple statistics about the data stored in Imbo.

curl http://imbo/stats.json

results in:

{
  "numImages": 12,
  "numUsers": 2,
  "numBytes": 3898294
  "custom": {}
}

if the client making the request is allowed access.

Access control

The access control for the stats endpoint is controlled by an event listener, which is enabled per default, and only allows connections from 127.0.0.1 (IPv4) and ::1 (IPv6). Read more about how to configure this event listener in the Stats access event listener section.

Custom statistics

The stats resource enables users to attach custom statistics via event listeners by using the data model as a regular associative array. The following example attaches a simple event listener in the configuration file that populates some custom data in the statistics model:

<?php
return [
    // ...

    'eventListeners' => [
        'customStats' => [
            'events' => ['stats.get'],
            'callback' => function($event) {
                // Fetch the model from the response
                $model = $event->getResponse()->getModel();

                // Set some values
                $model['someValue'] = 123;
                $model['someOtherValue'] = [
                    'foo' => 'bar',
                ];
                $model['someList'] = [1, 2, 3];
            }
        ],
    ],

    // ...
];

When requesting the stats endpoint, the output will look like this:

{
  "users": {
    "someuser": {
      "numImages": 11,
      "numBytes": 3817197
    },
    "someotheruser": {
      "numImages": 1,
      "numBytes": 81097
    }
  },
  "total": {
    "numImages": 12,
    "numUsers": 2,
    "numBytes": 3898294
  },
  "custom": {
    "someValue": 123,
    "someOtherValue": {
      "foo": "bar"
    },
    "someList": [1, 2, 3]
  }
}

Use cases for this might be to simply store data in some backend in various events (for instance image.get or metadata.get) and then fetch these and display then when requesting the stats endpoint (stats.get).

Note

The stats resource is not cache-able.

Status resource - /status

Imbo includes a simple status resource that can be used with for instance monitoring software.

curl http://imbo/status.json

results in:

{
  "timestamp": "Tue, 24 Apr 2012 14:12:58 GMT",
  "database": true,
  "storage": true
}

where timestamp is the current timestamp on the server, and database and storage are boolean values informing of the status of the current database and storage adapters respectively. If both are true the HTTP status code is 200 OK, and if one or both are false the status code is 503. When the status code is 503 the reason phrase will inform you whether it’s the database or the storage adapter (or both) that is having issues. As soon as the status code does not equal 200 Imbo will no longer work as expected.

The reason for adapter failures depends on what kind of adapter you are using. The file system storage adapter will for instance return a failure if it can no longer write to the storage directory. The MongoDB and Doctrine database adapters will fail if they can no longer connect to the server they are configured to communicate with.

Typical response codes:

  • 200 OK
  • 503 Database error
  • 503 Storage error
  • 503 Storage and database error

Note

The status resource is not cache-able.

Global short URL resource - /s/<id>

Images in Imbo can have short URLs associated with them, which are generated on demand when interacting with the short URLs resource. These URLs can be used in place of the rather long original URLs which includes both access tokens and transformations.

The format of the random ID part of the short URL can be matched with the following regular expression:

/^[a-zA-Z0-9]{7}$/

There are some caveats regarding the short URLs:

  1. If the data used to generate the short URL contained an image extension, content negotiation will not be applied to the short URL. You will always get the mime type associated with the extension used to generate the short URL.
  2. If the data used to generate the short URL did not contain an image extension you can use the Accept header to decide the mime type of the generated image when requesting the short URL.
  3. Short URLs do not support extensions, so you can not append .jpg to force image/jpeg. If you need to make sure the image is always a JPEG, simply add jpg as an extension when generating the short URL.

You can read more about how to generate these URLs in the short URLs section.

Note

In Imbo only images have short URLs

User resource - /users/<user>

The user resource represents a single user on the current Imbo installation. The output contains basic user information:

curl http://imbo/users/<user>.json

results in:

{
  "user": "<user>",
  "numImages": 42,
  "lastModified": "Wed, 18 Apr 2012 15:12:52 GMT"
}

where user is the user (the same used in the URI of the request), numImages is the number of images the user has stored in Imbo and lastModified is when the user last uploaded or deleted an image, or when the user last updated metadata of an image. If the user has not added any images yet, the lastModified value will be set to the current time on the server.

Typical response codes:

  • 200 OK
  • 304 Not modified
  • 404 User not found
Images resource - /users/<user>/images

The images resource is the collection of images owned by a specific user. This resource can be used to search added images, and is also used to add new images to a collection.

Add an image

To be able to display images stored in Imbo you will first need to add one or more images. This is done by requesting this endpoint with an image attached to the request body, and changing the HTTP METHOD to POST. The body of the response for such a request contains a JSON object containing the image identifier of the added image:

curl -XPOST http://imbo/users/<user>/images --data-binary @<file to add>

results in:

{
  "imageIdentifier": "<imageIdentifier>",
  "width": <width>,
  "height": <height>,
  "extension": "<extension>"
}

The <imageIdentifier> in the response is the identifier of the added image. This is used with the image resource<>. The response body also contains the width, height and extension of the image that was just added.

Typical response codes:

  • 200 OK
  • 201 Created
  • 400 Bad request
Get image collections

The images resource can also be used to gather information on which images a user owns. This is done by requesting this resource using HTTP GET. Supported query parameters are:

page
The page number. Defaults to 1.
limit
Number of images per page. Defaults to 20.
metadata
Whether or not to include metadata in the output. Defaults to 0, set to 1 to enable.
from
Fetch images starting from this Unix timestamp.
to
Fetch images up until this timestamp.
fields[]
An array with fields to display. When not specified all fields will be displayed.
sort[]
An array with fields to sort by. The direction of the sort is specified by appending asc or desc to the field, delimited by :. If no direction is specified asc will be used. Example: ?sort[]=size&sort[]=width:desc is the same as ?sort[]=size:asc&sort[]=width:desc. If no sort is specified Imbo will sort by the date the images was added, in a descending fashion.
ids[]
An array of image identifiers to filter the results by.
checksums[]
An array of image checksums to filter the results by.
originalChecksums[]
An array of the original image checksums to filter the results by.
curl "http://imbo/users/<user>/images.json?limit=1&metadata=1"

results in:

{
  "search": {
    "hits": 3,
    "page": 1,
    "limit": 1,
    "count": 1
  },
  "images": [
    {
      "added": "Mon, 10 Dec 2012 11:57:51 GMT",
      "updated": "Mon, 10 Dec 2012 11:57:51 GMT",
      "checksum": "<checksum>",
      "originalChecksum": "<originalChecksum>",
      "extension": "png",
      "size": 6791,
      "width": 1306,
      "height": 77,
      "mime": "image/png",
      "imageIdentifier": "<image>",
      "user": "<user>",
      "metadata": {
        "key": "value",
        "foo": "bar"
      }
    }
  ]
}

The search object is data related to pagination, where hits is the number of images found by the query, page is the current page, limit is the current limit, and count is the number of images in the visible collection.

The images list contains image objects. Each object has the following fields:

  • added: A formatted date of when the image was added to Imbo.
  • updated: The formatted date of when the image was last updated (read: when metadata attached to the image was last updated, as the image itself never changes).
  • checksum: The MD5 checksum of the image blob stored in Imbo.
  • originalChecksum: The MD5 checksum of the original image. Might differ from <checksum> if event listeners that might change incoming images have been enabled. This field was added to Imbo in version 1.2.0. If this field is null when you query the images resource, you will need to manually update the database. If you have event listeners changing incoming images you might not want to simply set the original checksum to <checksum> as that might not be true.
  • extension: The original image extension.
  • size: The size of the image in bytes.
  • width: The width of the image in pixels.
  • height: The height of the image in pixels.
  • mime: The mime type of the image.
  • imageIdentifier: The image identifier stored in Imbo.
  • user: The user who owns the image.
  • metadata: A JSON object containing metadata attached to the image. This field is only available if the metadata query parameter described above is used.

Typical response codes:

  • 200 OK
  • 304 Not modified
  • 404 User not found
Image resource - /users/<user>/images/<image>

The image resource represents specific images owned by a user. This resource is used to retrieve and remove images. It’s also responsible for transforming the images based on the transformation parameters in the query.

Fetch images

Fetching images added to Imbo is done by requesting the image identifiers of the images.

curl http://imbo/users/<user>/images/<image>

results in:

<binary data of the original image>

When fetching images Imbo also sends a set of custom HTTP response headers related to the image:

X-Imbo-Originalextension: png
X-Imbo-Originalmimetype: image/png
X-Imbo-Originalfilesize: 45826
X-Imbo-Originalheight: 390
X-Imbo-Originalwidth: 380

These are all related to the image that was just requested.

How to use this resource to generate image transformations is described in the Transforming images on the fly chapter.

Typical response codes:

  • 200 OK
  • 304 Not modified
  • 400 Bad request
  • 404 Image not found
Delete images

Deleting images from Imbo is accomplished by requesting the image URIs using HTTP DELETE. All metadata attached to the image will be removed as well.

curl -XDELETE http://imbo/users/<user>/images/<image>

results in:

{
  "imageIdentifier": "<image>"
}

where <image> is the image identifier of the image that was just deleted (the same as the one used in the URI).

Typical response codes:

  • 200 OK
  • 400 Bad request
  • 404 Image not found
Short URLs resource - /users/<user>/images/<image>/shorturls

This resource is used to create short URLs for images on demand, as well as removing all short URLs associated with a single image.

Create a short URL

Creating a short URL is done by requesting this resource using HTTP POST while including some parameters for the short URL in the request body. The parameters must be specified as a JSON object, and the object supports the following fields:

  • imageIdentfier: The same image identifier as the one in the requested URI.
  • user: The same user as the one in the requested URI.
  • extension: An optional extension to the image, for instance jpg or png.
  • query: The query string with transformations that will be applied. The format is the same as when requesting the image resource with one or more transformations. See the Transforming images on the fly chapter for more information regarding the transformation of images.

The generated ID of the short URL can be found in the response:

curl -XPOST http://imbo/users/<user>/images/<image>/shorturls.json -d '{
  "imageIdentifier": "<image>",
  "user": "<user>",
  "extension": "jpg",
  "query": "t[]=thumbnail:width=75,height=75&t[]=desaturate"
}'

results in:

{
  "id": "<id>"
}

where <id> can be used with the global short URL resource for requesting the image with the configured extension / transformations applied.

Delete all short URLs associated with an image

If you want to remove all short URLs associated with an image, you can request this resource using HTTP DELETE:

curl -XDELETE http://imbo/users/<user>/images/<image>/shorturls.json

results in:

{
  "imageIdentifier": "<image>"
}
Short URL resource - /users/<user>/images/<image>/shorturls/<id>

This resource can be used to remove a single short URL for a specific image variation.

This is achieved by simply requesting the resource with HTTP DELETE, specifying the ID of the short URL in the URI:

curl -XDELETE http://imbo/users/<user>/images/<image>/shorturls/<id>

results in:

{
  "id": "<id>"
}
Metadata resource - /users/<user>/images/<image>/metadata

Imbo can also be used to attach metadata to the stored images. The metadata is based on a simple key => value model, for instance:

  • category: Music
  • band: Koldbrann
  • genre: Black metal
  • country: Norway

Values can be nested key => value pairs.

Adding/replacing metadata

To add (or replace all existing metadata) on an image a client should make a request against this resource using HTTP PUT with the metadata attached in the request body as a JSON object. The response body will contain the added metadata.

curl -XPUT http://imbo/users/<user>/images/<image>/metadata.json -d '{
  "beer":"Dark Horizon First Edition",
  "brewery":"Nøgne Ø",
  "style":"Imperial Stout"
}'

results in:

{
  "beer": "Dark Horizon First Edition",
  "brewery": "Nøgne Ø",
  "style": "Imperial Stout"
}

Note

When using the Doctrine database adapter, metadata keys can not contain ::.

Typical response codes:

  • 200 OK
  • 400 Bad request
  • 400 Invalid metadata (when using the Doctrine adapter, and keys contain ::)
  • 404 Image not found
Partially updating metadata

Partial updates to metadata attached to an image is done by making a request with HTTP POST and attaching metadata to the request body as a JSON object. If the object contains keys that already exists in the metadata on the server the old values will be replaced by the ones found in the request body. New keys will be added to the metadata. The response will contain all metadata attached to the image after the update.

curl -XPOST http://imbo/users/<user>/images/<image>/metadata.json -d '{
  "ABV":"16%",
  "score":"100/100"
}'

results in:

{
  "beer": "Dark Horizon First Edition",
  "brewery": "Nøgne Ø",
  "style": "Imperial Stout",
  "ABV":"16%",
  "score":"100/100"
}

if the image already included the first three keys as metadata.

Note

When using the Doctrine database adapter, metadata keys can not contain ::.

Typical response codes:

  • 200 OK
  • 400 Bad request
  • 400 Invalid metadata (when using the Doctrine adapter, and keys contain ::)
  • 404 Image not found
Fetch metadata

Requests using HTTP GET on this resource returns all metadata attached to an image.

curl http://imbo/users/<user>/images/<image>/metadata.json

results in:

{}

when there is no metadata stored, or for example

{
  "category": "Music",
  "band": "Koldbrann",
  "genre": "Black metal",
  "country": "Norway"
}

if the image has metadata attached to it.

Typical response codes:

  • 200 OK
  • 304 Not modified
  • 404 Image not found
Remove metadata

To remove metadata attached to an image a request using HTTP DELETE can be made.

curl -XDELETE http://imbo/users/<user>/images/<image>/metadata.json

results in:

{}

Typical response codes:

  • 200 OK
  • 400 Bad request
  • 404 Image not found
Global images resource - /images

The global images resource is used to search for images across users, given that the public key has access to the images of these users.

This resource is read only, and behaves in the same way as described in the Get image collections section of Images resource - /users/<user>/images. In addition to the parameters specified for Get image collections, the following query parameter can be specified:

users[]
An array of users to get images for.
curl "http://imbo/images?users[]=foo&users[]=bar"

results in a response with the exact same format as shown under Get image collections.

If the users[] parameter is not set, the endpoint will return images from all users, granted that the public key has access to all users present in the database.

Public key resource - /keys/<publicKey>

The public key resource provides a way for clients to dynamically add, remove and update public keys to be used as part of the access control routines. Not all access control adapters implement this functionality - in this case the configuration is done through configuration files.

A private key does not have any specific requirements, while a public key must match the following regular expression:

/^[a-zA-Z0-9_-]{1,}$/
Add a public key

Every public key must also have a private key, which is used to sign and generate access tokens for requests. This is the only required parameter in the request body.

curl -XPUT http://imbo/keys/<publicKey>.json -d '{"privateKey":"<privateKey>"}'
Check if a public key exist

A HEAD request can be used if you want to check if a public key exist. The public key used to sign the request must have access to the keys.head resource.

curl -XHEAD http://imbo/keys/<publicKey>

Typical response codes:

  • 200 OK
  • 404 Public key not found
Change private key for a public key

Use the same method as when adding a public key to change the private key.

Remove a public key

Public keys can be removed using a DELETE request. The public key used to sign the request must have access to the keys.delete resource.

curl -XDELETE http://imbo/keys/<publicKey>.json

Typical response codes:

  • 200 OK
  • 201 Created
  • 400 Bad request
  • 404 Public key not found
  • 405 Access control adapter is immutable

Note

The keys resource is not cache-able.

Groups resource - /groups

The groups resource can list available resource groups, used in the access control routines.

List resource groups

Requests using HTTP GET on this resource returns all available resource groups. Supported query parameters are:

page
The page number. Defaults to 1.
limit
Number of groups per page. Defaults to 20.
curl http://imbo/groups.json

results in:

{"search":{"hits":0,"page":1,"limit":20,"count":0},"groups":[]}

when there are no resource groups defined, or for example

{
  "search": {
    "hits": 1,
    "page": 1,
    "limit": 20,
    "count": 1
  },
  "groups": [
    {
      "name": "read-stats",
      "resources": [
        "user.get",
        "user.head",
        "user.options"
      ]
    }
  ]
}

if there are resource groups defined.

Typical response codes:

  • 200 OK
Group resource - /groups/<groupName>

The group resource enables adding, modifying and deleting resource groups used in the access control routine. Not all access control adapters allow modification of groups - in this case the configuration is done through configuration files, and PUT/DELETE operations will result in an HTTP 405 response.

List resources of a group

Requests using HTTP GET on this resource returns all the resources the group consists of.

curl http://imbo/groups/<group>.json

results in:

{
  "resources": [
    "user.get",
    "user.head",
    "user.options"
  ]
}
Add/modify resources for a group

Requests using HTTP PUT on this resource either adds a new group with the given name, or if it already exists, updates it. The request body should contain an array of resources the group should consist of.

curl -XPUT http://imbo/groups/<group>.json -d '[
  "user.get",
  "stats.get"
]'
Delete a resource group

Requests using HTTP DELETE on this resource will remove the entire resource group. Note that any access control rules that are using this resource group will also be deleted, since they are now invalid.

curl -XDELETE http://imbo/groups/<group>.json

Typical response codes:

  • 200 OK
  • 201 Created
  • 404 Group not found
  • 405 Access control adapter is immutable
Access rules resource - /keys/<publicKey>/access

The access rules endpoint allows you to add rules that give a public key access to a specified set of resources. These rules can also be defined on a per-user basis. Instead of defining a list of resources, you also have the option to specify a resource group.

Listing access control rules

Requests using HTTP GET on this resource returns all the access control rules defined for the given public key.

curl http://imbo/keys/<publicKey>/access.json

results in:

[
  {
    "id": 1,
    "resources": ['images.get', 'image.get', 'images.post', 'image.delete'],
    "users": [
      "user1",
      "user2"
    ]
  },
  {
    "id": 2,
    "group": "read-stats",
    "users": [
      "user1",
      "user2"
    ]
  }
]
Adding access control rules

Requests using HTTP POST on this resource adds new rules to the given public key. The request body should contain an array of rules. The parameters for a rule must be specified as JSON objects, where the object supports the following fields:

  • users: Defines on which users the public key should have access to the defined resources. Either an array of users or the string * (all users).
  • resources: An array of resources you want the public key to have access to.
  • group: A resource group the public key should have access to.

Note

A rule must contain either resources or group, not both. users is required.

curl -XPOST http://imbo/keys/<publicKey>/access -d '[{
  "resources": ["user.get", "image.get", "images.get", "metadata.get"],
  "users": "*"
}]'

Typical response codes:

  • 200 OK
  • 400 No access rule data provided
  • 404 Public key not found
  • 405 Access control adapter is immutable
Access rule resource - /keys/<publicKey>/access/<ruleId>

The access rule endpoint allows you to see which resources and users a given access control rule contains. It also allows you to remove a specific access control rule.

Get access rule details
curl http://imbo/keys/<publicKey>/access/<ruleId>.json

results in:

{
  "id": 1,
  "resources": ['images.get', 'image.get', 'images.post', 'image.delete'],
  "users": [
    "user1",
    "user2"
  ]
}
Removing an access rule

Requests using HTTP DELETE on this resource removes the access control rule, given the access control adapter supports mutations.

curl -XDELETE http://imbo/keys/<publicKey>/access/<ruleId>

Typical response codes:

  • 200 OK
  • 404 Public key not found
  • 404 Access rule not found
  • 405 Access control adapter is immutable

Access tokens

Access tokens are enforced by an event listener that is enabled in the default configuration file. The access tokens are used to prevent DoS attacks so think twice before you disable the event listener.

An access token, when enforced by the event listener, must be supplied in the URI using the accessToken query parameter and without it, most GET and HEAD requests will result in a 400 Bad request response. The value of the accessToken parameter is a Hash-based Message Authentication Code (HMAC). The code is a SHA-256 hash of the URI itself using the private key of the user as the secret key. It is very important that the URI is not URL-encoded when generating the hash. Below is an example on how to generate a valid access token for a specific image using PHP:

 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
<?php
$user       = "<user>";         // The user id
$publicKey  = "<public key>";   // The public key of the user
$privateKey = "<secret value>"; // The private key of the user
$image      = "<image>";        // The image identifier

// Image transformations
$query = [
    "t[]=thumbnail:width=40,height=40,fit=outbound",
    "t[]=border:width=3,height=3,color=000",
    "t[]=canvas:width=100,height=100,mode=center"
];

// Add a query parameter for public key if it differs from the user
if ($user != $publicKey) {
    $query[] = 'publicKey=' . $publicKey;
}

// The URI
$uri = sprintf("http://imbo/users/%s/images/%s?%s", $user, $image, implode('&', $query));

// Generate the token
$accessToken = hash_hmac("sha256", $uri, $privateKey);

// Output the URI with the access token
echo sprintf("%s&accessToken=%s", $uri, $accessToken) . PHP_EOL;

and Python:

 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
from hashlib import sha256
import hmac

user       = "<user>"         # The user id
publicKey  = "<public key>"   # The public key of the user
privateKey = "<secret value>" # The private key of the user
image      = "<image>"        # The image identifier

# Image transformations
query = [
    "t[]=thumbnail:width=40,height=40,fit=outbound",
    "t[]=border:width=3,height=3,color=000",
    "t[]=canvas:width=100,height=100,mode=center"
]

# Specify public key if it differs from user
if user != publicKey:
    query.append('publicKey=%s' % (publicKey))

# The URI
uri = "http://imbo/users/%s/images/%s?%s" % (user, image, "&".join(query))

# Generate the token
accessToken = hmac.new(privateKey, uri, sha256)

# Output the URI with the access token
print "%s&accessToken=%s" % (uri, accessToken.hexdigest())

and Ruby:

 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
require "digest"

user       = "<user>"         # User id
publicKey  = "<public key>"   # The public key of the user
privateKey = "<secret value>" # The private key of the user
image      = "<image>"        # The image identifier

# Image transformations
queryParts = [
    "t[]=thumbnail:width=40,height=40,fit=outbound",
    "t[]=border:width=3,height=3,color=000",
    "t[]=canvas:width=100,height=100,mode=center"
]

# Specify public key if it differs from user
if user != publicKey
    queryParts.push("publicKey=#{publicKey}")
end

# Join the query parts
query = queryParts.join("&")

# The URI
uri = "http://imbo/users/#{user}/images/#{image}?#{query}"

# Generate the token
accessToken = Digest::HMAC.hexdigest(uri, privateKey, Digest::SHA256)

# Output the URI with the access token
puts "#{uri}&accessToken=#{accessToken}"

If the event listener enforcing the access token check is removed, Imbo will ignore the accessToken query parameter completely. If you wish to implement your own form of access token you can do this by either implementing an event listener of your own (see Writing an event listener for more information), or by writing a new access token signature generator for the access token event listener.

Prior to Imbo-1.0.0 there was no rule that the URL had to be completely URL-decoded prior to generating the access token in the clients. Because of this Imbo will also try to re-generate the access token server side by using the URL as-is. This feature has been added to ease the transition to Imbo >= 1.0.0, and will be removed some time in the future.

Signing write requests

To be able to write to Imbo the user agent will have to specify two request headers: X-Imbo-Authenticate-Signature and X-Imbo-Authenticate-Timestamp.

X-Imbo-Authenticate-Signature is, like the access token, an HMAC (also using SHA-256 and the private key of the user).

The data for the hash is generated using the following elements:

  • HTTP method (PUT, POST or DELETE)
  • The URI
  • Public key
  • GMT timestamp (YYYY-MM-DDTHH:MM:SSZ, for instance: 2011-02-01T14:33:03Z)

These elements are concatenated in the above order with | as a delimiter character, and a hash is generated using the private key of the user. The following snippet shows how this can be accomplished in PHP when deleting an image:

 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
<?php
$user       = "<user>";                 // User id
$publicKey  = "<public key>";           // The public key of the user
$privateKey = "<secret value>";         // The private key of the user
$timestamp  = gmdate("Y-m-d\TH:i:s\Z"); // Current timestamp (UTC)
$image      = "<image>";                // The image identifier
$method     = "DELETE";                 // HTTP method to use

// The URI
$uri = sprintf("http://imbo/users/%s/images/%s", $user, $image);

// Data for the hash
$data = implode("|", [$method, $uri, $publicKey, $timestamp]);

// Generate the token
$signature = hash_hmac("sha256", $data, $privateKey);

// Request the URI
$response = file_get_contents($uri, false, stream_context_create([
    "http" => [
        "method" => $method,
        "header" => [
            "X-Imbo-PublicKey " . $publicKey,
            "X-Imbo-Authenticate-Signature: " . $signature,
            "X-Imbo-Authenticate-Timestamp: " . $timestamp,
        ],
    ],
]));

and Python (using the Requests library):

 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
import hmac, requests
from hashlib import sha256
from datetime import datetime

user       = "<user>"                                         # The user ID
publicKey  = "<public key>"                                   # The public key of the user
privateKey = "<secret value>"                                 # The private key of the user
timestamp  = datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ") # Current timestamp (UTC)
image      = "<image>"                                        # The image identifier
method     = "DELETE"                                         # HTTP method to use

# The URI
uri = "http://imbo/users/%s/images/%s" % (user, image)

# Data for the hash
data = "|".join([method, uri, publicKey, timestamp])

# Generate the token
signature = hmac.new(privateKey, data, sha256).hexdigest()

# Request the URI
response = requests.delete(uri, headers = {
    "X-Imbo-PublicKey": publicKey,
    "X-Imbo-Authenticate-Signature": signature,
    "X-Imbo-Authenticate-Timestamp": timestamp
})

and Ruby (using the httpclient gem):

 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
require "digest"
require "httpclient"

user       = "<user>"                                    # The user ID
publicKey  = "<public key>"                              # The public key of the user
privateKey = "<secret value>"                            # The private key of the user
timestamp  = Time.now.utc.strftime("%Y-%m-%dT%H:%M:%SZ") # Current timestamp (UTC)
image      = "<image>"                                   # The image identifier
method     = "DELETE"                                    # HTTP method to use

# The URI
uri = "http://imbo/users/#{user}/images/#{image}"

# Data for the hash
data = [method, uri, publicKey, timestamp].join("|")

# Generate the token
signature = Digest::HMAC.hexdigest(data, privateKey, Digest::SHA256)

# Request the URI
client = HTTPClient.new
response = client.delete(uri, {}, {
    "X-Imbo-PublicKey" => publicKey,
    "X-Imbo-Authenticate-Signature" => signature,
    "X-Imbo-Authenticate-Timestamp" => timestamp
})

Imbo requires that X-Imbo-Authenticate-Timestamp is within ± 120 seconds of the current time on the server.

As with the access token the signature check is enforced by an event listener that can also be disabled. If you disable this event listener you effectively open up for writing from anybody, which you probably don’t want to do.

If you want to implement your own authentication paradigm you can do this by creating a custom event listener.

Supported content types

Imbo currently responds with images (jpg, gif and png) and JSON and accepts images (jpg, gif and png) and JSON as input.

Imbo will do content negotiation using the Accept header found in the request, unless you specify a file extension, in which case Imbo will deliver the type requested without looking at the Accept header.

The only supported content type for non-image responses is JSON. Theses responses can be wrapped in a function (JSONP) by using one of the following query parameters:

  • callback
  • jsonp
  • json
curl http://imbo/status.json?callback=func

will result in:

func(
  {
    "date": "Mon, 05 Nov 2012 19:18:40 GMT",
    "database": true,
    "storage": true
  }
)

For images the default mime-type is the original mime-type of the image. If you add an image/gif image and fetch that image with Accept: */* or Accept: image/* the mime-type of the image returned will be image/gif. To choose a different mime type either change the Accept header, or use .jpg or .png (for image/jpeg and image/png respectively).

An exception to this is if the configuration option contentNegotiateImages is set to false, in which case Imbo will not convert the image to a different format than the original, unless explicitly told to do so by specifying an extension (.jpg, .png, .gif etc).

Cache headers

Most responses from Imbo includes a set of cache-related headers that enables shared caches and user agents to cache content.

Cache-Control

Some responses from Imbo are not cache-able. These will typically include Cache-Control: max-age=0, no-store, private. The following resources are not cache-able:

All other resources will include Cache-Control: public. The image and short url resources will also set a max-age, resulting in the following header: Cache-Control: max-age=31536000, public.

ETag

Imbo provides entity tags for cache validation mechanisms. User agents can use the ETag response header to do conditional requests further down the road (by specifying the original ETag value in the If-None-Match request header). This results in saved bandwidth as web caches and Imbo servers no longer need to send the response body, as the one cached by the user agent can be re-used. This is achieved by sending 304 Not Modified back to the user agent, instead of 200 OK.

The following resources in Imbo will include an ETag:

The value of the ETag header is simply the MD5 sum of the content in the response body, enclosed in quotes. For instance ETag: "fd2fd87a2f5288be31c289e70e916123".

Last-Modified

Imbo also includes a Last-Modified response header for resources that has a know last modification date, and these resources are:

User agents can use the value of the Last-Modified header in the If-Modified-Since request header to make a conditional request. The value of the Last-Modified header is an HTTP-date, for instance Last-Modified: Wed, 12 Feb 2014 09:46:02 GMT.

Errors

When an error occurs Imbo will respond with a fitting HTTP response code along with a JSON object explaining what went wrong.

curl -g "http://imbo/users/<user>/foobar"

results in:

{
  "error": {
    "imboErrorCode": 0,
    "date": "Wed, 12 Dec 2012 21:15:01 GMT",
    "message": "Not Found",
    "code": 404
  }
}

The code is the HTTP response code, message is a human readable error message, date is when the error occurred on the server, and imboErrorCode is an internal error code that can be used by the user agent to distinguish between similar errors (such as 400 Bad request).

The JSON object will also include imageIdentifier if the request was made against the image or the metadata resource.

Transforming images on the fly

What you as an end-user of an Imbo installation will be doing most of the time, is working with images. This is what Imbo was originally made for, and this chapter includes details about all the different image transformations Imbo supports.

All image transformations can be triggered by specifying the t query parameter. This parameter must be used as an array so that you can provide several image transformations. The transformations will be applied to the image in the same order as they appear in the URL. Each element in this array represents a single transformation with optional parameters, specified as a string. If the t query parameter is not an array or if any of its elements are not strings, Imbo will respond with HTTP 400.

Below you will find all image transformations supported “out of the box”, along with their parameters. Some transformations are rarely used with HTTP GET, but are instead used by event listeners that transform images when they are added to Imbo (HTTP POST). If this is the case it will be mentioned in the description of the transformation.

Auto rotate image based on EXIF data - t[]=autoRotate

This transformation will auto rotate the image based on EXIF data stored in the image. This transformation is rarely used per request, but is typically used by the Auto rotate image event listener when adding images to Imbo.

Examples:

  • t[]=autoRotate

Blur the image - t[]=blur

This transformation can be used to blur the image.

Parameters:

mode

The blur type (optional). Defaults to gaussian. Possible values are:

gaussian
When adding gaussian blur, the radius and sigma parameters are required.
adaptive
When adding adaptive blur, the radius and sigma parameters are required. Adaptive blur decrease the blur in the part of the picture near to the edge of the image canvas.
motion
When adding motion blur, the radius, sigma and angle parameters are required.
radial
When adding radial blur, the angle parameter is required.
radius
The radius of the Gaussian, in pixels, not counting the center pixel.
sigma
The standard deviation of the Gaussian, in pixels.
angle
The number of degrees to rotate the image.

Examples:

  • t[]=blur:radius=1,sigma=2
  • t[]=blur:type=adaptive,radius=2,sigma=4

Add an image border - t[]=border

This transformation will apply a border around the image.

Parameters:

color
Color of the border in hexadecimal. Defaults to 000000 (You can also specify short values like f00 (ff0000)).
width
Width of the border in pixels on the left and right sides of the image. Defaults to 1.
height
Height of the border in pixels on the top and bottom sides of the image. Defaults to 1.
mode
Mode of the border. Can be inline or outbound. Defaults to outbound. Outbound places the border outside of the image, increasing the dimensions of the image. inline paints the border inside of the image, retaining the original width and height of the image.

Examples:

  • t[]=border
  • t[]=border:mode=inline
  • t[]=border:color=000
  • t[]=border:color=f00,width=2,height=2

Expand the image canvas - t[]=canvas

This transformation can be used to change the canvas of the original image.

Parameters:

width
Width of the surrounding canvas in pixels. If omitted the width of <image> will be used.
height
Height of the surrounding canvas in pixels. If omitted the height of <image> will be used.
mode
The placement mode of the original image. free, center, center-x and center-y are available values. Defaults to free.
x
X coordinate of the placement of the upper left corner of the existing image. Only used for modes: free and center-y.
y
Y coordinate of the placement of the upper left corner of the existing image. Only used for modes: free and center-x.
bg
Background color of the canvas. Defaults to ffffff (also supports short values like f00 (ff0000)).

Examples:

  • t[]=canvas:width=200,mode=center
  • t[]=canvas:width=200,height=200,x=10,y=10,bg=000
  • t[]=canvas:width=200,height=200,x=10,mode=center-y
  • t[]=canvas:width=200,height=200,y=10,mode=center-x

Compress the image - t[]=compress

This transformation compresses images on the fly resulting in a smaller payload. It is advisable to only use this transformation in combination with an image type in the URL (for instance .jpg or .png). This transformation is not applied to images of type image/gif.

Parameters:

level
The level of the compression applied to the image. The effect this parameter has on the image depends on the type of the image. If the image in the response is an image/jpeg a high level means high quality, usually resulting in larger files. If the image in the response is an image/png a high level means high compression, usually resulting in smaller files. If you do not specify an image type in the URL the result of this transformation is not deterministic as clients have different preferences with regards to the type of images they want to receive (via the Accept request header).

Examples:

  • t[]=compress:level=40

Change image contrast - t[]=contrast

This transformation can be used to change the contrast of the colors in the image.

Parameters:

alpha
Used to adjust the intensity differences between the lighter and darker elements of the image. Can also be negative. Note: this parameter was named sharpen in Imbo 1.x.
beta
Where the midpoint of the gradient will be. This value should be in the range 0 to 1. Default: 0.5.

Examples:

  • t[]=contrast:alpha=3

Note

If you are getting different results than expected when using negative alpha values, your imagick extension is probably built against an old version of ImageMagick.

Convert the image type - .jpg/.gif/.png

This transformation can be used to change the image type. It is not applied like the other transformations, but is triggered when specifying a custom extension to the <image>. Currently Imbo can convert to:

  • image/jpeg
  • image/png
  • image/gif

Examples:

  • curl http://imbo/users/<user>/images/<image>.gif
  • curl http://imbo/users/<user>/images/<image>.jpg
  • curl http://imbo/users/<user>/images/<image>.png

Crop the image - t[]=crop

This transformation is used to crop the image.

Parameters:

x
The X coordinate of the cropped region’s top left corner.
y
The Y coordinate of the cropped region’s top left corner.
width
The width of the crop in pixels.
height
The height of the crop in pixels.
mode

The crop mode (optional). Possible values are:

center
When using the center mode the x and y parameters are ignored, and the center of the cropped area is placed in the center of the original image.
center-x
Center the crop on the x-axis. Use the y parameter to control the upper edge of the crop.
center-y
Center the crop on the y-axis. Use the x parameter to control the left edge of the crop.

Examples:

  • t[]=crop:x=10,y=25,width=250,height=150
  • t[]=crop:width=100,height=100,mode=center
  • t[]=crop:width=50,height=50,mode=center-x,y=15
  • t[]=crop:width=50,height=50,mode=center-y,x=15

Make a gray scaled image - t[]=desaturate

This transformation desaturates the image (in practice, gray scales it).

Examples:

  • t[]=desaturate

Draw points of interest - t[]=drawPois

This transformation will draw an outline around all the POIs (points of interest) stored in the metadata for the image. The format of the metadata is documented under the smartSize transformation.

Parameters:

color
Color of the border in hexadecimal format. Defaults to ff0000 (You can also specify short values like f0f (ff00ff)).
borderSize
Width of the border in pixels. Defaults to 2.
pointSize
The diameter (in pixels) of the circle drawn around points of interest that do not have a height and width specified. Defaults to 30.

Examples:

  • t[]=drawPois
  • t[]=drawPois:borderSize=10
  • t[]=drawPois:color=0f0
  • t[]=drawPois:color=00f,borderSize=10,pointSize=100

Note

This transformation has a bug/limitation: all coordinates are based on the original image. In other words, applying this at the end of a transformation chain which resizes/crops/rotates the image can lead to unexpected results. This will hopefully change in the future.

Make a mirror image - t[]=flipHorizontally

This transformation flips the image horizontally.

Examples:

  • t[]=flipHorizontally

Flip the image upside down - t[]=flipVertically

This transformation flips the image vertically.

Examples:

  • t[]=flipVertically

Generate a histogram of the image - t[]=histogram

This transformation will convert the image into a histogram of the image itself, with a size of 256x158 pixels. The size of the generated image can be overridden by using one or more of the supported parameters.

Parameters:

scale
The amount to scale the histogram. Defaults to 1.
ratio
The ratio to use when calculating the height of the image. Defaults to 1.618.
red
The color to use when drawing the graph for the red channel. Defaults to #D93333.
green
The color to use when drawing the graph for the green channel. Defaults to #58C458.
blue
The color to use when drawing the graph for the blue channel. Defaults to #3767BF.

Examples:

  • t[]=histogram
  • t[]=histogram:scale=2
  • t[]=histogram:red=f00,green=0f0,blue=00f

Apply an ICC color profile - t[]=icc

Adds an ICC color profile to the image, enforcing the given profile for the next steps in the processing chain until output or another icc transformation is applied.

This transformation requires explicit configuration and is not enabled by default, since you have to register the color profiles you want to make available and their aliases.

To enable the transformation add it in your local configuration file:

<?php
return [
    // ...

    'transformations' => [
        'icc' => function () {
            return new Imbo\Image\Transformation\Icc([
                'default' => '/path/to/sRGB_v4_ICC_preference.icc',
                'srgb' => '/path/to/sRGB_v4_ICC_preference.icc',
            ]);
        },
    ],

    // ...
];

The profile given under the key default will be applied if no specific profile is requested (i.e. if icc is added without a name argument.)

The most common ICC profiles can be downloaded directly from the ICC’s page for sRGB profiles, or from Adobe and their ICC profiles download page.

Parameters:

profile
The name of the profile to apply as defined in the configuration file when adding the transformation.

Examples:

  • t[]=icc
  • t[]=icc:profile=srgb

Adjust levels of the image - t[]=level

This transformation will adjust the levels of an image. You are able to specify individual channels to adjust - by default it will apply to all channels.

Parameters:

channel
The channel to adjust. r (red), g (green), b (blue), c (cyan), m (magenta), y (yellow), k (black) and all (all channels) are available values. These channels can also be combined, if multiple channels should be adjusted. Defaults to all.
amount
The amount to adjust by. Range is from -100 to 100. Defaults to 1.

Examples:

  • t[]=level
  • t[]=level:channel=r,amount=30
  • t[]=level:channel=rg,amount=-45

Enforce a max size of an image - t[]=maxSize

This transformation will resize the image using the original aspect ratio. Two parameters are supported and at least one of them must be supplied to apply the transformation.

Note the difference from the resize transformation: given both width and height, the resulting image will not be the same width and height as specified unless the aspect ratio is the same.

Parameters:

width
The max width of the resulting image in pixels. If not specified the width will be calculated using the same aspect ratio as the original image.
height
The max height of the resulting image in pixels. If not specified the height will be calculated using the same aspect ratio as the original image.

Examples:

  • t[]=maxSize:width=100
  • t[]=maxSize:height=100
  • t[]=maxSize:width=100,height=50

Modulate the image - t[]=modulate

This transformation can be used to control the brightness, saturation and hue of the image.

Parameters:

b
Brightness of the image in percent. Defaults to 100.
s
Saturation of the image in percent. Defaults to 100.
h
Hue percentage. Defaults to 100.

Examples:

  • t[]=modulate:b=150
  • t[]=modulate:b=120,s=130,h=90

Make a progressive image - t[]=progressive

This transformation makes the image progressive.

Examples:

  • t[]=progressive

Resize the image - t[]=resize

This transformation will resize the image. Two parameters are supported and at least one of them must be supplied to apply the transformation.

Parameters:

width
The width of the resulting image in pixels. If not specified the width will be calculated using the same aspect ratio as the original image.
height
The height of the resulting image in pixels. If not specified the height will be calculated using the same aspect ratio as the original image.

Examples:

  • t[]=resize:width=100
  • t[]=resize:height=100
  • t[]=resize:width=100,height=50

Rotate the image - t[]=rotate

This transformation will rotate the image clock-wise.

Parameters:

angle
The number of degrees to rotate the image (clock-wise).
bg
Background color in hexadecimal. Defaults to 000000 (also supports short values like f00 (ff0000)).

Examples:

  • t[]=rotate:angle=90
  • t[]=rotate:angle=45,bg=fff

Apply a sepia color tone - t[]=sepia

This transformation will apply a sepia color tone transformation to the image.

Parameters:

threshold
Threshold ranges from 0 to QuantumRange and is a measure of the extent of the sepia toning. Defaults to 80

Examples:

  • t[]=sepia
  • t[]=sepia:threshold=70

Sharpen the image - t[]=sharpen

This transformation can be used to change the sharpness in the image.

Parameters:

radius
The radius of the Gaussian operator in pixels. Defaults to 2.
sigma
The standard deviation of the Gaussian, in pixels. Defaults to 1.
gain
The percentage of the difference between the original and the blur image that is added back into the original. Defaults to 1.
threshold
The threshold in pixels needed to apply the difference gain. Defaults to 0.05.
preset

Different presets that can be used. The presets are:

  • light (radius = 2, sigma = 1, gain = 1, threshold = 0.05)
  • moderate (radius = 2, sigma = 1, gain = 2, threshold = 0.05)
  • strong (radius = 2, sigma = 1, gain = 3, threshold = 0.025)
  • extreme (radius = 2, sigma = 1, gain = 4, threshold = 0)

When using any of the presets the different parameters can be overridden by specifying radius, sigma, gain and/or threshold. Not specifying any parameters at all is the same as using the light preset.

Examples:

  • t[]=sharpen
  • t[]=sharpen:preset=light (same as above)
  • t[]=sharpen:preset=extreme,gain=10 (use the extreme preset, but use a gain value of 10 instead of 4)
  • t[]=sharpen:radius=2,sigma=1,gain=1,threshold= 0.05 (same as using t[]=sharpen:preset=light, or simply t[]=sharpen)

Smart size the image - t[]=smartSize

This transformation is used to crop the image based on a point of interest (POI) provided either as a transformation parameter or from the image metadata.

Metadata format

The smart size transformation supports reading the POI from the metadata of the image. The POI information is expected to be stored on the poi property in metadata. Below is an example of a valid metadata object containing a 600,240 POI:

{
  "poi": [
    {
        x: 600,
        y: 240
    }
  ]
}

Note

The smart size transformation currently takes only the first object into account when cropping the image, but the POIs is stored as an array of objects in order to be easy to expand with more information for a more sophisticated smart size algorithm in the future.

Parameters:

width
The width of the crop in pixels.
height
The height of the crop in pixels.
poi
The POI coordinate x,y to crop around. The parameter is optional if the POI exists in metadata.
crop

The closeness of the crop (optional). Possible values are:

close medium wide full

Examples:

  • t[]=smartSize:width=250,height=250,poi=300,200
  • t[]=smartSize:width=250,height=250,poi=300,200,crop=close

Strip image properties and comments - t[]=strip

This transformation removes all properties and comments from the image. If you want to strip EXIF tags from the image for instance, this transformation will do that for you.

Examples:

  • t[]=strip

Create a thumbnail of the image - t[]=thumbnail

This transformation creates a thumbnail of <image>.

Parameters:

width
Width of the thumbnail in pixels. Defaults to 50.
height
Height of the thumbnail in pixels. Defaults to 50.
fit
Fit style. Possible values are: inset or outbound. Default to outbound.

Examples:

  • t[]=thumbnail
  • t[]=thumbnail:width=20,height=20,fit=inset

Create a vertical mirror image - t[]=transpose

This transformation transposes the image.

Examples:

  • t[]=transpose

Create a horizontal mirror image - t[]=transverse

This transformation transverses the image.

Examples:

  • t[]=transverse

Add a vignette to the image - t[]=vignette

This transformation can be used to add a vignette to the image.

Parameters:

inner
Color at the center of the image, in hexadecimal. Defaults to none, which means transparent. (You can also specify short values like f00 (ff0000)).
outer
Color at the edge of the image, in hexadecimal. Defaults to 000.
scale
Scale factor of the vignette. 2 will create a vignette twice the size of the original image. Defaults to 1.5.

Examples:

  • t[]=vignette
  • t[]=vignette:outer=ccc
  • t[]=vignette:scale=1,outer=333

Add a watermark to the image - t[]=watermark

This transformation can be used to apply a watermark on top of the original image.

Parameters:

img
Image identifier of the image to apply as watermark. Can be set to a default value in configuration by using <setDefaultImage>.
width
Width of the watermark image in pixels. If omitted the width of <img> will be used.
height
Height of the watermark image in pixels. If omitted the height of <img> will be used.
position
The placement of the watermark image. top-left, top-right, bottom-left, bottom-right, bottom, top, left, right and center are available values. Defaults to top-left.
x
Number of pixels in the X-axis the watermark image should be offset from the original position (defined by the position parameter). Supports negative numbers. Defaults to 0
y
Number of pixels in the Y-axis the watermark image should be offset from the original position (defined by the position parameter). Supports negative numbers. Defaults to 0
opacity
Can be an integer between 0 and 100 where 0 is fully transparent, and 100 is fully opaque. Defaults to 100

Examples:

  • t[]=watermark:img=f5f7851c40e2b76a01af9482f67bbf3f
  • t[]=watermark:img=f5f7851c40e2b76a01af9482f67bbf3f,width=200,x=5
  • t[]=watermark:img=f5f7851c40e2b76a01af9482f67bbf3f,height=50,x=-5,y=-5,position=bottom-right,opacity=50

If you want to set the default watermark image you will have to do so in the configuration:

<?php
return [
    // ...

    'eventListeners' => [
        'watermark' => function() {
            $transformation = new Imbo\Image\Transformation\Watermark();
            $transformation->setDefaultImage('some image identifier');

            return $transformation;
        },
    ],

    // ...
];

When you have specified a default watermark image you are not required to use the img option for the transformation, but if you do so it will override the default one.

Extending/customizing Imbo

Working with events and event listeners

Imbo uses an event dispatcher to trigger certain events from inside the application that you can subscribe to by using event listeners. In this chapter you can find information regarding the events that are triggered, and how to be able to write your own event listeners for Imbo.

Events

When implementing an event listener you need to know about the events that Imbo triggers. The most important events are combinations of the accessed resource along with the HTTP method used. Imbo currently provides these resources:

Examples of events that are triggered:

  • image.get
  • images.post
  • image.delete
  • metadata.get
  • status.head
  • stats.get

As you can see from the above examples the events are built up by the resource name and the HTTP method, lowercased and separated by ..

Some other notable events:

  • storage.image.insert
  • storage.image.load
  • storage.image.delete
  • db.image.insert
  • db.image.load
  • db.image.delete
  • db.metadata.update
  • db.metadata.load
  • db.metadata.delete
  • response.send

Image transformations also use the event dispatcher when triggering events. The events triggered for this is prefixed with image.transformation. and ends with the transformation as specified in the URL, lowercased. If you specify t[]=thumbnail&t[]=flipHorizontally as a query parameter when requesting an image the following events will be triggered:

  • image.transformation.thumbnail
  • image.transformation.fliphorizontally

All image transformation events adds the image and parameters for the transformation as arguments to the event, which can be fetched by the transformation via the $event object passed to the methods which subscribe to the transformation events.

Writing an event listener

When writing an event listener for Imbo you can choose one of the following approaches:

  1. Implement the Imbo\EventListener\ListenerInterface interface that comes with Imbo
  2. Implement a callable piece of code, for instance a class with an __invoke method
  3. Use a Closure

Below you will find examples on the approaches mentioned above.

Note

Information regarding how to attach the event listeners to Imbo is available in the event listener configuration section.

Implement the Imbo\EventListener\ListenerInterface interface

Below is the complete interface with comments:

 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
<?php
namespace Imbo\EventListener;

/**
 * Event listener interface
 */
interface ListenerInterface {
    /**
     * Return an array with events to subscribe to
     *
     * Single callbacks can use the simplest method, defaulting to a priority of 0
     *
     * return [
     *     'event' => 'someMethod',
     *     'event2' => 'someOtherMethod',
     * ];
     *
     * If you want to specify multiple callbacks and/or a priority for the callback(s):
     *
     * return [
     *     'event' => [
     *         'someMethod', // Defaults to priority 0, same as 'someMethod' => 0
     *         'someOtherMethod' => 10, // Will trigger before "someMethod"
     *         'someThirdMethod' => -10, // Will trigger after "someMethod"
     *     ],
     *     'event2' => 'someOtherMethod',
     * ];
     *
     * @return array
     */
    static function getSubscribedEvents();
}

The only method you need to implement is called getSubscribedEvents and that method should return an array where the keys are event names, and the values are callbacks. You can have several callbacks to the same event, and they can all have specific priorities.

Below is an example of how the Authenticate event listener implements the getSubscribedEvents method:

<?php

// ...

public static function getSubscribedEvents() {
    $callbacks = [];
    $events = [
        'images.post',
        'image.delete',
        'metadata.put',
        'metadata.post',
        'metadata.delete'
    ];

    foreach ($events as $event) {
        $callbacks[$event] = ['authenticate' => 100];
    }

    return $callbacks;
}

public function authenticate(Imbo\EventManager\EventInterface $event) {
    // Code that handles all events this listener subscribes to
}

// ...

In the snippet above the same method (authenticate) is attached to several events. The priority used is 100, which means it’s triggered early in the application flow.

The authenticate method, when executed, receives an instance of the event object that it can work with. The fact that the above code only uses a single callback for all events is an implementation detail. You can use different callbacks for all events if you want to.

Use a class with an __invoke method

You can also keep the listener definition code out of the event listener entirely, and specify that piece of information in the Imbo configuration instead. An invokable class could for instance look like this:

<?php
class SomeEventListener {
    public function __invoke(Imbo\EventManager\EventInterface $event) {
        // some custom code
    }
}

where the $event object is the same as the one passed to the authenticate method in the previous example.

Use a Closure

For testing and/or debugging purposes you can also write the event listener directly in the configuration, by using a Closure:

<?php
return [
    // ...

    'eventListeners' => [
        'customListener' => [
            'callback' => function(Imbo\EventManager\EventInterface $event) {
                // Custom code
            },
            'events' => ['image.get'],
        ],
    ],

    // ...
];

The $event object passed to the function is the same as in the previous two examples. This approach should mostly be used for testing purposes and quick hacks. More information regarding this approach is available in the event listener configuration section.

Subscribing to events using a wild card

When subscribing to one or more events Imbo let’s you use wild cards. This means that you can have your event listener subscribe to all the different image events by specifying 'image.*' as the event you are subscribing to. If you want a listener to subscribe to all events you can simply use '*'. Use $event->getName() in your handler to figure out which event is actually being triggered.

One other thing to keep in mind while using wild card events is priorities. Global wild card event listeners (that listens to '*') is triggered before all other listeners, and other wild card listeners are triggered before the ones who has absolute event names they are subscribing to.

Example:

<?php
return [
    // ...

    'eventListeners' => [
        'listener1' => [
            'callback' => function ($e) { /* ... */ },
            'events' => ['*'],
            'priority' => 100,
        ],
        'listener2' => [
            'callback' => function ($e) { /* ... */ },
            'events' => ['*'],
            'priority' => 200,
        ],
        'listener3' => [
            'callback' => function ($e) { /* ... */ },
            'events' => ['image.*'],
            'priority' => 300,
        ],
        'listener4' => [
            'callback' => function ($e) { /* ... */ },
            'events' => ['image.*'],
            'priority' => 400,
        ],
        'listener5' => [
            'callback' => function ($e) { /* ... */ },
            'events' => ['image.get'],
            'priority' => PHP_INT_MAX,
        ],
    ],

    // ...
];

Given the configuration above the execution order would be:

  • listener2
  • listener1
  • listener4
  • listener3
  • listener5

Even though listener5 has the highest possible priority the wild card listeners are executed first, because they are in their own priority queues.

The event object

The object passed to the event listeners is an instance of the Imbo\EventManager\EventInterface interface. This interface has some methods that event listeners can use:

getName()
Get the name of the current event. For instance image.delete.
getHandler()
Get the name of the current event handler, as specified in the configuration. Can come in handy when you have to dynamically register more callbacks based on constructor parameters for the event listener. Have a look at the implementation of the CORS event listener for an example on how to achieve this.
getRequest()
Get the current request object (an instance of Imbo\Http\Request\Request)
getResponse()
Get the current response object (an instance of Imbo\Http\Response\Response)
getDatabase()
Get the current database adapter (an instance of Imbo\Database\DatabaseInterface)
getStorage()
Get the current storage adapter (an instance of Imbo\Storage\StorageInterface)
getManager()
Get the current event manager (an instance of Imbo\EventManager\EventManager)
getConfig()
Get the complete Imbo configuration. This should be used with caution as it includes all authentication information regarding the Imbo users.
stopPropagation()
If you want your event listener to force Imbo to skip all following listeners for the same event, call this method in your listener.
isPropagationStopped()
This method is used by Imbo to check if a listener wants the propagation to stop. Your listener will most likely never need to use this method.
getArgument()
This method can be used to fetch arguments given to the event. This method is used by all image transformation event listeners as the image itself and the parameters for the transformation is stored as arguments to the event.

With these methods you have access to most parts of Imbo. Be careful when using the database and storage adapters as these grant you access to all data stored in Imbo, with both read and write permissions.

Implement your own database and/or storage adapter

If the adapters shipped with Imbo does not fit your needs you can implement your own set of database and/or storage adapters and have Imbo use them pretty easily. A set of interfaces exists for you to implement, and then all that’s left to do is to enable the adapters in your configuration file. See the Database confguration and Storage configuration sections for more information on how to enable different adapters in the configuration.

Custom database adapters must implement the Imbo\Database\DatabaseInterface interface, and custom storage adapters must implement the Imbo\Storage\StorageInterface interface.

If you implement an adapter that you think should be a part of Imbo feel free to send a pull request on GitHub.

Implement your own image transformations

Imbo also supports custom image transformations. All you need to do is to create an event listener, and configure your transformation:

<?php
class My\Custom\Transformation implements Imbo\EventListener\ListenerInterface {
    public static function getSubscribedEvents() {
        return ['image.transformation.cooltransformation' => 'transform'];
    }

    public function transform($event) {
        $image = $event->getArgument('image');
        $params = $event->getArgument('params'); // If the transformation allows params in the URL

        // ...
    }
}

return [
    // ..

    'eventListeners' => [
        'coolTransformation' => 'My\Custom\Transformation',
    ],

    // ...
];

Whenever someone requests an image using ?t[]=coolTransformation:width=100,height=200 Imbo will trigger the image.transformation.cooltransformation event, and assign the following value to the params argument of the event:

[
    'width' => '100',
    'height' => '200',
]

Take a look at the existing transformations included with Imbo for more information.

Contributing to Imbo

Imbo is an open source project licensed with the MIT license. All contributions should ideally be sent in form of a pull request on GitHub. Please use features branches with descriptive names, and remember to send the pull request against the develop branch.

If you have found a bug in Imbo, please leave an issue in the issue tracker.

Requirements

When contributing to Imbo (or any of the other related packages) there are some guidelines you should follow.

Coding standard

Imbo has a coding standard that is partially defined as a PHP Code Sniffer standard. The standard is available on GitHub and is installable via PEAR. There are some details that might not be covered by the standard, so if you send a PR you might notice some nitpicking from my part regarding stuff not covered by the standard. Browse existing code to understand the general look and feel.

Tests

When introducing new features you are required to add tests. Unit/integration tests (PHPUnit) and/or Behat scenarios is sufficient. To run the PHPUnit test suite you can execute the following command in the project root directory after installing Imbo:

composer test-phpunit

For the Behat test suite you can the following command:

composer test-behat

If you want to run both suites you can simply run:

composer test

If you find a bug that you want to fix please add a test first that confirms the bug, and then fix the bug, making the newly added test pass.

Documentation

API documentation is written using phpDocumentor, and can be generated via a composer script:

composer qa-phpdoc

End user documentation (the ones you are reading now) is written using Sphinx and is located in the docs/ directory in the project root. To generate the HTML version of the docs you can execute the following command:

composer docs

This task also includes a spell checking stage.

Pull requests on GitHub

If you want to send a pull request, please do so from a publicly available fork of Imbo, using a feature branch with a self descriptive name. The pull request should be sent to the develop branch. If your pull request is fixing an open issue from the issue tracker your branch can be named after the issue number, for instance issue-312.