00x1 错误链分析

cpp
[Thu Mar 12 11:40:55 2026] [::1]:49795 Accepted
//错误类型
//Uncaught Slim\Exception\HttpNotFoundException
//错误信息
//Not found.
//触发文件
//in C:\Users\jc\Desktop\Slim-4.x\Slim\Middleware\RoutingMiddleware.php:76
[Thu Mar 12 11:40:55 2026] PHP Fatal error: Uncaught Slim\Exception\HttpNotFoundException: Not found. in C:\Users\jc\Desktop\Slim-4.x\Slim\Middleware\RoutingMiddleware.php:76
//问题链
Stack trace:
//从下到上
//5-0
//0就是最终报错源,调用到这一步
//真正的异常抛出行 是堆栈末尾的 thrown in ... RoutingMiddleware.php on line 76。
//也就是说,#0 代表调用链的最后一环,而 thrown in 那一行才是异常真正被 throw 的位置。
//也就是说0是最终报错的最后入口,最底下的位置才是报错点
#0 C:\Users\jc\Desktop\Slim-4.x\Slim\Routing\RouteRunner.php(62): Slim\Middleware\RoutingMiddleware->performRouting()
#1 C:\Users\jc\Desktop\Slim-4.x\Slim\MiddlewareDispatcher.php(73):
//启动前自动注入RouteRunner
//$this->tip:这是 MiddlewareDispatcher 类的一个属性,它存储了一个对象。这个对象在程序启动时被设置为 RouteRunner 的实例。
Slim\Routing\RouteRunner->handle()
#2 C:\Users\jc\Desktop\Slim-4.x\Slim\App.php(209): Slim\MiddlewareDispatcher->handle()
#3 C:\Users\jc\Desktop\Slim-4.x\Slim\App.php(193): Slim\App->handle()
//错误链
#4 C:\Users\jc\Desktop\Slim-4.x\public\index.php(15): Slim\App->run()
//最开始,入口,thrown in错误抛出,[Thu Mar 12 11:40:55 2026] [::1]:44730 [500]: GET /favicon.ico触发错误请求
//没有favicon.ico,in C:\Users\jc\Desktop\Slim-4.x\Slim\Middleware\RoutingMiddleware.php:76触发错误
#5 {main}
thrown in C:\Users\jc\Desktop\Slim-4.x\Slim\Middleware\RoutingMiddleware.php on line 76
[Thu Mar 12 11:40:55 2026] [::1]:44730 [500]: GET /favicon.ico - Uncaught Slim\Exception\HttpNotFoundException: Not found. in C:\Users\jc\Desktop\Slim-4.x\Slim\Middleware\RoutingMiddleware.php:76
[Thu Mar 12 11:40:55 2026] [::1]:44730 Closing
[Thu Mar 12 11:40:55 2026] [::1]:49795 [200]: GET /
php
public function performRouting(ServerRequestInterface $request): ServerRequestInterface
{//这里
//把路由解析器对象存入请求
$request = $request->withAttribute(RouteContext::ROUTE_PARSER, $this->routeParser);
//执行真正的路由解析:根据当前请求的路径、方法等信息,匹配定义好的路由,返回一个包含匹配结果的对象。
$routingResults = $this->resolveRoutingResultsFromRequest($request);
//从结果对象中取出状态码(例如 FOUND、NOT_FOUND、METHOD_NOT_ALLOWED),为下一步的流程分支提供依据。
$routeStatus = $routingResults->getRouteStatus();
//存档
//调用当前对象
$request = $request->withAttribute(RouteContext::ROUTING_RESULTS, $routingResults);
switch ($routeStatus) {
case RoutingResults::FOUND:
$routeArguments = $routingResults->getRouteArguments();
$routeIdentifier = $routingResults->getRouteIdentifier() ?? '';
$route = $this->routeResolver
->resolveRoute($routeIdentifier)
->prepare($routeArguments);
return $request->withAttribute(RouteContext::ROUTE, $route);
//出错
//当客户端尝试访问网站的某个页面或资源时,如果服务器无法找到并提供相应的信息,就会返回"Not Found"错误,即404状态码。这意味着所请求的URL可能已失效或者路径已更改,导致服务器无法正常响应。
//::访问常量
case RoutingResults::NOT_FOUND:
//抛出错误
throw new HttpNotFoundException($request);
case RoutingResults::METHOD_NOT_ALLOWED:
$exception = new HttpMethodNotAllowedException($request);
$exception->setAllowedMethods($routingResults->getAllowedMethods());
throw $exception;
default:
throw new RuntimeException('An unexpected error occurred while performing routing.');
}
}

00x1 xss
php
$app->get('/xss', function ($request, $response) {
$name = $request->getQueryParams()['name'] ?? 'guest';
$response->getBody()->write("Hello, " . $name);
return $response;
});

后台
php
[Thu Mar 12 15:57:19 2026] [::1]:20841 Accepted
[Thu Mar 12 15:57:19 2026] [::1]:20841 [200]: GET /xss?name=%3Cscript%3Ealert(%27%E8%BF%99%E6%98%AF%E4%B8%80%E4%B8%AA%E5%BC%B9%E7%AA%97%27);%3C/script%3E
[Thu Mar 12 15:57:19 2026] [::1]:20841 Closing
[Thu Mar 12 15:57:19 2026] [::1]:45458 Accepted
//与之前一样
[Thu Mar 12 15:57:19 2026] PHP Fatal error: Uncaught Slim\Exception\HttpNotFoundException: Not found. in C:\Users\jc\Desktop\Slim-4.x\Slim\Middleware\RoutingMiddleware.php:76
Stack trace:
#0 C:\Users\jc\Desktop\Slim-4.x\Slim\Routing\RouteRunner.php(62): Slim\Middleware\RoutingMiddleware->performRouting()
#1 C:\Users\jc\Desktop\Slim-4.x\Slim\MiddlewareDispatcher.php(73): Slim\Routing\RouteRunner->handle()
#2 C:\Users\jc\Desktop\Slim-4.x\Slim\App.php(209): Slim\MiddlewareDispatcher->handle()
#3 C:\Users\jc\Desktop\Slim-4.x\Slim\App.php(193): Slim\App->handle()
#4 C:\Users\jc\Desktop\Slim-4.x\public\index.php(20): Slim\App->run()
//浏览器行为
#5 {main}
thrown in C:\Users\jc\Desktop\Slim-4.x\Slim\Middleware\RoutingMiddleware.php on line 76
[Thu Mar 12 15:57:19 2026] [::1]:45458 [500]: GET /.well-known/appspecific/com.chrome.devtools.json - Uncaught Slim\Exception\HttpNotFoundException: Not found. in C:\Users\jc\Desktop\Slim-4.x\Slim\Middleware\RoutingMiddleware.php:76
Stack trace:
#0 C:\Users\jc\Desktop\Slim-4.x\Slim\Routing\RouteRunner.php(62): Slim\Middleware\RoutingMiddleware->performRouting()
#1 C:\Users\jc\Desktop\Slim-4.x\Slim\MiddlewareDispatcher.php(73): Slim\Routing\RouteRunner->handle()
#2 C:\Users\jc\Desktop\Slim-4.x\Slim\App.php(209): Slim\MiddlewareDispatcher->handle()
#3 C:\Users\jc\Desktop\Slim-4.x\Slim\App.php(193): Slim\App->handle()
#4 C:\Users\jc\Desktop\Slim-4.x\public\index.php(20): Slim\App->run()
//两次是因为浏览器插件拓展
#5 {main}
thrown in C:\Users\jc\Desktop\Slim-4.x\Slim\Middleware\RoutingMiddleware.php on line 76
[Thu Mar 12 15:57:19 2026] [::1]:45458 Closing
[Thu Mar 12 15:57:21 2026] [::1]:32562 Accepted
//收到的请求
[Thu Mar 12 15:57:21 2026] [::1]:32562 [200]: GET /xss?name=%3Cscript%3Ealert(%27%E8%BF%99%E6%98%AF%E4%B8%80%E4%B8%AA%E5%BC%B9%E7%AA%97%27);%3C/script%3E
[Thu Mar 12 15:57:21 2026] [::1]:32562 Closing
xss是浏览器的渲染导致的,这里没有防护直接渲染
00x2 this规则


this中的值是这个类的全部属性,如上
00x3 跳转规则

serverRequest来自

指向Slim\Psr7\Request(前面有配置,路由(实例化)调用类)
跳转后到了这里,Request是跳转类名,

00x4 debug name = request->getQueryParams()['name'] ?? 'guest';

php
public function getQueryParams(): array
{
//serverRequest成员变量,保存了PSR-7的request对象
//调用serverRequest对象的getQueryParams()方法
//地址指向
//获取xss后面的值以数组形式返回
$queryParams = $this->serverRequest->getQueryParams();
//检查是否为数组类型,是否为空
//PHP的empty()函数可判断标记是否为空字符串、0、"0"、NULL、FALSE或空数组
//正常返回
if (is_array($queryParams) && !empty($queryParams)) {
//返回
return $queryParams;
}
//
$parsedQueryParams = [];
//
parse_str($this->serverRequest->getUri()->getQuery(), $parsedQueryParams);
return $parsedQueryParams;
}
这是

- string $method
请求类型
存储的值:"GET"
含义:HTTP 请求方法,如 GET、POST、PUT 等。它决定了请求的意图。
- UriInterface $uri
uri实例化地址
存储的值:$lim\Psr7\Uri 对象
含义:封装了请求的 URI(统一资源标识符),包括 scheme(如 https)、host、port、path、query 字符串等。通过该对象可以获取 URL 的各个部分。
- string $requestTarget
请求目标查询部分
存储的值:null
含义:请求目标(通常指 URI 的路径和查询部分,如 /index.php?foo=bar)。PSR-7 允许显式设置请求目标,若为 null 则通常从 $uri 推导。
- array|null $queryParams
解析后存储数组
存储的值:null
含义:解析后的查询参数(来自 URL 中 ? 后面的部分)。如果尚未解析或设置,则为 null。一旦通过 withQueryParams() 设置或内部解析,就会变成一个关联数组(如 ['foo' => 'bar'])。
- array $cookies
存储的值:array[5](包含 5 个 cookie)
含义:从请求头 Cookie 中解析出的所有 cookie 键值对。数组的键是 cookie 名,值是 cookie 值。
- array $serverParams
存储的值:array[31](包含 31 个元素)
含义:服务器环境变量,通常对应 PHP 的 _SERVER 超全局数组。它包含了诸如请求方法、URI、客户端 IP、脚本路径等底层信息。PSR-7 要求将 _SERVER 数据传入请求对象。
- array $attributes
存储的值:array[4](包含 4 个属性)
含义:用户自定义的请求属性。这些属性不是 HTTP 协议的一部分,而是框架或中间件用来在请求生命周期中传递额外数据的"附件"。例如,可以存储路由参数、身份验证结果等。可以通过 withAttribute() 添加,getAttribute() 获取。
- array|null|object $parsedBody
存储的值:null
含义:解析后的请求体。对于 POST、PUT 等包含请求体的请求,可以解析为数组或对象。例如,JSON 请求体解析后可能是一个数组或 stdClass 对象。未解析或没有请求体时为 null。
- array $uploadedFiles
存储的值:array[0](空数组)
含义:上传的文件信息,符合 PSR-7 UploadedFileInterface 的对象数组。它对应于 $_FILES 的结构,但经过规范化处理。这里为空表示没有文件上传。
返回值

php
public function getQueryParams(): array
{
//获取get信息
if (is_array($this->queryParams)) {
return $this->queryParams;
}
if ($this->uri === null) {
return [];
}
//处理
//2
//3
//真正执行
//$this->uri->getQuery()的值是name=xxxxxxx
parse_str($this->uri->getQuery(), $this->queryParams);
//返回数组
return is_array($this->queryParams) ? $this->queryParams : [];
}


php
//2转化string
//:string返回类型
public function getQuery(): string
{
//返回xss后面的值
return $this->query;
}
php
public function getQueryParams(): array
{
//3
//url转义
$queryParams = $this->serverRequest->getQueryParams();
//是数组,不为空
if (is_array($queryParams) && !empty($queryParams)) {
return $queryParams;
}
$parsedQueryParams = [];
parse_str($this->serverRequest->getUri()->getQuery(), $parsedQueryParams);
return $parsedQueryParams;
}
由上可见,以上代码毫无过滤
00x5 debug response-\>getBody()-\>write("Hello, " . name);

php
public function getBody(): StreamInterface
{
return $this->response->getBody();
}


php
public function getBody(): StreamInterface
{
return $this->body;
}

常量 FSTAT_MODE_S_IFIFO
· 值:0010000(八进制)
· 含义:这是一个文件模式常量,来自 Unix/Linux 的文件类型定义。它代表 FIFO(命名管道) 类型,用于 fstat() 返回的模式中,以判断流是否为管道。这个常量通常在内部用来检测流类型,以便正确处理(例如管道不可 seek)。
属性
protected $stream
· 当前值:resource id='130' type='stream'
resource id='130',唯一流编号,stream资源类型声明
· 含义:底层的 PHP 流资源。这是该对象包装的核心,一个通过 fopen()、popen() 等创建的流资源。所有实际的 I/O 操作最终都通过这个资源进行。
protected ?array $meta
· 当前值:未显示(可能为 null)
· 含义:流的元数据,通过 stream_get_meta_data($this->stream) 获得。它包含流的信息,如模式、是否阻塞、超时设置、包装器类型等。延迟加载,首次调用 getMetadata() 时填充。
protected ?bool $readable = null
· 当前值:null
· 含义:流是否可读。通过分析流模式(如 r+, w+ 等)判断,但不会立即计算,而是在首次需要时通过检测模式设置。null 表示尚未检测。
protected ?bool $writable = null
· 当前值:null
· 含义:流是否可写。类似 readable,通过流模式判断,延迟检测。
protected ?bool $seekable = null
· 当前值:null
· 含义:流是否可定位(seek)。检测流是否支持 fseek(),通常基于元数据中的 seekable 字段。null 表示未检测。
protected ?int $size = null
· 当前值:null
· 含义:流的总大小(字节数)。如果能通过 fstat() 获取大小(如普通文件),则存储该值。对于网络流或管道,可能为 null。
protected ?bool $isPipe = null
· 当前值:null
· 含义:流是否为管道(pipe)。通过 fstat() 的模式判断(与常量 FSTAT_MODE_S_IFIFO 比较)。管道通常不可 seek,且大小未知。null 表示尚未判断。
protected bool $finished = false
· 当前值:false
· 含义:流是否已结束。对于可读流,当尝试读取时检测到 EOF,此标志可能设为 true。用于某些优化或判断是否还有数据可读。
protected ?StreamInterface $cache
· 当前值:null
· 含义:缓存流。某些实现中,当需要多次读取流内容(如获取完整内容后再次读取),可能会将内容缓存到另一个流中。这个属性存储缓存的流对象,通常为 null 表示未启用缓存。
这些属性有什么用?
这些属性共同管理流的行为,避免重复调用系统函数(如 fstat()、stream_get_meta_data()),并缓存状态以提升性能。例如:
· 第一次调用 isReadable() 时,检测流模式并设置 $readable,后续直接返回该值。
· 调用 getSize() 时,若已设置 $size 则直接返回,否则尝试通过 fstat() 获取并缓存。
所有属性均为 protected,意味着它们只能在类内部访问,但调试器可以显示它们,方便开发者查看流的当前状态。
php
public function write($string): int
{
$written = false;
if ($this->isWritable() && $this->stream) {
$written = fwrite($this->stream, $string);
}
if ($written !== false) {
$this->size = null;
return $written;
}
throw new RuntimeException('Could not write to stream.');
}

php
public function isWritable(): bool
{//检查流是否可写
if ($this->writable === null) {
$this->writable = false;
//底层的 PHP 流资源。这是该对象包装的核心,一个通过 fopen()、popen() 等创建的流资源。所有实际的 I/O 操作最终都通过这个资源进行。
//
if ($this->stream) {
$mode = $this->getMetadata('mode');
if (is_string($mode) && (strstr($mode, 'w') !== false || strstr($mode, '+') !== false)) {
$this->writable = true;
}
}
}
return $this->writable;
}


php
public function getMetadata($key = null)
{
if (!$this->stream) {
return null;
}
//执行这一行
$this->meta = stream_get_meta_data($this->stream);
if (!$key) {
return $this->meta;
}
//meta,流的元数据,通过 stream_get_meta_data($this->stream) 获得。它包含流的信息,如模式、是否阻塞、超时设置、包装器类型等。延迟加载,首次调用 getMetadata() 时填充。
return $this->meta[$key] ?? null;
}