This guide walks through installing Compose, bootstrapping the starter pipeline, and serving a simple page. It assumes a clean directory and access to PHP 8.3 with Composer installed.
php
executable available on your PATH
.Create a new project directory and require Compose:
mkdir my-compose-app
cd my-compose-app
composer init --name="acme/hello-compose" --require="php:~8.3" --quiet
composer require phpcompose/compose:^1.0@rc
Composer will install the framework along with Laminas PSR-7 components.
Create a public/
directory with an index.php
file. This is the entry point that bootstraps the pipeline.
<?php
declare(strict_types=1);
use Compose\Starter;
require __DIR__ . '/../vendor/autoload.php';
// load or compose your configuration array
$config = require __DIR__ . '/../config/app.php';
Starter::start($config);
The starter builds an instance of Compose\Http\Pipeline
, pipes the default middleware stack, and starts listening for requests.
Next, add a config/app.php
file. Start with the baseline framework configuration and merge your overrides:
<?php
declare(strict_types=1);
use Compose\Config;
$base = (new Config())();
return array_replace_recursive($base, [
'app' => [
'name' => 'Hello Compose',
],
'template' => [
'layout' => 'layouts::app',
'folders' => [
'layouts' => __DIR__ . '/../layouts',
],
],
'pages' => [
'dir' => __DIR__ . '/../pages',
],
]);
Configuration is stored as an array (or Compose\Support\Configuration
instance) and injected into the service container. You can override services, middleware, routes, subscribers, and template renderer settings the same way.
The value layouts::app
tells the renderer to use the app.phtml
script inside the folder that was aliased as layouts
in the configuration above.
Compose ships with a Pages middleware (the primary feature) that renders templates with optional code-behind scripts. Add the following files:
layouts/app.phtml
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title><?= $this->e($title ?? 'Compose App') ?></title>
</head>
<body>
<?= $this->section('content') ?>
</body>
</html>
pages/index.phtml
<?php $this->layout('layouts::app', ['title' => $title ?? 'Compose Demo']); ?>
<h1><?= $this->e($title ?? 'Hello from Compose') ?></h1>
<p><?= $this->e($message ?? 'This response came from pages/index.phtml.') ?></p>
pages/index.phtml.php
<?php
use Psr\Http\Message\ServerRequestInterface;
return static function (ServerRequestInterface $request): array {
return [
'title' => 'Compose Demo',
'message' => 'Hello from the Pages middleware!',
];
};
The .phtml.php
suffix lets you keep the view template and an optional script together. When the page file returns an array, the value is passed to the template as data. Returning a ResponseInterface
short-circuits the rendering pipeline. If no code-behind script is present, the template runs on its own.
Run the built-in PHP development server from the project root:
php -S 0.0.0.0:8080 -t public/
Visit http://localhost:8080
to see the app. The starter wires the following middleware by default:
OriginalMessages
.middleware
config key).