-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 0841fea
Showing
9 changed files
with
344 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
.idea | ||
vendor | ||
.env | ||
.phpunit.result.cache | ||
composer.lock |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
{ | ||
"name": "topthink/think-dumper", | ||
"description": "Dumper extend for thinkphp", | ||
"license": "Apache-2.0", | ||
"authors": [ | ||
{ | ||
"name": "yunwuxin", | ||
"email": "[email protected]" | ||
} | ||
], | ||
"autoload": { | ||
"psr-4": { | ||
"think\\dumper\\": "src" | ||
}, | ||
"files": [ | ||
"src/helper.php" | ||
] | ||
}, | ||
"autoload-dev": { | ||
"psr-4": { | ||
"think\\tests\\dumper\\": "tests/" | ||
} | ||
}, | ||
"require": { | ||
"php": "^8.0", | ||
"topthink/framework": "^6.0|^8.0", | ||
"symfony/var-dumper": ">=6.3" | ||
}, | ||
"require-dev": { | ||
"phpunit/phpunit": "^11.4" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
<?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\ContextProvider\SourceContextProvider; | ||
use Symfony\Component\VarDumper\Dumper\ContextualizedDumper; | ||
use Symfony\Component\VarDumper\Dumper\HtmlDumper; | ||
use think\Request; | ||
|
||
class Dumper | ||
{ | ||
private static $handlers; | ||
|
||
public static function dump($var, string $label = null) | ||
{ | ||
$token = env('DUMPER_TOKEN'); | ||
|
||
$format = self::isCli() ? 'cli' : 'html'; | ||
|
||
if (!empty($token)) { | ||
$format = 'server'; | ||
} | ||
|
||
return self::getHandler($format)($var, $label); | ||
} | ||
|
||
private static function getHandler($format): callable | ||
{ | ||
if (empty(self::$handlers[$format])) { | ||
self::$handlers[$format] = self::createHandler($format); | ||
} | ||
|
||
return self::$handlers[$format]; | ||
} | ||
|
||
private static 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; | ||
case 'server' : | ||
$dumper = self::isCli() ? new CliDumper() : new HtmlDumper(); | ||
$dumper = new ServerDumper($dumper, self::getDefaultContextProviders()); | ||
break; | ||
default: | ||
throw new Exception('Invalid dump format.'); | ||
} | ||
|
||
if (!$dumper instanceof ServerDumper) { | ||
$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 static function getDefaultContextProviders(): array | ||
{ | ||
$contextProviders = []; | ||
|
||
if (self::isCli()) { | ||
$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']), | ||
]; | ||
} | ||
}; | ||
} else { | ||
$contextProviders['request'] = new class implements ContextProviderInterface { | ||
public function getContext(): ?array | ||
{ | ||
$request = app(Request::class); | ||
return [ | ||
'uri' => $request->getUri(), | ||
'method' => $request->getMethod(), | ||
'identifier' => spl_object_hash($request), | ||
]; | ||
} | ||
}; | ||
} | ||
|
||
$contextProviders['source'] = new SourceContextProvider(); | ||
|
||
return $contextProviders; | ||
} | ||
|
||
private static function isCli() | ||
{ | ||
return app()->runningInConsole(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
<?php | ||
|
||
use Symfony\Component\VarDumper\Caster\ScalarStub; | ||
use think\dumper\Dumper; | ||
|
||
function d(mixed ...$vars): mixed | ||
{ | ||
if (!$vars) { | ||
Dumper::dump(new ScalarStub('🐛')); | ||
|
||
return null; | ||
} | ||
|
||
if (array_key_exists(0, $vars) && 1 === count($vars)) { | ||
Dumper::dump($vars[0]); | ||
$k = 0; | ||
} else { | ||
foreach ($vars as $k => $v) { | ||
Dumper::dump($v, is_int($k) ? 1 + $k : $k); | ||
} | ||
} | ||
|
||
if (1 < count($vars)) { | ||
return $vars; | ||
} | ||
|
||
return $vars[$k]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
<?php | ||
|
||
namespace think\tests\dumper; | ||
|
||
use PHPUnit\Framework\TestCase; | ||
use think\dumper\Dumper; | ||
|
||
class DumperTest extends TestCase | ||
{ | ||
public function testDump() | ||
{ | ||
Dumper::dump('aa', 'cc'); | ||
d(['aa' => 'bbb', 'cc' => 'dd']); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
<?php | ||
|
||
$app = new \think\App(); | ||
$app->config->set([ | ||
'default' => 'file', | ||
'stores' => [ | ||
'file' => [ | ||
'type' => 'File', | ||
], | ||
], | ||
], 'cache'); | ||
|
||
$app->initialize(); |