확장이 SP Kernel 미들웨어 그룹을 직접 조작하거나 라우트 파일에 미들웨어 FQCN 을 직접 부착하던 임시 방식을, 확장이 부착 대상(targets)을 명시 선언하고 코어가 요청 시점에 라우트명·URI 로 매칭해 실행하는 self-gate 로 정규화. 번들 7건(ecommerce/gdpr/pay 3종) 이전, audit 룰 3건·문서·버전 동기화 동반. 부수적으로 generate-skills.cjs 가 재생성 시 신의성실(부수의무) 섹션을 소실 시키던 회귀를 수정 — 스킬별 불릿을 스크립트 SSoT 로 편입해 재생성에도 보존.
66 lines
2.7 KiB
PHP
66 lines
2.7 KiB
PHP
<?php
|
||
|
||
namespace App\Http\Middleware;
|
||
|
||
use App\Contracts\Extension\ExtensionMiddlewareRegistryInterface;
|
||
use Closure;
|
||
use Illuminate\Contracts\Foundation\Application;
|
||
use Illuminate\Http\Request;
|
||
use Illuminate\Pipeline\Pipeline;
|
||
use Symfony\Component\HttpFoundation\Response;
|
||
|
||
/**
|
||
* 확장 선언 미들웨어의 self-gate 실행기.
|
||
*
|
||
* 확장이 getMiddleware() 로 선언한 미들웨어를 web/api 그룹에 직접 넣는 대신,
|
||
* 이 래퍼가 그룹에 등록되어 요청 시점에 현재 라우트 이름·URI 가 각 확장 미들웨어의
|
||
* targets 패턴에 매칭될 때만 해당 미들웨어를 동적으로 실행한다. 매칭 없으면
|
||
* 즉시 통과 (코어 EnforceIdentityPolicy 의 라우트명 인덱스 조회 모델과 동일).
|
||
*
|
||
* 부팅 시점 라우트 순회로 확장 간 미들웨어를 부착할 수 없는 제약(확장 라우트는
|
||
* booted 지연 등록 + 순서 미보장)을 요청 시점 매칭으로 원천 회피한다.
|
||
*
|
||
* bootstrap/app.php 에서 web/api × before_core/after_core 로 총 4회 등록된다.
|
||
* Kernel 상 prepend/append 위치는 등록 시점 매핑이고, 게이트 실행 로직은 timing 을 안다.
|
||
*
|
||
* @since 7.0.5
|
||
*/
|
||
class ExtensionMiddlewareGate
|
||
{
|
||
/**
|
||
* @param Application $app Pipeline 실행용 컨테이너
|
||
* @param ExtensionMiddlewareRegistryInterface $registry 확장 미들웨어 매칭 인덱스
|
||
*/
|
||
public function __construct(
|
||
protected Application $app,
|
||
protected ExtensionMiddlewareRegistryInterface $registry,
|
||
) {}
|
||
|
||
/**
|
||
* 미들웨어 진입점.
|
||
*
|
||
* @param Request $request HTTP 요청
|
||
* @param Closure $next 다음 파이프라인
|
||
* @param string $group 이 래퍼가 등록된 그룹 ('web'|'api')
|
||
* @param string $timing 이 래퍼 인스턴스가 담당하는 실행 시점 ('before_core'|'after_core')
|
||
*/
|
||
public function handle(Request $request, Closure $next, string $group, string $timing): Response
|
||
{
|
||
// 라우트명(무명이면 빈 문자열)과 URI path(항상 존재, 선행 슬래시 없음) 둘 다 전달 —
|
||
// 무명 SSR 셸 catch-all 은 URI 패턴 target 으로만 매칭되므로 routeName 빈 값에 조기 통과하지 않는다.
|
||
$routeName = $request->route()?->getName() ?? '';
|
||
$path = $request->path();
|
||
$matched = $this->registry->resolveForRoute($routeName, $path, $group, $timing);
|
||
|
||
if ($matched === []) {
|
||
return $next($request);
|
||
}
|
||
|
||
// 매칭된 확장 미들웨어들을 Laravel Pipeline 으로 순차 실행 후 $next.
|
||
return (new Pipeline($this->app))
|
||
->send($request)
|
||
->through($matched)
|
||
->then($next);
|
||
}
|
||
}
|