Pular para o conteúdo

ADR-005 - Padrão de Middleware PSR-15

ADR-005: Padrão de Middleware PSR-15 para XOOPS 4.0

Seção intitulada “ADR-005: Padrão de Middleware PSR-15 para XOOPS 4.0”

Adote manipuladores de requisição de servidor HTTP PSR-15 (middleware) para pipeline de processamento de requisição melhorado.


Proposto - Sob avaliação para lançamento XOOPS 4.0


XOOPS 2.5 usa uma abordagem monolítica de manipulação de requisição:

// Atual: Processamento sequencial
require_once 'mainfile.php';
// → Inicialização do kernel
// → Autenticação de usuário
// → Carregamento de módulo
// → Renderização de página
// Tudo em um fluxo, preocupações misturadas
  1. Preocupações Misturadas - Autenticação, logging, roteamento todos entrelaçados
  2. Difícil de Testar - Difícil fazer teste unitário de etapas de processamento de requisição individual
  3. Difícil de Estender - Módulos apenas podem usar preload/eventos
  4. Separação Pobre - Lógica de processamento de requisição espalhada por codebase
  5. Não Componível - Não é fácil encadear ou reordenar etapas de processamento

PSR-15 define uma interface padrão para middleware HTTP:

<?php
interface RequestHandlerInterface {
public function handle(ServerRequestInterface $request): ResponseInterface;
}
interface MiddlewareInterface {
public function process(
ServerRequestInterface $request,
RequestHandlerInterface $handler
): ResponseInterface;
}

Cadeia de Middleware:

Requisição
[Logger] → registra requisição
[Auth] → valida sessão de usuário
[CORS] → verifica origem cruzada
[Router] → despacha para manipulador
[Handler] → gera resposta
Resposta

Implementar um pipeline de processamento de requisição baseado em middleware seguindo padrão PSR-15.

graph TD
subgraph "Pipeline de Processamento de Requisição"
A["Requisição HTTP<br/>(PSR-7 ServerRequest)"]
B["Pilha de Middleware<br/>(PSR-15)"]
C["Middleware de Logger"]
D["Middleware de Sessão"]
E["Middleware de Auth"]
F["Middleware de CORS"]
G["Middleware de Router"]
H["Manipulador<br/>(Controller/Action)"]
I["Resposta<br/>(PSR-7 Response)"]
end
A --> B
B --> C
C --> D
D --> E
E --> F
F --> G
G --> H
H --> I
<?php
declare(strict_types=1);
namespace XoopsCore;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
class SessionMiddleware implements MiddlewareInterface
{
public function process(
ServerRequestInterface $request,
RequestHandlerInterface $handler
): ResponseInterface {
// 1. Recuperar sessão (ou iniciar nova)
$sessionId = $request->getCookieParams()['PHPSESSID'] ?? null;
$session = $this->sessionManager->load($sessionId);
// 2. Anexar sessão à requisição
$request = $request->withAttribute('session', $session);
// 3. Passar para próximo middleware
$response = $handler->handle($request);
// 4. Definir cookie de sessão se necessário
if ($session->isModified()) {
$response = $response->withAddedHeader(
'Set-Cookie',
'PHPSESSID=' . $session->getId() . '; HttpOnly; SameSite=Strict'
);
}
return $response;
}
}
<?php
class AuthMiddleware implements MiddlewareInterface
{
public function process(
ServerRequestInterface $request,
RequestHandlerInterface $handler
): ResponseInterface {
// Obter sessão do middleware anterior
$session = $request->getAttribute('session');
// Autenticar usuário da sessão
$user = $this->authenticate($session);
// Anexar usuário à requisição
$request = $request->withAttribute('user', $user);
return $handler->handle($request);
}
private function authenticate(?Session $session): User
{
if ($session && $session->has('uid')) {
return $this->userRepository->findById($session->get('uid'));
}
return new AnonymousUser();
}
}
<?php
class AuthorizationMiddleware implements MiddlewareInterface
{
public function __construct(private AuthorizationChecker $checker)
{
}
public function process(
ServerRequestInterface $request,
RequestHandlerInterface $handler
): ResponseInterface {
$user = $request->getAttribute('user');
$route = $request->getAttribute('route');
// Verificar se usuário tem permissão para esta rota
if (!$this->checker->isGranted($user, $route)) {
return new JsonResponse(
['error' => 'Não autorizado'],
403
);
}
return $handler->handle($request);
}
}
<?php
// Módulos podem fornecer seu próprio middleware
class PublisherAccessMiddleware implements MiddlewareInterface
{
public function process(
ServerRequestInterface $request,
RequestHandlerInterface $handler
): ResponseInterface {
$user = $request->getAttribute('user');
// Controle de acesso específico do módulo
if (!$user->hasPermission('publisher_view')) {
return new HtmlResponse('Acesso negado', 403);
}
return $handler->handle($request);
}
}
<?php
// bootstrap.php - Configuração de aplicação
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Xoops\Core\Middleware\{
LoggerMiddleware,
SessionMiddleware,
AuthMiddleware,
CorsMiddleware,
ErrorHandlingMiddleware
};
// Criar pipeline de middleware
$middlewareStack = [
// 1. Manipulação de erro (mais externo)
new ErrorHandlingMiddleware(),
// 2. Logging
new LoggerMiddleware($logger),
// 3. Manipulação CORS
new CorsMiddleware($corsConfig),
// 4. Gerenciamento de sessão
new SessionMiddleware($sessionManager),
// 5. Autenticação
new AuthMiddleware($userRepository),
// 6. Autorização
new AuthorizationMiddleware($authChecker),
// 7. Roteamento e despacho
new RoutingMiddleware($router),
// 8. Middleware de módulo (dinâmico)
...$this->loadModuleMiddleware(),
];
// Processar requisição através pilha de middleware
$request = ServerRequestFactory::fromGlobals();
$dispatcher = new MiddlewareDispatcher($middlewareStack);
$response = $dispatcher->dispatch($request);
// Enviar resposta
http_response_code($response->getStatusCode());
foreach ($response->getHeaders() as $name => $values) {
foreach ($values as $value) {
header("$name: $value", false);
}
}
echo $response->getBody();

Módulos podem fornecer middleware:

<?php
// Módulo Publisher - xoops_version.php
$modversion['middleware'] = [
'PublisherAccessMiddleware' => true, // Auto-carregamento
'PublisherLogMiddleware' => true,
];
// Ou customizado:
$modversion['middleware_factory'] = function() {
return [
new PublisherCacheMiddleware(),
new PublisherPermissionMiddleware(),
];
};

  1. Separação de Preocupações - Cada middleware manipula uma responsabilidade
  2. Testabilidade - Fácil fazer teste unitário de componentes de middleware individuais
  3. Componibilidade - Middleware pode ser combinado e reordenado
  4. Em Conformidade com Padrões - Usa padrões PSR-15 e PSR-7
  5. Extensibilidade - Módulos podem adicionar middleware customizado facilmente
  6. Depuração - Fluxo de requisição claro através pipeline
  7. Performance - Pode otimizar camadas de middleware específicas
  8. Interoperabilidade - Pode usar middleware PSR-15 de terceiros
  1. Curva de Aprendizado - Desenvolvedores devem entender PSR-15
  2. Overhead de Performance - Mais chamadas de função em pipeline
  3. Complexidade - Mais partes móveis que abordagem monolítica
  4. Esforço de Migração - Requer refatoração de código existente
  5. Dependências - Requer biblioteca HTTP PSR-7
RiscoSeveridadeMitigação
Cadeias de middleware complexasMédiaDocumentação clara, exemplos
Degradação de performanceMédiaBenchmark, otimizar caminhos quentes
Uso incorreto do desenvolvedorMédiaRevisão de código, guia de melhores práticas
Mudanças de ruptura de migraçãoAltaPeríodo de deprecação, helpers
Problemas de ordenação de middlewareMédiaGráfico de dependência claro

  • Implementar wrapper de mensagem HTTP PSR-7
  • Criar MiddlewareDispatcher
  • Implementar middleware principal (sessão, auth)
  • Atualizar kernel para usar middleware
  • Migrar funcionalidade existente para middleware
  • Adicionar suporte de middleware de módulo
  • Criar utilitários de teste de middleware
  • Escrever documentação abrangente
  • Fornecer camada de compatibilidade para código antigo
  • Ajudar módulos a atualizar para novo middleware
  • Otimização de performance
  • Auditoria de segurança
  • Lançamento XOOPS 4.0 com middleware
  • Deprecar sistema antigo de preload/hook
  • Feedback da comunidade e atualizações

  • Todas funcionalidade principal migrada para middleware
  • Cobertura de teste 90%+ para middleware
  • Documentação completa com exemplos
  • Performance dentro de 10% de versão anterior
  • Módulos usam com sucesso novo sistema de middleware
  • Taxa de adoção da comunidade >80%

  • Manter middleware focado (responsabilidade única)
  • Usar imutabilidade (criar nova requisição/resposta)
  • Manipular erros gracefully
  • Documentar dependências
  • Adicionar type hints
  • Escrever testes para middleware
  • Usar interfaces padrão PSR-15
  • Não modificar objetos de requisição/resposta compartilhados
  • Não acessar globais diretamente
  • Não criar dependências em ordem de middleware
  • Não capturar todas exceções
  • Não misturar lógica de negócios com middleware
  • Não fazer middleware fazer muito

<?php
// Exemplo: Middleware de limitação de taxa
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
class RateLimitMiddleware implements MiddlewareInterface
{
public function __construct(
private RateLimiter $limiter,
private int $limit = 100,
private int $window = 3600
) {
}
public function process(
ServerRequestInterface $request,
RequestHandlerInterface $handler
): ResponseInterface {
$user = $request->getAttribute('user');
$identifier = $user->getId() ?? $request->getClientIp();
// Verificar limite de taxa
$remaining = $this->limiter->check($identifier, $this->limit, $this->window);
if ($remaining < 0) {
return new JsonResponse(
['error' => 'Limite de taxa excedido'],
429
);
}
// Adicionar cabeçalhos de limite de taxa
$response = $handler->handle($request);
return $response
->withAddedHeader('X-RateLimit-Limit', (string)$this->limit)
->withAddedHeader('X-RateLimit-Remaining', (string)$remaining);
}
}

  • ADR-001: Arquitetura Modular - Fundação
  • ADR-004: Sistema de Segurança - Usa middleware para auth
  • ADR-006: Auth de Dois Fatores - Pode ser middleware


VersãoDataMudanças
1.0.02024-01-28Proposta inicial

#xoops #adr #psr-15 #middleware #architecture #psr-7