Skip to content

Framework Bootstrap

If your documentation examples need access to a framework (Laravel, Symfony, etc.) or a custom autoloader, you can configure a bootstrap file that runs before every code block.

Configuration

Add a bootstrap key to your doctest.php config:

php
return [
    'bootstrap' => '.doctest/bootstrap.php',
];

The bootstrap file is require_once'd at the top of every generated script, giving your code blocks access to the framework environment.

Examples

Laravel

Create a bootstrap file that boots the Laravel application:

php
// .doctest/bootstrap.php
require_once __DIR__.'/../vendor/autoload.php';

$app = require_once __DIR__.'/../bootstrap/app.php';
$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);
$kernel->bootstrap();

Your code blocks can then use facades, Eloquent, and the service container:

markdown
```php
$users = DB::table('users')->get();
echo $users->count();
```

Symfony

php
// .doctest/bootstrap.php
require_once __DIR__.'/../vendor/autoload.php';

$kernel = new \App\Kernel('test', true);
$kernel->boot();

Laravel Packages (Orchestra Testbench)

Laravel packages don't have a full application — they need Orchestra Testbench to create one. A bootstrap profile can set up the application, register service providers, configure the database, and run migrations:

php
// .doctest/laravel.php
require_once __DIR__.'/../vendor/autoload.php';

$app = \Orchestra\Testbench\Foundation\Application::create(
    options: [
        'extra' => [
            'providers' => [
                \YourVendor\YourPackage\YourServiceProvider::class,
                // Add any dependency service providers
            ],
        ],
    ],
);

Setting config values — use config()->set() after the application boots:

php
// .doctest/database.php
config()->set('database.default', 'testing');
config()->set('database.connections.testing', [
    'driver'   => 'sqlite',
    'database' => ':memory:',
]);
config()->set('cache.default', 'array');

Running migrations — include migration stubs directly from your package's database/migrations/ directory:

php
// .doctest/migrations.php
$migration = include __DIR__.'/../database/migrations/create_your_table.php.stub';
$migration->up();

Then compose profiles as needed:

markdown
```php bootstrap="laravel"
// Only needs the framework — no database
echo config('app.name');
```

```php bootstrap="laravel,database,migrations"
// Needs framework + database + tables
$count = DB::table('your_table')->count();
echo $count;
```
<!-- doctest: 0 -->

TIP

Keep each concern in a separate profile. Blocks that only need the framework skip the database overhead, and blocks that need the database get it by composing profiles.

Custom Autoloader

For projects without a framework, just load the autoloader:

php
// .doctest/bootstrap.php
require_once __DIR__.'/../vendor/autoload.php';

Bootstrap Profiles

When different code blocks need different environments — some need a framework, others need a database, others need neither — you can use bootstrap profiles instead of loading everything in a single global bootstrap file.

Creating Profiles

Create a .doctest/ directory in your project root and add PHP files. Each .php file at the top level becomes a profile:

.doctest/
├── laravel.php      → profile "laravel"   ✓
├── database.php     → profile "database"  ✓
├── helpers.php      → profile "helpers"   ✓
├── README.md        → ignored (not .php)  ✗
└── nested/
    └── special.php  → ignored (subdir)    ✗

Only .php files directly in .doctest/ are discovered — subdirectories and non-PHP files are ignored.

php
// .doctest/laravel.php
require_once __DIR__.'/../vendor/autoload.php';

$app = require_once __DIR__.'/../bootstrap/app.php';
$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);
$kernel->bootstrap();
php
// .doctest/database.php
$pdo = new PDO('sqlite::memory:');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

Using Profiles

Add bootstrap="profile-name" to the code fence info string:

markdown
```php bootstrap="laravel"
$users = User::all();
echo $users->count();
```

Blocks without a bootstrap attribute run without any profile — exactly as before.

Alternative: HTML Comment Syntax

markdown
<!-- doctest-attr: bootstrap="laravel" -->
```php
$user = User::factory()->create();
echo $user->exists;
```
<!-- doctest: 1 -->

This preserves editor syntax highlighting. See HTML Comment Syntax for details.

Composing Profiles

Combine multiple profiles with comma-separated names:

markdown
```php bootstrap="laravel,database"
// Both Laravel and database environments are available
DB::connection()->getPdo();
```

Profiles are loaded left to right: laravel.php first, then database.php.

Execution Order

When a code block runs, DocTest generates a single PHP file with all layers in shared scope:

php
<?php
// 1. Global bootstrap (from config, if set)
require_once '/path/to/bootstrap.php';

// 2. Bootstrap profiles (left to right, if specified)
require_once '/path/to/.doctest/laravel.php';
require_once '/path/to/.doctest/database.php';

// 3. Setup code (if block is in a group)
$pdo = new PDO('sqlite::memory:');

// 4. Your code block
echo $result;

// 5. Teardown code (if block is in a group)
$pdo = null;

All layers execute in the same process with shared variable scope.

Profiles with Groups

Bootstrap profiles work with grouped blocks. All blocks in the same group must use identical bootstrap profiles — mixing different profiles within a group causes a runtime error:

RuntimeException: All blocks in group "queries" must have identical bootstrap profiles.
markdown
```php bootstrap="database" setup group="queries"
$pdo->exec('CREATE TABLE items (id INTEGER PRIMARY KEY, name TEXT)');
$pdo->exec("INSERT INTO items (name) VALUES ('Widget')");
```

```php bootstrap="database" group="queries"
$stmt = $pdo->query('SELECT name FROM items');
echo $stmt->fetchColumn();
```
<!-- doctest: Widget -->

```php bootstrap="database" teardown group="queries"
$pdo->exec('DROP TABLE items');
```

Example: Mixed Documentation

A single markdown file can have blocks with different bootstrap needs:

markdown
## Pure PHP

```php
echo strtoupper('hello');
```
<!-- doctest: HELLO -->

## Framework Features

```php bootstrap="laravel"
echo config('app.name');
```
<!-- doctest: Laravel -->

## Database Queries

```php bootstrap="laravel,database"
$count = DB::table('users')->count();
echo $count;
```
<!-- doctest: 0 -->

Custom Profile Directory

The default profile directory is .doctest/. To use a different path, set bootstraps_dir in your doctest.php config:

php
return [
    'bootstraps_dir' => 'tests/.doctest',
];

Released under the MIT License.