提交
This commit is contained in:
35
vendor/topthink/think-dumper/README.md
vendored
Normal file
35
vendor/topthink/think-dumper/README.md
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
ThinkPHP 调试输出扩展
|
||||
--------------------
|
||||
支持本地和远程调试输出
|
||||
|
||||

|
||||
|
||||
## 安装
|
||||
```
|
||||
composer require topthink/think-dumper --dev
|
||||
```
|
||||
|
||||
环境要求 `PHP8.0+`
|
||||
|
||||
## 调试
|
||||
打开浏览器访问 https://developer.topthink.com/thinkphp/dumper
|
||||
|
||||
点击左侧菜单 生成令牌并复制令牌内容
|
||||
|
||||
修改`.env` 环境变量文件,增加
|
||||
|
||||
```
|
||||
DUMPER_TOKEN = 令牌值
|
||||
```
|
||||
|
||||
然后 在代码中使用助手函数`d`输出变量调试
|
||||
```
|
||||
dump($var...)
|
||||
```
|
||||
|
||||
调试内容会在
|
||||
https://developer.topthink.com/thinkphp/dumper 中显示
|
||||
|
||||
如果没有设置令牌或远程打印页面没有打开,则`dump`函数只会在本地输出调试内容
|
||||
|
||||
> 请放心,变量内容不会经过服务器,只会保存在本地浏览器,并且可以随时清除。
|
||||
32
vendor/topthink/think-dumper/composer.json
vendored
Normal file
32
vendor/topthink/think-dumper/composer.json
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"name": "topthink/think-dumper",
|
||||
"description": "Dumper extend for thinkphp",
|
||||
"license": "Apache-2.0",
|
||||
"authors": [
|
||||
{
|
||||
"name": "yunwuxin",
|
||||
"email": "448901948@qq.com"
|
||||
}
|
||||
],
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"think\\dumper\\": "src"
|
||||
},
|
||||
"files": [
|
||||
"src/helper.php"
|
||||
]
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"think\\tests\\dumper\\": "tests/"
|
||||
}
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.0.2",
|
||||
"topthink/framework": "^6.0|^8.0",
|
||||
"symfony/var-dumper": ">=6.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^11.4"
|
||||
}
|
||||
}
|
||||
25
vendor/topthink/think-dumper/phpunit.xml.dist
vendored
Normal file
25
vendor/topthink/think-dumper/phpunit.xml.dist
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
beStrictAboutTestsThatDoNotTestAnything="false"
|
||||
bootstrap="tests/bootstrap.php"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnError="false"
|
||||
stopOnFailure="false"
|
||||
verbose="true"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="ThinkPHP Test Suite">
|
||||
<directory suffix="Test.php">./tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<filter>
|
||||
<whitelist processUncoveredFilesFromWhitelist="true">
|
||||
<directory suffix=".php">./src</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
||||
82
vendor/topthink/think-dumper/src/Connection.php
vendored
Normal file
82
vendor/topthink/think-dumper/src/Connection.php
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
namespace think\dumper;
|
||||
|
||||
use Symfony\Component\VarDumper\Cloner\Data;
|
||||
|
||||
class Connection
|
||||
{
|
||||
private string $host;
|
||||
private string $token;
|
||||
private array $contextProviders;
|
||||
|
||||
private $handle;
|
||||
|
||||
public function __construct(array $contextProviders = [])
|
||||
{
|
||||
$this->host = env('DUMPER_HOST', 'https://developer.topthink.com');
|
||||
$this->token = env('DUMPER_TOKEN');
|
||||
$this->contextProviders = $contextProviders;
|
||||
}
|
||||
|
||||
public function write(Data $data): bool
|
||||
{
|
||||
if (!$this->handle = $this->handle ?: $this->createHandle()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$context = ['timestamp' => microtime(true)];
|
||||
foreach ($this->contextProviders as $name => $provider) {
|
||||
$context[$name] = $provider->getContext();
|
||||
}
|
||||
$context = array_filter($context);
|
||||
$encodedPayload = base64_encode(serialize([$data, $context])) . "\n";
|
||||
|
||||
set_error_handler(fn() => true);
|
||||
try {
|
||||
return $this->sendPayload($encodedPayload);
|
||||
} finally {
|
||||
restore_error_handler();
|
||||
}
|
||||
}
|
||||
|
||||
private function sendPayload($payload)
|
||||
{
|
||||
curl_setopt($this->handle, CURLOPT_POSTFIELDS, $payload);
|
||||
$res = curl_exec($this->handle);
|
||||
if ($res === false) {
|
||||
curl_close($this->handle);
|
||||
$this->handle = null;
|
||||
return false;
|
||||
}
|
||||
$code = curl_getinfo($this->handle, CURLINFO_HTTP_CODE);
|
||||
return $code == 204;
|
||||
}
|
||||
|
||||
private function createHandle()
|
||||
{
|
||||
set_error_handler(fn() => true);
|
||||
try {
|
||||
$handle = curl_init("{$this->host}/api/thinkphp/dumper/server");
|
||||
curl_setopt($handle, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($handle, CURLOPT_POST, true);
|
||||
curl_setopt($handle, CURLOPT_HTTPHEADER, [
|
||||
"Accept: application/json",
|
||||
"Authorization: Bearer {$this->token}",
|
||||
]);
|
||||
curl_setopt($handle, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($handle, CURLOPT_TIMEOUT, 3);
|
||||
curl_setopt($handle, CURLOPT_CONNECTTIMEOUT, 1);
|
||||
return $handle;
|
||||
} finally {
|
||||
restore_error_handler();
|
||||
}
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
if ($this->handle) {
|
||||
curl_close($this->handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
103
vendor/topthink/think-dumper/src/Dumper.php
vendored
Normal file
103
vendor/topthink/think-dumper/src/Dumper.php
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
namespace think\dumper;
|
||||
|
||||
use Exception;
|
||||
use Symfony\Component\VarDumper\Caster\ReflectionCaster;
|
||||
use Symfony\Component\VarDumper\Cloner\VarCloner;
|
||||
use Symfony\Component\VarDumper\Dumper\CliDumper;
|
||||
use Symfony\Component\VarDumper\Dumper\ContextProvider\ContextProviderInterface;
|
||||
use Symfony\Component\VarDumper\Dumper\ContextualizedDumper;
|
||||
use Symfony\Component\VarDumper\Dumper\HtmlDumper;
|
||||
use think\App;
|
||||
use think\Env;
|
||||
use think\Request;
|
||||
|
||||
class Dumper
|
||||
{
|
||||
public function __construct(protected App $app, protected Env $env)
|
||||
{
|
||||
}
|
||||
|
||||
public function dump($var, ?string $label = null)
|
||||
{
|
||||
$format = $this->app->runningInConsole() ? 'cli' : 'html';
|
||||
|
||||
$handler = $this->createHandler($format);
|
||||
|
||||
return $handler($var, $label);
|
||||
}
|
||||
|
||||
private function createHandler($format)
|
||||
{
|
||||
$cloner = new VarCloner();
|
||||
$cloner->addCasters(ReflectionCaster::UNSET_CLOSURE_FILE_INFO);
|
||||
|
||||
switch ($format) {
|
||||
case 'html' :
|
||||
$dumper = new HtmlDumper();
|
||||
break;
|
||||
case 'cli':
|
||||
$dumper = new CliDumper();
|
||||
break;
|
||||
default:
|
||||
throw new Exception('Invalid dump format.');
|
||||
}
|
||||
|
||||
if ($this->env->has('DUMPER_TOKEN')) {
|
||||
$dumper = new ServerDumper($dumper, $this->getDefaultContextProviders($format));
|
||||
} else {
|
||||
$dumper = new ContextualizedDumper($dumper, [new SourceContextProvider()]);
|
||||
}
|
||||
|
||||
return function ($var, ?string $label = null) use ($cloner, $dumper) {
|
||||
$var = $cloner->cloneVar($var);
|
||||
|
||||
if (null !== $label) {
|
||||
$var = $var->withContext(['label' => $label]);
|
||||
}
|
||||
|
||||
$dumper->dump($var);
|
||||
};
|
||||
}
|
||||
|
||||
private function getDefaultContextProviders($format): array
|
||||
{
|
||||
$contextProviders = [];
|
||||
|
||||
switch ($format) {
|
||||
case 'html' :
|
||||
$contextProviders['request'] = new class($this->app->request) implements ContextProviderInterface {
|
||||
public function __construct(protected Request $request)
|
||||
{
|
||||
}
|
||||
|
||||
public function getContext(): ?array
|
||||
{
|
||||
$request = $this->request;
|
||||
return [
|
||||
'uri' => $request->url(),
|
||||
'method' => $request->method(),
|
||||
'identifier' => spl_object_hash($request),
|
||||
];
|
||||
}
|
||||
};
|
||||
break;
|
||||
case 'cli':
|
||||
$contextProviders['cli'] = new class implements ContextProviderInterface {
|
||||
public function getContext(): ?array
|
||||
{
|
||||
return [
|
||||
'command_line' => $commandLine = implode(' ', $_SERVER['argv'] ?? []),
|
||||
'identifier' => hash('crc32b', $commandLine . $_SERVER['REQUEST_TIME_FLOAT']),
|
||||
];
|
||||
}
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
$contextProviders['source'] = new SourceContextProvider();
|
||||
|
||||
return $contextProviders;
|
||||
}
|
||||
}
|
||||
31
vendor/topthink/think-dumper/src/ServerDumper.php
vendored
Normal file
31
vendor/topthink/think-dumper/src/ServerDumper.php
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace think\dumper;
|
||||
|
||||
use Symfony\Component\VarDumper\Cloner\Data;
|
||||
use Symfony\Component\VarDumper\Dumper\DataDumperInterface;
|
||||
|
||||
class ServerDumper implements DataDumperInterface
|
||||
{
|
||||
protected Connection $connection;
|
||||
|
||||
protected ?DataDumperInterface $wrappedDumper;
|
||||
|
||||
public function __construct(?DataDumperInterface $wrappedDumper = null, array $contextProviders = [])
|
||||
{
|
||||
$this->connection = new Connection($contextProviders);
|
||||
$this->wrappedDumper = $wrappedDumper;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function dump(Data $data)
|
||||
{
|
||||
if (!$this->connection->write($data) && $this->wrappedDumper) {
|
||||
return $this->wrappedDumper->dump($data);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
58
vendor/topthink/think-dumper/src/SourceContextProvider.php
vendored
Normal file
58
vendor/topthink/think-dumper/src/SourceContextProvider.php
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace think\dumper;
|
||||
|
||||
use Symfony\Component\VarDumper\Dumper\ContextProvider\ContextProviderInterface;
|
||||
use Symfony\Component\VarDumper\VarDumper;
|
||||
|
||||
final class SourceContextProvider implements ContextProviderInterface
|
||||
{
|
||||
|
||||
public function __construct(
|
||||
private ?string $charset = null,
|
||||
private ?string $projectDir = null,
|
||||
private int $limit = 9,
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
public function getContext(): ?array
|
||||
{
|
||||
$trace = debug_backtrace(\DEBUG_BACKTRACE_PROVIDE_OBJECT | \DEBUG_BACKTRACE_IGNORE_ARGS, $this->limit);
|
||||
$trace = array_reverse($trace);
|
||||
|
||||
$i = 0;
|
||||
$file = '-';
|
||||
$line = 0;
|
||||
|
||||
do {
|
||||
$file = $trace[$i]['file'] ?? $file;
|
||||
$line = $trace[$i]['line'] ?? $line;
|
||||
|
||||
$function = $trace[$i]['function'] ?? null;
|
||||
$class = $trace[$i]['class'] ?? null;
|
||||
if ($function == 'dump' && ($class == null || $class == Dumper::class || $class == VarDumper::class)) {
|
||||
break;
|
||||
}
|
||||
} while (++$i < $this->limit);
|
||||
|
||||
$name = '-' === $file || 'Standard input code' === $file ? 'Standard input code' : false;
|
||||
|
||||
if (false === $name) {
|
||||
$name = str_replace('\\', '/', $file);
|
||||
$name = substr($name, strrpos($name, '/') + 1);
|
||||
}
|
||||
|
||||
$context = ['name' => $name, 'file' => $file, 'line' => $line];
|
||||
|
||||
if (null !== $this->projectDir) {
|
||||
$context['project_dir'] = $this->projectDir;
|
||||
if (str_starts_with($file, $this->projectDir)) {
|
||||
$context['file_relative'] = ltrim(substr($file, \strlen($this->projectDir)), \DIRECTORY_SEPARATOR);
|
||||
}
|
||||
}
|
||||
|
||||
return $context;
|
||||
}
|
||||
|
||||
}
|
||||
8
vendor/topthink/think-dumper/src/helper.php
vendored
Normal file
8
vendor/topthink/think-dumper/src/helper.php
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
use Symfony\Component\VarDumper\VarDumper;
|
||||
use think\dumper\Dumper;
|
||||
|
||||
VarDumper::setHandler(function ($var, ?string $label = null) {
|
||||
app(Dumper::class)->dump($var, $label);
|
||||
});
|
||||
14
vendor/topthink/think-dumper/tests/DumperTest.php
vendored
Normal file
14
vendor/topthink/think-dumper/tests/DumperTest.php
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace think\tests\dumper;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class DumperTest extends TestCase
|
||||
{
|
||||
public function testDump()
|
||||
{
|
||||
dump(['aa' => 'bbb', 'cc' => 'dd']);
|
||||
dump($this);
|
||||
}
|
||||
}
|
||||
13
vendor/topthink/think-dumper/tests/bootstrap.php
vendored
Normal file
13
vendor/topthink/think-dumper/tests/bootstrap.php
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
$app = new \think\App();
|
||||
$app->config->set([
|
||||
'default' => 'file',
|
||||
'stores' => [
|
||||
'file' => [
|
||||
'type' => 'File',
|
||||
],
|
||||
],
|
||||
], 'cache');
|
||||
|
||||
$app->initialize();
|
||||
Reference in New Issue
Block a user