Search by

movescode / middleware

DjalmaMartins

A lightweight and flexible middleware pipeline for PHP applications.

Package info

github.com/djalmamartins/middleware

pkg:composer/movescode/middleware

Statistics

Installs: 648

Dependents: 1

Suggesters: 0

Stars: 0

Open Issues: 0

0.1.0 2026-09-10 14:18 UTC

This package is auto-updated.

Last update: 2026-10-08 07:38:15 UTC


README

A lightweight and flexible middleware pipeline for PHP applications.

Requisitos e instalação

PHP 8.2+ e Composer. Sem dependências de produção além do PHP. Após a publicação de uma release:

composer require movescode/middleware

Esta versão inicial ainda não possui tag nem publicação no Packagist. Para desenvolvimento, clone o repositório e execute composer install.

Primeiro pipeline

<?php

declare(strict_types=1);

require __DIR__ . '/vendor/autoload.php';

use MovesCode\Middleware\MiddlewareInterface;
use MovesCode\Middleware\Pipeline;

/**
 * MovesCode | FirstMiddleware
 *
 * Demonstra transformação do resultado da aplicação.
 *
 * @author Djalma Martins
 * @package MovesCode\Middleware
 */
final class FirstMiddleware implements MiddlewareInterface
{
    public function handle(callable $next): mixed
    {
        // Antes da próxima etapa.
        $result = $next();
        // Depois da próxima etapa.
        return '[' . $result . ']';
    }
}

$result = (new Pipeline())
    ->pipe(new FirstMiddleware())
    ->then(static fn () => 'done'); // [done]

$empty = (new Pipeline())->then(static fn () => 'done'); // done

pipe(MiddlewareInterface $middleware): self registra objetos na ordem e retorna a mesma instância. Callables, strings de classe e arrays não são aceitos como middleware. then(callable $handler): mixed executa a sequência e retorna seu resultado, inclusive null, objetos ou valores escalares. O handler final aceita Closure, nome de função, método de objeto ([$service, 'run']), método estático ou objeto invocável, explicitamente registrados pela aplicação. Ele é chamado sem argumentos; deve aceitar uma chamada sem argumentos. Nunca converta nomes recebidos por HTTP em handlers.

Ordem e interrupção

Para A e B registrados nessa ordem:

A BEFORE
B BEFORE
Handler
B AFTER
A AFTER

Um middleware pode transformar o retorno ou interromper a cadeia retornando sem chamar $next(). Nenhuma etapa posterior será executada. Cada registro é executado no máximo uma vez por execução; registrar o mesmo objeto duas vezes cria duas etapas intencionais.

A continuação só pode ser chamada uma vez e durante a chamada de handle(). Repetição, inclusive depois de uma exceção downstream, ou uso posterior lança MiddlewareException. Não há retries internos. O contrato é síncrono; não armazene continuações para tarefas assíncronas.

Exceções e reutilização

Exceções e erros da aplicação propagam sem conversão, logging ou alteração. Um middleware externo pode capturá-los conforme a política da aplicação. MovesCode\Middleware\Exception\MiddlewareException estende LogicException e indica uso inválido da continuação; argumentos inválidos geram TypeError pelo PHP.

Cada then() constrói sua própria cadeia a partir dos registros existentes. Um pipe() durante a execução só participa das próximas execuções. O pipeline pode ser reutilizado após sucesso ou exceção: não mantém cursor, resultado, handler ou estado de execução em propriedades. Alterações explícitas nos registros não são revertidas em caso de erro.

Os objetos registrados são reutilizados, não clonados. Estado guardado por middleware ou dependências pertence à aplicação. Em workers e servidores persistentes, prefira middlewares sem estado de requisição ou crie uma instância por requisição. Não compartilhe objetos mutáveis entre tarefas concorrentes sem uma política de isolamento.

Integração com aplicações

Use composição ou injeção pelo construtor para acessar contexto da aplicação. O pacote funciona em web, APIs e CLI e não define Request ou Response.

Exemplo conceitual de autenticação, exclusivamente no código consumidor (Auth e Response são classes da aplicação, não deste pacote):

<?php

declare(strict_types=1);

use MovesCode\Middleware\MiddlewareInterface;

/**
 * MovesCode | AuthMiddleware
 *
 * Aplica a política de autenticação da aplicação consumidora.
 *
 * @author Djalma Martins
 * @package MovesCode\Middleware
 */
final class AuthMiddleware implements MiddlewareInterface
{
    public function handle(callable $next): mixed
    {
        if (!Auth::check()) {
            return Response::to('/login');
        }

        return $next();
    }
}

Security

  • Erros apresentados pela aplicação não devem expor argumentos, tokens, senhas ou outros dados sensíveis.
  • Autenticação e autorização pertencem exclusivamente à aplicação.
  • Só execute código confiável. O pacote não é sandbox para middlewares maliciosos. Não usa eval, resolução de classes, includes dinâmicos, estado global ou logging automático.
  • Tipos de entrada restringem middleware a objetos do contrato e handler a um callable válido. Essa validação verifica se ele pode ser chamado, não se é confiável. Não passe dados de requisição a mecanismos de criação de callables.
  • A sequência de cada execução é fixa. Continuações têm proteção contra repetição e execução após retorno/erro do middleware.
  • Não há autenticação, sessões, CSRF, redirects, banco, router, container ou regras de negócio no pacote.
  • A aplicação deve validar destinos derivados de usuários para evitar open redirect; prefira destinos internos permitidos.
  • Autenticação e sessão pertencem à aplicação. Configure session.use_strict_mode, cookies seguros e regenere o identificador de sessão após elevação de privilégio.
  • Um CsrfMiddleware pode interromper a cadeia. Use tokens criptograficamente seguros com random_bytes() e comparação com hash_equals(); a aplicação define armazenamento, expiração e validação.
  • A cadeia tem custo O(n) de construção e memória/pilha O(n). O teste cobre 1.000 etapas; isso não é garantia de profundidade ilimitada. Limites dependem do runtime, extensões e memória. Não derive a quantidade de registros de entrada não confiável. Recursão explícita, loops e chamadas reentrantes criadas pela aplicação continuam sob responsabilidade dela.
  • O pipeline não retém dados de execução após retorno; um middleware que capture uma continuação pode reter objetos downstream, embora não possa executá-la depois. Não retenha essas referências.

Relação com PSR-15

PSR-15 especifica middleware HTTP com process(ServerRequestInterface, RequestHandlerInterface): ResponseInterface, usando mensagens PSR-7. Esta API genérica usa handle(callable $next): mixed, sem mensagens HTTP obrigatórias. Ela não implementa PSR-15 e não instala psr/http-message ou psr/http-server-middleware.

Uma futura camada separada pode implementar os contratos PSR e adaptar o contexto HTTP por composição, mantendo Pipeline e MiddlewareInterface intactos. Essa integração não faz parte da versão inicial.

Desenvolvimento e versionamento

composer validate --strict
composer install
composer dump-autoload
vendor/bin/phpunit
composer analyse
composer audit

Execute também php -l caminho/do/arquivo.php em cada arquivo PHP de src/ e tests/. PHPUnit 11.5 e PHPStan são dependências apenas de desenvolvimento. A resolução de dependências usa config.platform.php: 8.2.0 para preservar a versão mínima mesmo em máquinas mais recentes. O lockfile é versionado para fixar o ambiente de desenvolvimento; consumidores de bibliotecas resolvem suas próprias dependências.

Versões serão definidas exclusivamente por tags Git seguindo Semantic Versioning, sem campo version no Composer. Mudanças incompatíveis após estabilização exigem versão major. Nenhuma tag, versão 1.0.0 ou publicação no Packagist será feita sem aprovação do mantenedor.

Licença MIT. Autor: Djalma Martins.