Each 'slot' on a page can have its content changed by query parameters, allowing for any possible number of permutations of content on the same page.
This is especially useful for marketing and creating landing pages for A-B testing or for prototyping a design of a webpage without cluttering your templates with a bunch of if-statements.
For example, visiting exmaple.com/page?h=1&b=1
and exmaple.com/page?h=2&b=2
will give
you a page with a different headline and body if we were to assign the following:
headline:
1: Book your test drive
2: Test drive our new vehicle
body:
1: Fill in this quick form and one of our representatives will call you back
2: Request a callback for a 48 hour test drive today
Works with PHP 5.3 through PHP 8 and HHVM.
Version 1.0 is fully released, build on top of Pimple 1.0 and Symfony HttpFoundation 2.8.
Version 2.0 provides compatibility with PHP 7+, Symfony HttpFoundation 3+ and Pimple 2+, but still works with older versions of PHP and these packages.
Since November 2012, SlotMachine has been used on several live web pages with tens of thousands of hits a day, mostly landing pages from Facebook ads.
For those working in digital marketing, a effective landing page is always required for an advertisement to lead to. This is more effective if the page matches the content and design of the ad. However if one has many ads, many static pages would therefore be required. Sometimes only one feature on the page such as the headline or the border color of a container would want to be different to another page.
SlotMachine is a PHP library that allows for one HTML page to display different variants of content on the fly. These can be defined beforehand or with a query string. (ie. example.com/landingpage?h=2&c=3)
Think of a bus that changes its blinds (assuming they still use blinds instead of a matrix) to display different combinations of what route number its running, what places it passes through and what its destination is. In SlotMachine, that would be three slots.
- Install with Composer
- Set up the configuration and data
- Create your page
<?php
// your-landing-page.php
require __DIR__.'/vendor/autoload.php';
$data = include __DIR__.'/slotmachine.config.php';
$slots = new SlotMachine\SlotMachine($data);
$headline = $slots->get('headline');
$body = $slots->get('body');
$description = $slots->get('description');
$image = $slots->get('image');
?>
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title><?=$headline?> :: My Site</title>
<meta name="description" content="<?=$description?>">
</head>
<body>
<h1><?=$headline?></h1>
<p><?=$body?></p>
<img src="<?=$image?>" alt="Featured Image" />
<small>© <?php echo date('Y'); ?> My Site</small>
</body>
</html>
First, Download Composer from the command line
$ cd path/to/your/project
$ curl -s https://getcomposer.org/installer | php
Then create a composer.json
file in your project with the most recent and widely compatible version 2
{
"require": {
"slotmachine/slotmachine": "~2.0"
}
}
Or to use the older, more stable version (currently v1.0)
{
"require": {
"slotmachine/slotmachine": "~1.0"
}
}
Then install with the following command
$ php composer.phar install
All new instances of SlotMachine\SlotMachine
will take an array for its configuration.
Below is an example that would be used with the page example above.
<?php
// slotmachine.config.php
return array(
'reels' => array(
'headline' => array(
'cards' => array(
'Join our free service today.',
'Welcome, valued customer.',
'Welcome back, {user}!',
'Check out our special offers'
),
),
'body' => array(
'cards' => array(
0 => 'Time is of the essence, apply now!',
1 => 'Get a discount today',
2 => 'Merry Christmas'
),
'aliases' => array(
'xmas' => 2
)
),
'description' => array(
'cards' => array(
0 => 'Acme Corp. Specialists for anvils.',
1 => 'Special offer only online at Acme.',
9001 => 'Acme has anvils for all occasions.',
),
'aliases' => array(
'_default' => 9001
),
'undefined_card' => 'DEFAULT_CARD'
),
),
'slots' => array(
'headline' => array(
'reel' => 'headline',
'keys' => array('h'),
'nested' => array(
'user'
)
),
'body' => array(
'reel' => 'body',
'keys' => array('c'),
),
'description' => array(
'reel' => 'description',
'keys' => array('c'),
),
'user' => array(
'reel' => array(
'cards' => array(
0 => 'valued customer',
1 => 'Peter',
2 => 'Lois',
3 => 'Brian',
4 => 'Chris',
5 => 'Meg',
6 => 'Stewie'
),
),
'keys' => array('uid'),
),
),
);
The values returned from a slot are called cards.
To get a card from a single slot, pass the name of the slot to get()
method
$slots->get('headline');
To get a card from every slot in the SlotMachine, use the all()
method.
$slot = $slots->all();
echo $slot['headline'];
To override the default card returned from a slot, pass the card's array key
to the second argument of the get()
method.
$slots->get('headline', 4);
If you would like example.com/?app_data[i]=1
and example.com/?i=1
to render the same result,
just assign an array of HTTP query parameters to the keys
attribute.
This is useful for passing parameters to the Facebook Page Tab, but not having to
use app_data
each time.
$config = array(
//...
'slots' => array(
//...
'hero_image' => array(
'keys' => array('i', 'app_data[i]'),
'reel' => 'hero_image'
)
)
)
SlotMachine uses the Symfony2 HttpFoundation component to resolve the page based on query string parameters.
The SlotMachine class also extends Pimple, a lightweight dependency injection container, which the Slots are injected into.
Although not required, it is recommended that you install the Symfony Yaml component.
Just add "symfony/yaml": "~2.0"
to your composer.json
file.
This makes reading the configuration easier and give access to more features that YAML files provide.
The following YAML would be used instead of the PHP array above
#slots.config.yml
slots:
headline:
keys: [ h ]
reel: headline
nested: [ 'user' ]
body:
keys: [ c ]
reel: body
description:
keys: [ c ]
reel: description
user:
keys: [ 'uid' ]
reel:
cards:
0: 'valued customer'
1: 'Peter'
2: 'Lois'
3: 'Brian'
4: 'Chris'
5: 'Meg'
6: 'Stewie'
reels:
headline:
cards:
- 'Join our free service today.'
- 'Welcome, valued customer.'
- 'Welcome back, {user}!'
- 'Check out our special offers'
body:
aliases: { xmas: 2 }
cards:
0: 'Time is of the essence, apply now!'
1: 'Get a discount today'
2: 'Merry Christmas'
description:
undefined_card: DEFAULT_CARD
aliases: { _default: 9001 }
cards:
0: 'Acme Corp. Specialists for anvils.'
1: 'Special offer only online at Acme.'
9001: 'Acme has anvils for all occasions.'
Silex is a micro-framework that uses Symfony2 components. Like SlotMachine
, it also extends Pimple, and uses Symfony HttpFoundation.
Since SlotMachine was initally designed to work with Silex, a service provider is included as part of the the library.
Note that the the SlotMachineServiceProvider
is removed from the 2.0
version
of SlotMachine since SlotMachine uses Pimple 2.0 and Silex is still using Pimple 1.0.
Below is an example of using SlotMachine in Silex, including Twig for templating.
<?php
require __DIR__.'/vendor/autoload.php';
use SlotMachine\SlotMachineServiceProvider;
use Silex\Application;
use Silex\Provider\TwigServiceProvider;
use Symfony\Component\Yaml\Yaml;
use Symfony\Component\HttpFoundation\Request;
$app = new Application();
$app['debug'] = true;
$app->register(new SlotMachineServiceProvider(), array(
'slotmachine.config' => Yaml::parse(file_get_contents(__DIR__.'/config/slots.config.yml')),
'slotmachine.request' => Request::createFromGlobals()
));
$app->register(new TwigServiceProvider(), array(
'twig.path' => __DIR__.'/templates';
));
$pageData['slot'] = $app['slotmachine']->all();
$pageData['site_name'] = 'My Site';
$app['page_data'] = $pageData;
// If a Silex app is iframed in Facebook, the request type has to match POST first before matching GET
$app->match('/', function() use ($app) {
return $app['twig']->render('landing_page.html.twig', $app['page_data']);
})->method('POST|GET');
$app->run();
The above would render the following Twig template.
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="description" content="{{ slot.description }}">
<title>{{ slot.headline }} :: {{ site_name }}</title>
</head>
<body>
<h1>{{ slot.headline|upper }}</h1>
<p>{{ slot.body }}</p>
<small>© {{ "now"|date('Y') }} {{ site_name }}</small>
</body>
</html>
Unfortunately, this feature will not work for installations of Symfony 3 or installations of HttpFoundation 3.0 and above! See symfony/symfony#14039
Whilst a normal query string combination is used to resolve cards and render the page uniquely...
http://example.com/campaign/landingpage?a=1&b=2&c=3
... and given the following configuration that will resolve this query...
slots:
foo:
keys: [ a ]
reel: foo
bar:
keys: [ b ]
reel: bar
baz:
keys: [ c ]
reel: baz
However, you can assign all of them to one parameter, so the query string would use array keys.
This is required for Facebook apps and landing pages being accessed through the Facebook Page Tab
as Facebook will only whitelist the app_data
query parameter and ignore others.
http://mycampaign.facebook.com/landingpage?app_data[a]=1&app_data[b]=2&app_data[c]=3
Here is the following configuration which will resolve this query:
slots:
foo:
keys: [ "q[a]" ]
reel: foo # "[]" must be in quotes so not to be confused for a YAML array
bar:
keys: [ "q[b]" ]
reel: bar
baz:
keys: [ "q[c]" ]
reel: baz
These commands assumes that PHPUnit and Composer are installed globally on your system.
$ cd path/to/SlotMachine/
$ composer install
$ phpunit
Please feel free to create a new issue on the GitHub repository https://github.com/archfizz/slotmachine