Conduit
Middleware for PHP
Install / Use
/learn @phly/ConduitREADME
Conduit
:warning: Archived 2025-08-17
Abandoned! Or, rather, rebranded!
phly/conduit has moved to the zendframework organization as zend-stratigility (from Strata, meaning "layer," and "agility").
Please use that package instead, and contribute issues and pull requests against it I have closed issues and pull requests against phly/conduit at this time.
Conduit is a port of Sencha Connect to PHP. It allows you to build applications out of middleware.
Installation and Requirements
Install this library using composer:
$ composer require phly/http phly/conduit
Conduit has the following dependencies (which are managed by Composer):
psr/http-message, which provides the interfaces specified in PSR-7, and type-hinted against in this package. In order to use Conduit, you will need an implementation of PSR-7; one such package is phly/http (and hence the reference to it in the install line above).zendframework/zend-escaper, used by theFinalHandlerfor escaping error messages prior to passing them to the response.
You can provide your own request and response implementations if desired as long as they implement the PSR HTTP message interfaces.
Usage
Creating an application consists of 3 steps:
- Create middleware or a middleware pipeline
- Create a server, using the middleware
- Instruct the server to listen for a request
use Phly\Conduit\MiddlewarePipe;
use Phly\Http\Server;
require __DIR__ . '/../vendor/autoload.php';
$app = new MiddlewarePipe();
$server = Server::createServer($app,
$_SERVER,
$_GET,
$_POST,
$_COOKIE,
$_FILES
);
$server->listen();
The above example is useless by itself until you pipe middleware into the application.
Middleware
What is middleware?
Middleware is code that exists between the request and response, and which can take the incoming request, perform actions based on it, and either complete the response or pass delegation on to the next middleware in the queue.
use Phly\Conduit\MiddlewarePipe;
use Phly\Http\Server;
require __DIR__ . '/../vendor/autoload.php';
$app = new MiddlewarePipe();
$server = Server::createServer($app, $_SERVER, $_GET, $_POST, $_COOKIE, $_FILES);
// Landing page
$app->pipe('/', function ($req, $res, $next) {
if ($req->getUri()->getPath() !== '/') {
return $next($req, $res);
}
return $res->end('Hello world!');
});
// Another page
$app->pipe('/foo', function ($req, $res, $next) {
return $res->end('FOO!');
});
$server->listen();
In the above example, we have two examples of middleware. The first is a landing page, and listens at the path /. If the path is an exact match, it completes the response. If it is not, it delegates to the next middleware in the stack. The second middleware matches on the path /foo -- meaning it will match /foo, /foo/, and any path beneath. In that case, it will complete the response with its own message. If no paths match at this point, a "final handler" is composed by default to report 404 status.
So, concisely put, middleware are PHP callables that accept a request and response object, and do something with it.
Middleware can decide more processing can be performed by calling the $next callable that is passed as the third argument. With this paradigm, you can build a workflow engine for handling requests -- for instance, you could have middleware perform the following:
- Handle authentication details
- Perform content negotiation
- Perform HTTP negotiation
- Route the path to a more appropriate, specific handler
Each middleware can itself be middleware, and can attach to specific paths -- allowing you to mix and match applications under a common domain. As an example, you could put API middleware next to middleware that serves its documentation, next to middleware that serves files, and segregate each by URI:
$app->pipe('/api', $apiMiddleware);
$app->pipe('/docs', $apiDocMiddleware);
$app->pipe('/files', $filesMiddleware);
The handlers in each middleware attached this way will see a URI with that path segment stripped -- allowing them to be developed separately and re-used under any path you wish.
Within Conduit, middleware can be:
- Any PHP callable that accepts, minimally, a PSR-7 request and a response (in that order), and, optionally, a callable (for invoking the next middleware in the queue, if any).
- An object implementing
Phly\Conduit\MiddlewareInterface.Phly\Conduit\MiddlewarePipeimplements this interface.
Error Handlers
To handle errors, you can write middleware that accepts exactly four arguments:
function ($error, $request, $response, $next) { }
Alternately, you can implement Phly\Conduit\ErrorMiddlewareInterface.
When using MiddlewarePipe, as the queue is executed, if $next() is called with an argument, or if an exception is thrown, middleware will iterate through the queue until the first such error handler is found. That error handler can either complete the request, or itself call $next(). Error handlers that call $next() SHOULD call it with the error it received itself, or with another error.
Error handlers are usually attached at the end of middleware, to prevent attempts at executing non-error-handling middleware, and to ensure they can intercept errors from any other handlers.
Creating Middleware
To create middleware, write a callable capable of receiving minimally a request and a response object, and optionally a callback to call the next in the chain. In your middleware, you can handle as much or as little of the request as you want -- including delegating to other middleware. If your middleware accepts a third argument, $next, if it is unable to complete the request, or allows further processing, it can call it to return handling to the parent middleware.
As an example, consider the following middleware which will use an external router to map the incoming request path to a handler; if unable to map the request, it returns processing to the next middleware.
function ($req, $res, $next) use ($router) {
$path = $req->getUri()->getPath();
// Route the path
$route = $router->route($path);
if (! $route) {
return $next($req, $res);
}
$handler = $route->getHandler();
return $handler($req, $res, $next);
}
Middleware written in this way can be any of the following:
- Closures (as shown above)
- Functions
- Static class methods
- PHP array callbacks (e.g.,
[ $dispatcher, 'dispatch' ], where$dispatcheris a class instance) - Invokable PHP objects (i.e., instances of classes implementing
__invoke()) - Objects implementing
Phly\Conduit\MiddlewareInterface(includingPhly\Conduit\MiddlewarePipe)
In all cases, if you wish to implement typehinting, the signature is:
function (
Psr\Http\Message\ServerRequestInterface $request,
Psr\Http\Message\ResponseInterface $response,
callable $next = null
) {
}
The implementation Conduit offers also allows you to write specialized error handler middleware. The signature is the same as for normal middleware, except that it expects an additional argument prepended to the signature, $error. (Alternately, you can implement Phly\Conduit\ErrorMiddlewareInterface.) The signature is:
function (
$error, // Can be any type
Psr\Http\Message\ServerRequestInterface $request,
Psr\Http\Message\ResponseInterface $response,
callable $next
) {
}
Executing and composing middleware
The easiest way to execute middleware is to write closures and attach them to a Phly\Conduit\MiddlewarePipe instance. You can nest MiddlewarePipe instances to create groups of related middleware, and attach them using a base path so they only execute if that path is matched.
$api = new MiddlewarePipe(); // API middleware collection
$api->pipe(/* ... */); // repeat as necessary
$app = new MiddlewarePipe(); // Middleware representing the application
$app->pipe('/api', $api); // API middleware attached to the path "/api"
Another approach is to extend the Phly\Conduit\MiddlewarePipe class itself -- particularly if you want to allow attaching other middleware to your own middleware. In such a case, you will generally override the __invoke() method to perform any additional logic you have, and then call on the parent in order to iterate through your stack of middleware:
use Phly\Conduit\MiddlewarePipe;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
class CustomMiddleware extends MiddlewarePipe
{
public function __invoke(Request $request, Response $response, callable $next = null)
{
// perform some work...
// delegate to parent
parent::__invoke($request, $response, $next);
// maybe do more work?
}
}
Another approach using this method would be to override the constructor to add in specific middleware, perhaps using configuration provided. In this case, make sure to also call parent::__construct() to ensure the middleware queue i
Related Skills
node-connect
341.8kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
84.6kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
341.8kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
commit-push-pr
84.6kCommit, push, and open a PR



