Dummy.

Fill PHP templates with realistic placeholder content, vary the length of an article, or show an image only some of the time. Reload the page to see how your layout handles a different combination.

Install with Composer, use it in Laravel and Blade, or copy the folder into a plain PHP project. The instructions and working examples are all here.

This is a generated snapshot of the PHP demo. Run Dummy locally to get fresh random content on every request.

Getting Started

Requirements

Use PHP 8.3 or newer with the GD extension enabled. Composer 2 is needed for package installation. The Laravel integration supports Laravel 12 and 13. Dummy needs no database, Docker setup, or native PHP session.

To run the demo locally, use the following command from the Dummy directory, then open localhost:8000/demo.php.

Terminal
php -S localhost:8000

Install with Composer

From your application directory, install Dummy as a development dependency. Composer loads dummy(), dumb_luck(), and dumb_question() automatically.

Terminal
composer require --dev kerns/dummy

Next, follow the Laravel setup or the standalone Composer setup below.

Laravel & Blade

After installing with Composer, run this command from your Laravel application's root directory:

Terminal
php artisan vendor:publish --tag=dummy-assets

Laravel discovers Dummy's service provider automatically. The command publishes images, text pools, and ad templates to public/vendor/dummy/assets. Generated crops go into public/vendor/dummy/cache, which PHP creates automatically. The web server needs permission to create and write to that cache directory.

Create resources/views/dummy-demo.blade.php and use the helpers directly in the view:

Blade
<h1>@php(dummy('text@words:5-9,title'))</h1>

@if (dumb_luck('50%'))
    <img src="@php(dummy('image@640x,16:9'))" alt="Example image">
@endif

<ul>
@while (dumb_luck('3-8'))
    <li>@php(dummy('text@headline'))</li>
@endwhile
</ul>

dummy() prints its result. Use @php(...) for it rather than Blade's escaped {{ ... }} output. The 50% condition shows the image about half the time, and the loop produces between three and eight items.

Add a route to routes/web.php:

PHP
use Illuminate\Support\Facades\Route;

Route::view('/dummy-demo', 'dummy-demo');

Start Laravel with your application's normal development command, or run php artisan serve. Open localhost:8000/dummy-demo when using the default local port. Laravel serves the published assets and cached crops from its public directory.

Composer in a standalone PHP project

After the Composer installation above, put Dummy's assets under your public web root. For a project with a public directory, run:

Terminal
mkdir -p public/dummy
cp -R vendor/kerns/dummy/dummy/assets public/dummy/

Create public/demo.php with the following contents. The three paths connect Dummy to the assets and cache in your public directory:

PHP + HTML
<?php
require_once __DIR__ . '/../vendor/autoload.php';

$dummy_path = '/dummy';
$dummy_assets_path = __DIR__ . '/dummy/assets';
$dummy_cache_path = __DIR__ . '/dummy/cache';
?>
<h1><?php dummy('text@headline'); ?></h1>
<p><?php dummy('text@words:30-60'); ?></p>
<img src="<?php dummy('image@640x,16:9'); ?>" alt="Example image">

Make sure PHP can create public/dummy/cache, or create it yourself and give the web server write access. Start the server from your application directory and open localhost:8000/demo.php:

Terminal
php -S localhost:8000 -t public

If your project serves files from another directory, adjust the include path and asset locations to match that web root. Generated lorem ipsum works without copying assets; named text pools, images, and ads need the published files.

Plain PHP without Composer

  1. Copy the entire dummy directory into your public web root. Keep dummy.php, Runtime.php, Lorem.php, and the assets directory together.
  2. Create a PHP template in that web root and include Dummy before calling its helpers:
PHP + HTML
<?php require_once __DIR__ . '/dummy/dummy.php'; ?>

<h1><?php dummy('text@headline'); ?></h1>
<p><?php dummy('text@words:30-60'); ?></p>

<?php if (dumb_luck('50%')): ?>
    <img src="<?php dummy('image@640x,16:9'); ?>" alt="Example image">
<?php endif; ?>

Make dummy/cache writable by PHP. Dummy creates the directory if its parent is writable. Use php -S localhost:8000 from that web root, or your usual PHP development server, and open the template in your browser.

The image below demonstrates image cropping and caching. It uses a larger source image so it stays sharp on high-density displays.

A randomly selected demo photograph

Success. Images are rendering and the cache directory is writable.

Paths, cache & image settings

In Laravel, publish the configuration if you need different asset paths or a URL prefix:

Terminal
php artisan vendor:publish --tag=dummy-config

Edit config/dummy.php. These are the defaults:

PHP
<?php
return [
    'url' => '/vendor/dummy',
    'assets_path' => public_path('vendor/dummy/assets'),
    'cache_path' => public_path('vendor/dummy/cache'),
];

Set custom paths before publishing assets. The URL must serve the matching asset and cache directories. If configuration is cached, run php artisan config:cache after changing it.

Outside Laravel, set $dummy_path, $dummy_assets_path, and $dummy_cache_path before rendering, as in the Composer example above. The include path is a filesystem path; $dummy_path is the public URL prefix. Inside a function, set these through $GLOBALS.

Optional image constants such as JPEG_QUALITY (default 90), MAX_WIDTH_PX (default 2048), and MAX_HEIGHT_PX (default 1536) must be set before loading Dummy. In Laravel or a Composer project, that means before loading vendor/autoload.php. Use a fresh cache directory when changing JPEG quality so existing crops are not reused.

Troubleshooting

  • If PHP cannot find Dummy, check the filesystem path in require_once. Use __DIR__ so it does not depend on the current working directory.
  • If images fail to load, confirm that the configured public URL points to the assets and cache directories, and that PHP can write to the cache.
  • If GD is missing, enable the GD extension for the PHP runtime serving the page. Your terminal and web server can use different PHP configurations.
  • Use standard <?php ... ?> tags. Old templates using <? ... ?> need PHP's short_open_tag setting; new examples do not.

Text pools and custom ad templates can contain executable PHP. Use trusted local files. Keep Dummy as a development dependency for prototyping and QA.

Dummy Assets

The ability to select, manipulate, and insert different assets into your front-end project is a key feature of Dummy. All of the assets available to Dummy live in a folder located at dummy/assets/. It's a good idea to poke around in here and see how things are structured. The simple syntax used to request different assets relates very much to the structure of this directory.

Dummy Text

Dummy makes it easy to insert random strings of Lorem Ipsum that correspond to commonly needed lengths and formats. For example, the following snippet of code will produce a random string of Lorem Ipsum approximately the length of a headline.

PHP + HTML
<?php dummy("text@headline") ?>

The exact result will be different every time you reload your document, but it should look something like this...

Nullam Quis Risus Eget Urna Mollis Ornare Vel Eu Leo

The following table is a catalog of Dummy's default assets, the snippet of code used to insert the asset, and a sample of actual output.

Objective Dummy Code Output
Headline <?php dummy("text@headline") ?> Curabitur Gravida, Lorem id Venenatis Sodales
Teaser <?php dummy("text@teaser") ?> Lorem ipsum dolor sit amet consectetuer adipiscing elit.
Short Teaser <?php dummy("text@short-teaser") ?> Pretium taciti sodales porta ut condimentum.
Long Teaser <?php dummy("text@long-teaser") ?> Fusce et risus nonummy neque euismod pretium. Donec dignissim ligula vitae ipsum. Maecenas a velit eu lectus blandit blandit.
Item <?php dummy("text@item") ?> Consectetur
Date <?php dummy("text@date") ?> October 18th, 2026
Time <?php dummy("text@time") ?> 12:44 pm
Time ago (relative) <?php dummy("text@time-ago") ?> 12 days ago
Author <?php dummy("text@author") ?> Romain Garceau
Username <?php dummy("text@username") ?> amppilot
City <?php dummy("text@city") ?> Florence
Paragraph <?php dummy("text@paragraph") ?> Nam vulputate convallis massa viverra tristique. Aenean aliquet egestas pulvinar. Phasellus ante orci, sollicitudin quis congue non, scelerisque vitae leo. Phasellus a tortor quis dolor pharetra convallis. Pellentesque et neque et libero rhoncus tempus in id libero. Integer enim enim, mollis ac tincidunt ut, luctus id justo. Integer in magna urna, eu blandit libero. Sed quis massa erat. Aenean eros sapien, volutpat sed sodales ac, aliquam a risus. Quisque cursus, magna quis semper imperdiet, arcu orci tempus nulla, consectetur tristique eros felis at arcu.

Hint: Inside of dummy/assets/text/ you'll find a directory full of flat text files. These are the basis for organizing different text formats used by Dummy. You can edit these or add your own as you like.

Lorem ipsum at any length

Keep using named text pools for headlines and other curated content, or generate fresh lorem ipsum by specifying a count. Use a single number for an exact length, or a range for a different length on each call.

PHP + HTML
<?php dummy("text@words:50") ?>
<?php dummy("text@words:30-60") ?>
<?php dummy("text@words:5-9,title") ?>
<?php dummy("text@sentences:2-4") ?>
<?php dummy("text@paragraphs:2-3") ?>
LengthDummy CodeLive output
Exactly 50 words<?php dummy("text@words:50") ?>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Vestibulum id ligula porta felis euismod semper. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Fusce dapibus, tellus ac cursus commodo.
30–60 words<?php dummy("text@words:30-60") ?>Pellentesque ornare sem lacinia quam venenatis vestibulum. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Curabitur blandit tempus porttitor. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Pellentesque ornare sem lacinia quam venenatis vestibulum. Morbi.
5–9 words, title case<?php dummy("text@words:5-9,title") ?>Duis Aute Irure Dolor In Reprehenderit.
2–4 sentences<?php dummy("text@sentences:2-4") ?>Etiam porta sem malesuada magna mollis euismod. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Vestibulum id ligula porta felis euismod semper.
2–3 paragraphs<?php dummy("text@paragraphs:2-3") ?>Donec ullamcorper nulla non metus auctor fringilla. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Maecenas faucibus mollis interdum. Curabitur blandit tempus porttitor. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Donec ullamcorper nulla non metus auctor fringilla. Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Maecenas faucibus mollis interdum. Nullam quis risus eget urna mollis ornare vel eu leo. Etiam porta sem malesuada magna mollis euismod.

Output is plain text. Add ,title to capitalize every word; omit it for normal sentence casing. Paragraphs are separated by blank lines; use your own markup or CSS to display those breaks. Zero returns no text. Ranges include both endpoints. Limits per call: 10,000 words, 1,000 sentences, or 100 paragraphs. No extra packages or asset publishing needed.

Dummy Images

Dummy makes it easy to crop and size placeholder images on the fly. It ships with a starter pack of high-quality, newsworthy, Creative Commons licensed images, but like other assets it's easy to edit or expand these according to the needs of your project.

The syntax for requesting an image is flexible. In a very basic example, you request an image with specific dimensions...

PHP + HTML
<img src="<?php dummy("image@480x320") ?>" />

...and the following is returned:

A 480 by 320 pixel crop

This example returns exactly 480×320 image pixels. At 480 CSS pixels wide, it is a 1× image; high-density screens benefit from a larger crop, as shown below.

You can specify exact pixel dimensions or just an aspect ratio. You can also combine these parameters. For example, you can choose to specify just the width together with an aspect ratio. Dummy will return the path to an image that conforms to both requirements.

PHP + HTML
<img src="<?php dummy("image@640x,16:9") ?>" />

Returns...

A crop with a 16 to 9 aspect ratio

Use the returned image URL in your <img> element's src attribute.

Sharp images on high-density screens

Image dimensions are real pixels, not CSS display sizes. For a photo displayed at 640 pixels wide, request a larger crop and set its display width separately. The example below requests 1920×1080 pixels and displays them at 640×360, giving the browser three image pixels per CSS pixel.

PHP + HTML
<img
  src="<?php dummy('image/retina@1920x,16:9'); ?>"
  width="640"
  height="360"
  alt="A high-resolution demo photograph"
>
A high-resolution demo photograph

The bundled image/retina folder contains 2048×1536 source photos. Some older landscape photos are only 1024–1150 pixels wide. A larger request cannot restore detail missing from the original.

JPEG crops are encoded at quality 90 by default. That adds some compression, but reducing the pixel dimensions is the main reason the earlier demo looked soft on a high-density screen. Supply a sufficiently large source and request enough pixels for the intended display size. Aspect-ratio-only commands such as image@16:9 crop at the source's available resolution.

The following table provides some clues as to how you can structure your image requests.

Objective Dummy Code Output
100px square Classic. Square. <?php dummy("image@100x100") ?> dummy/cache/rania_world_economic_forum_540a9d7c8ee6bcf9_100x100.jpg
100px square (Part II) A rather pointless way of generating the same result as above. Proving a point here. <?php dummy("image@100x,1:1") ?> dummy/cache/bokeh_hearts_fung_leo_006356c7fec5e4ba_100x100.jpg
16:9 With a height of 200px. Pixel dimensions and aspect ratios must be separated with a comma. <?php dummy("image@16:9,x200") ?> dummy/cache/clinton_world_economic_forum_2231b202e683dc68_355x200.jpg
16:9 (Part II) Same as above. It doesn't matter which order you specify ratio or pixel dimensions. <?php dummy("image@x200,16:9") ?> dummy/cache/shibuya_center_joi_ito_7c1d9ae5d3838a37_355x200.jpg
3:2 The Photographer's aspect! We'll ask for assets from a specific subdirectory of ‘dummy/assets/image/’ here to improve the odds of getting a suitable image. <?php dummy("image/landscape@3:2") ?> dummy/cache/2008_olympics_familymwr_9abd9aa1ec15bc73_1024x682.jpg
!Avatar Subdirectories of ‘dummy/assets/image/’ that begin with a ‘!’ are exempted from normal random (and recursive) selection. You have to target them specifically. This is good. <?php dummy("image/!avatar@150x150") ?> dummy/cache/solar_flare_erupts_NASA_goddard_b55a508ae8dab465_150x150.gif

Note: When specifying dimensions, either as an exact pixel measurement or as an aspect ratio, describe the width first and height second (Width x Height).

The basic model for how Dummy selects image assets is semi-random and recursive. It begins looking for assets on the root level of "assets/image" and drills down, only ignoring sub-folders with names that begin with a ‘!’. Dummy also works in the background to track which image was most recently selected, in order to minimize the odds of inserting the same image in succession or in close proximity.

Dummy Ads

Inside of /dummy/assets/ad you'll find a host of folders representing many of the popular formats as defined by the IAB. The dummy code used to request and insert an ad follows the now familiar model.

PHP + HTML
<?php dummy("ad@120x90") ?>

Returns...

Note that a request for an ad returns a pre-formatted block of embed code containing an ad, as opposed to just the URL path to the ad (as is done with images). The reason for this is that we often need blocks of repetitious, ancillary markup to surround ad placements, as is the case with regard to advertising in the Adobe® Flash® format.

The default embed code for both standards compliant image based ads and ads in the Flash format can be found on the root level of /dummy/assets/ad in the form of two files — image.embed.php and flash.embed.php respectively. You can override the use of these defaults by placing an edited copy of one or both of these files in the folder of the format you wish to override.

The folders containing different formats are also used to specify their dimensions (WxH). Dummy parses these folder names and interprets them as variables, which in turn can be used to set the width and height values of ads as they are parsed into the embed code. If you don't feel it necessary to pass these values to your embed code, you're free to add new formats in folders and name them however you like — just remember to create a custom embed for these formats.

You may find it useful to enable or disable the insertion of Flash format ads under different testing scenarios. Set $flash_ads = true before rendering to include legacy Flash ads. The default is disabled, and current browsers do not support Flash.

Dumb Luck

The ability to insert randomly selected assets into a layout is great, but it's Dummy's simple logic for controlling probability and creating loop ranges that make it possible to flesh out highly variable, asset rich layouts with very little code. The basis for this logic is contained in a function called dumb_luck.

Controlling Probability

A basic building block of Dummy driven layouts, dumb_luck can be used to influence the probability that something will happen when the document is rendered. This helps answer the age old question, “What does this layout look like with or without X ?”, where X could be an image, a special announcement, or a block of JavaScript that triggers some other chain of events.

PHP + HTML
<?php if (dumb_luck("50%")): ?>
  There is a 50/50 chance of this happening.
<?php endif ?>

Jazz up your dumb_luck logic with fallback or alternative outcomes using standard PHP if/else statement structure.

PHP + HTML
<?php if (dumb_luck("95%")): ?>
  Pretty damn likely to happen.
<?php else: ?>
  This is a lot less likely.
<?php endif ?>

You can also nest dumb_luck logic as needed, to test and explore more complex outcomes.

PHP + HTML
<?php if (dumb_luck("50%")): ?>
  <?php if (dumb_luck("50%")): ?>
    Heads
  <?php else: ?>
    Tails
  <?php endif ?>
<?php else: ?>
  <?php if (dumb_luck("33%")): ?>
    Rock
  <?php elseif (dumb_luck("33%")): ?>
    Paper
  <?php else: ?>
    Scissors
  <?php endif ?>
<?php endif ?>

For the record... Rock!

Creating Loop Ranges

A thumbnail... A thumbnail... A thumbnail... A thumbnail... A thumbnail... A thumbnail... A thumbnail... A thumbnail... A thumbnail... A thumbnail... A thumbnail... A thumbnail... A thumbnail... A thumbnail... A thumbnail... A thumbnail... A thumbnail... A thumbnail... A thumbnail... A thumbnail... A thumbnail... A thumbnail... A thumbnail... A thumbnail... A thumbnail... A thumbnail... A thumbnail... A thumbnail... A thumbnail... A thumbnail... A thumbnail... A thumbnail... A thumbnail... A thumbnail... A thumbnail... A thumbnail... A thumbnail... A thumbnail... A thumbnail... A thumbnail... A thumbnail... A thumbnail... A thumbnail... A thumbnail... A thumbnail... A thumbnail... A thumbnail... A thumbnail... A thumbnail... A thumbnail...

Dumb Luck's loop range takes whatever content you place inside of it and loops it within a range of two numbers that you specify. Inside of the loop range above, a single request for a 35x35 pixel thumbnail.

Loop ranges make it possible to generate massive amounts of variable content very quickly. In the example above, there are two bits of Dummy code working together. A dumb_luck loop range of 50-100, and inside of that loop range, a single request for a 35x35 pixel thumbnail.

The thumbnail will be rendered no less than 50 times, and up to 100. The Dummy code used to achieve this is compact and human readable:

PHP + HTML
<?php while (dumb_luck("50-100")): ?>
  <?php dummy("image@35x35") ?>
<?php endwhile ?>

Loop ranges are built on the same dumb_luck function used to control probability, only instead of passing in a % value we pass it a range of two numbers separated by a hyphen. These two values define the minimum and maximum number of times the loop should continue. In the example below, we ask for between 5 and 10 instances of a list item containing a headline.

PHP + HTML
<ul>
<?php while (dumb_luck("5-10")): ?>
  <li><?php dummy("text@headline") ?></li>
<?php endwhile ?>
</ul>

Note that the PHP syntax used with dumb_luck requires “WHILE” in association with loop ranges.

The result is a simple UL containing between 5 and 10 list items:

  • Vel Scelerisque Nisl Consectetur Et
  • Nullam Id Dolor Id Nibh Ultricies Vehicula
  • Maecenas Faucibus Mollis Interdum
  • Fusce Dapibus, Tellus Ac Cursus Commodo
  • Donec Ullamcorper Nulla Non Metus Auctor Fringilla

Putting It All Together

Taking the same list used in the previous example, imagine we needed to see how it would look with some of the items linked some of the time. To achieve this we'll nest some probability logic inside of our loop range.

PHP + HTML
<ul>
<?php while (dumb_luck("5-10")): ?>
  <?php if (dumb_luck("50%")): ?>
    <li><a href="#"><?php dummy("text@headline") ?></a></li>
  <?php else: ?>
    <li><?php dummy("text@headline") ?></li>
  <?php endif ?>
<?php endwhile ?>
</ul>

Though the exact number will change every time you reload the page, roughly half of the items in the following list will be wrapped in a link. It's a very basic example, but one that uses all the core features of Dummy.

When used together, asset generation, probability, and loop range logic can create highly variable layouts with very little effort in code. This makes it possible to quickly test how the change of an item, attribute or parameter in one place propagates on a range of different scales. It can provide new insights on the performance or design efficacy of your work at the earliest stages of a project, and every time you reload the page.

PHP + HTML
<h1><?php dummy("text@headline") ?></h1>
<?php if (dumb_luck("25%")): ?>
  <p class="intro"><?php dummy("text@long-teaser") ?></p>
<?php endif ?>
<p>By <?php dummy("text@author") ?> | Published <?php dummy("text@date") ?></p>
<?php while (dumb_luck("3-8")): ?>
  <p><?php dummy("text@paragraph") ?></p>
<?php endwhile ?>
<?php if (dumb_luck("75%")): ?>
  <p><b>Related Story:</b> <a href="#"><?php dummy("text@headline") ?></a></p>
<?php endif ?>

An example of a simple article 3 to 8 paragraphs in length, with optionally appearing intro text and related story elements.

Loop positions and request queries

Inside a loop, dumb_question("order:first"), dumb_question("order:last"), and dumb_question("order:inner") test the current position. dumb_luck("") prints the one-based item number.

dumb_question("url:mode=compact") tests a query parameter, and dumb_question("segment:1=news") tests the first URL path segment. Laravel uses its current request automatically. See paths, cache and image settings for configuration.

In the meantime, follow the project on GitHub. Your ideas and contributions toward improving the code are welcome.