1. Go to this page and download the library: Download seba1rx/exhaust library. Choose the download type require.
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
seba1rx / exhaust example snippets
use App\Controllers\Wellcome;
use App\Controllers\UserController;
$routes = new Route();
$routes->registerGetRoute('/', [Wellcome::class, 'showLandingPage'])->name('home');
$routes->registerPostRoute('/users', [UserController::class, 'store'])
->name('users.store')
->middlewares(['Authentication']);
$routes->registerGetRoute('/users/{id}', [UserController::class, 'show'])
->name('users.show');
$routes->registerPutRoute('/users/{id}', [UserController::class, 'update']);
$routes->registerDeleteRoute('/users/{id}', [UserController::class, 'destroy']);
// Single-action controller — no method, class must be invokable
$routes->registerDeleteRoute('/session', [\App\Controllers\Logout::class]);
namespace App\Controllers;
use App\Controllers\Controller;
use Exhaust\Response\Commands;
class UserController extends Controller
{
public function edit(array $payload): array
{
$html = app()->render('user/edit.html.twig', ['id' => $payload['id']]);
Commands::html('content-section', $html);
return Commands::all();
}
public function destroy(array $payload): array
{
// … delete logic …
Commands::dialog(type: 'success', options: Commands::dialogBuilder(
icon: 'success',
title: 'Deleted',
btn_confirm: ['text' => 'Ok', 'callback' => "Engeen.route('/users')"],
));
return Commands::all();
}
}
namespace App\Controllers;
class Controller
{
public function __construct(array $middlewares)
{
$this->invokeBeforeMiddlewares($middlewares, app()->request->payload);
}
// …
}
public function saveProfile(array $payload): array
{
$bio = $payload['bio'] ?? '';
if (empty($bio)) {
Commands::dialog(type: 'warning', options: Commands::dialogBuilder(
icon: 'warning',
title: 'Validation error',
text: 'Bio cannot be empty.',
));
return Commands::all();
}
// persist …
app()->dbLink->update(
'UPDATE users SET bio = :bio WHERE id = :id',
[':bio' => $bio, ':id' => $payload['userId']]
);
Commands::html('profile-bio', htmlspecialchars($bio));
Commands::toast('success', ['title' => 'Profile updated', 'duration' => 3000, 'position' => 'top-end']);
Commands::log(type: 'info', text: 'Profile saved', details: ['userId' => $payload['userId']]);
return Commands::all();
}
app()->request->payload // stdClass — all body params, type-cast automatically
app()->request->uri->path // '/users/7'
app()->request->getRequestMethod() // 'POST'
app()->request->isAsync() // true for XHR and Fetch
app()->request->isNavigation() // true for regular browser navigation
app()->request->getRemoteAddress()
app()->request->getUriParam('page') // query string: ?page=2
app()->request->hasUpload()
use Exhaust\Contracts\RequestBlueprint;
// Type-hinting in a service or utility that receives the request
public function audit(RequestBlueprint $request): void
{
$method = $request->getRequestMethod(); // 'POST'
$ip = $request->getRemoteAddress(); // '192.168.1.10'
$payload = $request->payloadAsArray(); // ['userId' => 7, 'action' => 'delete']
$isXHR = $request->isAsync(); // true
}
use Nyholm\Psr7\Factory\Psr17Factory;
$factory = new Psr17Factory();
$psr7 = app()->request->toPsr7($factory);
// Pass to any PSR-15 middleware or PSR-7-aware library
$psr7 = $psr7->withAttribute('user', $currentUser);
$response = $someExternalMiddleware->process($psr7, $handler);
namespace App\Middlewares;
use Exhaust\Contracts\MiddlewareBlueprint;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Nyholm\Psr7\Response;
final class AuthMiddleware implements MiddlewareBlueprint
{
/**
* Validates the session and either short-circuits with a redirect or
* delegates to the next handler in the pipeline.
*
* @param ServerRequestInterface $request
* @param RequestHandlerInterface $handler
* @return ResponseInterface
*/
public function process(
ServerRequestInterface $request,
RequestHandlerInterface $handler,
): ResponseInterface
{
if (!isset($_SESSION['user'])) {
// Short-circuit: return a redirect without reaching the controller
return (new Response(302))->withHeader('Location', '/login');
}
// Delegate to the next middleware or the controller
return $handler->handle($request);
}
}
namespace App\Middlewares;
final class AuthMiddleware
{
/** Redirects to login if no active session exists. */
public function __invoke(): void
{
if (!isset($_SESSION['user'])) {
header('Location: /login');
exit;
}
}
}
namespace App\Controllers;
use Exhaust\Contracts\MiddlewareBlueprint;
use Nyholm\Psr7\Factory\Psr17Factory;
class Controller
{
public function __construct(array $middlewares)
{
$this->invokeBeforeMiddlewares($middlewares, app()->request->payload);
}
/**
* Executes each middleware before the controller action.
* Supports both PSR-15 (MiddlewareBlueprint) and legacy invokable middlewares.
*
* @param array $middlewares List of middleware class names.
* @param \stdClass $payload Current request payload.
* @return void
*/
public function invokeBeforeMiddlewares(
array $middlewares,
\stdClass $payload = new \stdClass,
): void
{
$factory = new Psr17Factory();
foreach ($middlewares as $className) {
$middleware = new ("\\App\\Middlewares\\{$className}")();
if ($middleware instanceof MiddlewareBlueprint) {
// PSR-15 path
$psr7 = app()->request->toPsr7($factory);
$handler = new \App\Http\FinalHandler($factory);
$middleware->process($psr7, $handler);
} else {
// Legacy invokable path
$middleware($payload);
}
}
if (app()->terminate_session) {
app()->response = new \stdClass;
}
}
}
namespace App\Http;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Nyholm\Psr7\Factory\Psr17Factory;
/**
* Terminal handler for the PSR-15 middleware pipeline.
*
* Returns an empty 200 response — the actual response body is built by
* Exhaust's Commands system and sent separately by app()->run().
*/
final class FinalHandler implements RequestHandlerInterface
{
public function __construct(private readonly Psr17Factory $factory) {}
/**
* @param ServerRequestInterface $request
* @return ResponseInterface
*/
public function handle(ServerRequestInterface $request): ResponseInterface
{
return $this->factory->createResponse(200);
}
}
namespace App\Http;
use Exhaust\Contracts\ResponseBlueprint;
use Exhaust\Exceptions\LogicException;
use Exhaust\Request\RequestType;
use Exhaust\Tools\CastingTool;
/**
* Wraps the validated Commands array and serialises it as HTML or JSON
* depending on the originating request type.
*/
final class Response implements ResponseBlueprint
{
/**
* @param array $commands Validated command set from Commands::all().
* @param RequestType $requestType Detected type of the originating request.
*/
public function __construct(
private readonly array $commands,
private readonly RequestType $requestType,
) {}
/** {@inheritdoc} */
public function getCommands(): array
{
return $this->commands;
}
/** {@inheritdoc} */
public function toJson(): string
{
return json_encode(CastingTool::arrayToObject($this->commands));
}
/**
* {@inheritdoc}
* @throws LogicException When the Commands set has no 'echo' entry.
*/
public function toHtml(): string
{
if (!isset($this->commands['echo'])) {
throw new LogicException('Response has no echo command — cannot render HTML.');
}
return $this->commands['echo'];
}
/** {@inheritdoc} */
public function send(): void
{
if ($this->requestType === RequestType::Navigation) {
header('Content-Type: text/html; charset=UTF-8');
echo $this->toHtml();
} else {
header('Content-Type: application/json; charset=utf-8');
echo $this->toJson();
}
}
}
use Exhaust\Response\Commands;
use App\Http\Response;
// Build commands as usual …
Commands::html('content', $html);
Commands::toast('success', ['title' => 'Saved', 'duration' => 3000]);
// Wrap in the concrete Response and send
$response = new Response(
commands: Commands::all(),
requestType: app()->request->requestType,
);
$response->send();