diff --git a/CHANGELOG.md b/CHANGELOG.md index e47de40c..bb63a985 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,13 @@ CHANGELOG ========= +7.3 +--- + + * Add casters for `Dba\Connection`, `SQLite3Result`, `OpenSSLAsymmetricKey` and `OpenSSLCertificateSigningRequest` + * Deprecate `ResourceCaster::castCurl()`, `ResourceCaster::castGd()` and `ResourceCaster::castOpensslX509()` + * Mark all casters as `@internal` + 7.2 --- diff --git a/Caster/AddressInfoCaster.php b/Caster/AddressInfoCaster.php new file mode 100644 index 00000000..f341c688 --- /dev/null +++ b/Caster/AddressInfoCaster.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Caster; + +use Symfony\Component\VarDumper\Cloner\Stub; + +/** + * @author Nicolas Grekas
+ *
+ * @internal since Symfony 7.3
+ */
+final class AddressInfoCaster
+{
+ private const MAPS = [
+ 'ai_flags' => [
+ 1 => 'AI_PASSIVE',
+ 2 => 'AI_CANONNAME',
+ 4 => 'AI_NUMERICHOST',
+ 8 => 'AI_V4MAPPED',
+ 16 => 'AI_ALL',
+ 32 => 'AI_ADDRCONFIG',
+ 64 => 'AI_IDN',
+ 128 => 'AI_CANONIDN',
+ 1024 => 'AI_NUMERICSERV',
+ ],
+ 'ai_family' => [
+ 1 => 'AF_UNIX',
+ 2 => 'AF_INET',
+ 10 => 'AF_INET6',
+ 44 => 'AF_DIVERT',
+ ],
+ 'ai_socktype' => [
+ 1 => 'SOCK_STREAM',
+ 2 => 'SOCK_DGRAM',
+ 3 => 'SOCK_RAW',
+ 4 => 'SOCK_RDM',
+ 5 => 'SOCK_SEQPACKET',
+ ],
+ 'ai_protocol' => [
+ 1 => 'SOL_SOCKET',
+ 6 => 'SOL_TCP',
+ 17 => 'SOL_UDP',
+ 136 => 'SOL_UDPLITE',
+ ],
+ ];
+
+ public static function castAddressInfo(\AddressInfo $h, array $a, Stub $stub, bool $isNested): array
+ {
+ static $resolvedMaps;
+
+ if (!$resolvedMaps) {
+ foreach (self::MAPS as $k => $map) {
+ foreach ($map as $v => $name) {
+ if (\defined($name)) {
+ $resolvedMaps[$k][\constant($name)] = $name;
+ } elseif (!isset($resolvedMaps[$k][$v])) {
+ $resolvedMaps[$k][$v] = $name;
+ }
+ }
+ }
+ }
+
+ foreach (socket_addrinfo_explain($h) as $k => $v) {
+ $a[Caster::PREFIX_VIRTUAL.$k] = match (true) {
+ 'ai_flags' === $k => ConstStub::fromBitfield($v, $resolvedMaps[$k]),
+ isset($resolvedMaps[$k][$v]) => new ConstStub($resolvedMaps[$k][$v], $v),
+ default => $v,
+ };
+ }
+
+ return $a;
+ }
+}
diff --git a/Caster/AmqpCaster.php b/Caster/AmqpCaster.php
index 68b1a65f..ff56288b 100644
--- a/Caster/AmqpCaster.php
+++ b/Caster/AmqpCaster.php
@@ -19,6 +19,8 @@
* @author Grégoire Pineau
+ *
+ * @internal
+ */
+final class CurlCaster
+{
+ public static function castCurl(\CurlHandle $h, array $a, Stub $stub, bool $isNested): array
+ {
+ foreach (curl_getinfo($h) as $key => $val) {
+ $a[Caster::PREFIX_VIRTUAL.$key] = $val;
+ }
+
+ return $a;
+ }
+}
diff --git a/Caster/DOMCaster.php b/Caster/DOMCaster.php
index fa58ec4c..e16b33d4 100644
--- a/Caster/DOMCaster.php
+++ b/Caster/DOMCaster.php
@@ -19,6 +19,8 @@
* @author Nicolas Grekas
*
* @final
+ *
+ * @internal since Symfony 7.3
*/
class DOMCaster
{
diff --git a/Caster/DateCaster.php b/Caster/DateCaster.php
index f6c35f2b..453d0cb9 100644
--- a/Caster/DateCaster.php
+++ b/Caster/DateCaster.php
@@ -19,6 +19,8 @@
* @author Dany Maillard
*
* @final
+ *
+ * @internal since Symfony 7.3
*/
class DoctrineCaster
{
diff --git a/Caster/ExceptionCaster.php b/Caster/ExceptionCaster.php
index fb67a704..4473bdc8 100644
--- a/Caster/ExceptionCaster.php
+++ b/Caster/ExceptionCaster.php
@@ -22,6 +22,8 @@
* @author Nicolas Grekas
*
* @final
+ *
+ * @internal since Symfony 7.3
*/
class ExceptionCaster
{
diff --git a/Caster/GdCaster.php b/Caster/GdCaster.php
new file mode 100644
index 00000000..db87653e
--- /dev/null
+++ b/Caster/GdCaster.php
@@ -0,0 +1,30 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\VarDumper\Caster;
+
+use Symfony\Component\VarDumper\Cloner\Stub;
+
+/**
+ * @author Nicolas Grekas
+ *
+ * @internal
+ */
+final class GdCaster
+{
+ public static function castGd(\GdImage $gd, array $a, Stub $stub, bool $isNested): array
+ {
+ $a[Caster::PREFIX_VIRTUAL.'size'] = imagesx($gd).'x'.imagesy($gd);
+ $a[Caster::PREFIX_VIRTUAL.'trueColor'] = imageistruecolor($gd);
+
+ return $a;
+ }
+}
diff --git a/Caster/GmpCaster.php b/Caster/GmpCaster.php
index b018cc7f..325d2e90 100644
--- a/Caster/GmpCaster.php
+++ b/Caster/GmpCaster.php
@@ -20,6 +20,8 @@
* @author Nicolas Grekas
*
* @final
+ *
+ * @internal since Symfony 7.3
*/
class GmpCaster
{
diff --git a/Caster/ImagineCaster.php b/Caster/ImagineCaster.php
index d1289da3..0fb2a903 100644
--- a/Caster/ImagineCaster.php
+++ b/Caster/ImagineCaster.php
@@ -16,6 +16,8 @@
/**
* @author Grégoire Pineau
+ * @author Alexandre Daubois
*
* @final
+ *
+ * @internal since Symfony 7.3
*/
class PdoCaster
{
diff --git a/Caster/PgSqlCaster.php b/Caster/PgSqlCaster.php
index 3e759f69..54a19064 100644
--- a/Caster/PgSqlCaster.php
+++ b/Caster/PgSqlCaster.php
@@ -19,6 +19,8 @@
* @author Nicolas Grekas
*
* @final
+ *
+ * @internal since Symfony 7.3
*/
class PgSqlCaster
{
diff --git a/Caster/ProxyManagerCaster.php b/Caster/ProxyManagerCaster.php
index 736a6e75..0d954f48 100644
--- a/Caster/ProxyManagerCaster.php
+++ b/Caster/ProxyManagerCaster.php
@@ -18,6 +18,8 @@
* @author Nicolas Grekas
*
* @final
+ *
+ * @internal since Symfony 7.3
*/
class ProxyManagerCaster
{
diff --git a/Caster/RdKafkaCaster.php b/Caster/RdKafkaCaster.php
index 5445b2d4..bfadef2f 100644
--- a/Caster/RdKafkaCaster.php
+++ b/Caster/RdKafkaCaster.php
@@ -28,6 +28,8 @@
* Casts RdKafka related classes to array representation.
*
* @author Romain Neutron
*
* @final
+ *
+ * @internal since Symfony 7.3
*/
class RedisCaster
{
diff --git a/Caster/ReflectionCaster.php b/Caster/ReflectionCaster.php
index e7bd9a15..e7310f40 100644
--- a/Caster/ReflectionCaster.php
+++ b/Caster/ReflectionCaster.php
@@ -19,6 +19,8 @@
* @author Nicolas Grekas
*
* @final
+ *
+ * @internal since Symfony 7.3
*/
class ReflectionCaster
{
diff --git a/Caster/ResourceCaster.php b/Caster/ResourceCaster.php
index f775f81c..5613c553 100644
--- a/Caster/ResourceCaster.php
+++ b/Caster/ResourceCaster.php
@@ -19,16 +19,31 @@
* @author Nicolas Grekas
*
* @final
+ *
+ * @internal since Symfony 7.3
*/
class ResourceCaster
{
+ /**
+ * @deprecated since Symfony 7.3
+ */
public static function castCurl(\CurlHandle $h, array $a, Stub $stub, bool $isNested): array
{
- return curl_getinfo($h);
+ trigger_deprecation('symfony/var-dumper', '7.3', 'The "%s()" method is deprecated without replacement.', __METHOD__, CurlCaster::class);
+
+ return CurlCaster::castCurl($h, $a, $stub, $isNested);
}
- public static function castDba($dba, array $a, Stub $stub, bool $isNested): array
+ /**
+ * @param resource|\Dba\Connection $dba
+ */
+ public static function castDba(mixed $dba, array $a, Stub $stub, bool $isNested): array
{
+ if (\PHP_VERSION_ID < 80402 && !\is_resource($dba)) {
+ // @see https://github.com/php/php-src/issues/16990
+ return $a;
+ }
+
$list = dba_list();
$a['file'] = $list[(int) $dba];
@@ -55,37 +70,23 @@ public static function castStreamContext($stream, array $a, Stub $stub, bool $is
return @stream_context_get_params($stream) ?: $a;
}
- public static function castGd($gd, array $a, Stub $stub, bool $isNested): array
+ /**
+ * @deprecated since Symfony 7.3
+ */
+ public static function castGd(\GdImage $gd, array $a, Stub $stub, bool $isNested): array
{
- $a['size'] = imagesx($gd).'x'.imagesy($gd);
- $a['trueColor'] = imageistruecolor($gd);
+ trigger_deprecation('symfony/var-dumper', '7.3', 'The "%s()" method is deprecated without replacement.', __METHOD__, GdCaster::class);
- return $a;
+ return GdCaster::castGd($gd, $a, $stub, $isNested);
}
- public static function castOpensslX509($h, array $a, Stub $stub, bool $isNested): array
+ /**
+ * @deprecated since Symfony 7.3
+ */
+ public static function castOpensslX509(\OpenSSLCertificate $h, array $a, Stub $stub, bool $isNested): array
{
- $stub->cut = -1;
- $info = openssl_x509_parse($h, false);
-
- $pin = openssl_pkey_get_public($h);
- $pin = openssl_pkey_get_details($pin)['key'];
- $pin = \array_slice(explode("\n", $pin), 1, -2);
- $pin = base64_decode(implode('', $pin));
- $pin = base64_encode(hash('sha256', $pin, true));
-
- $a += [
- 'subject' => new EnumStub(array_intersect_key($info['subject'], ['organizationName' => true, 'commonName' => true])),
- 'issuer' => new EnumStub(array_intersect_key($info['issuer'], ['organizationName' => true, 'commonName' => true])),
- 'expiry' => new ConstStub(date(\DateTimeInterface::ISO8601, $info['validTo_time_t']), $info['validTo_time_t']),
- 'fingerprint' => new EnumStub([
- 'md5' => new ConstStub(wordwrap(strtoupper(openssl_x509_fingerprint($h, 'md5')), 2, ':', true)),
- 'sha1' => new ConstStub(wordwrap(strtoupper(openssl_x509_fingerprint($h, 'sha1')), 2, ':', true)),
- 'sha256' => new ConstStub(wordwrap(strtoupper(openssl_x509_fingerprint($h, 'sha256')), 2, ':', true)),
- 'pin-sha256' => new ConstStub($pin),
- ]),
- ];
+ trigger_deprecation('symfony/var-dumper', '7.3', 'The "%s()" method is deprecated without replacement.', __METHOD__, OpenSSLCaster::class);
- return $a;
+ return OpenSSLCaster::castOpensslX509($h, $a, $stub, $isNested);
}
}
diff --git a/Caster/SocketCaster.php b/Caster/SocketCaster.php
new file mode 100644
index 00000000..6b95cd10
--- /dev/null
+++ b/Caster/SocketCaster.php
@@ -0,0 +1,64 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\VarDumper\Caster;
+
+use Symfony\Component\VarDumper\Cloner\Stub;
+
+/**
+ * @author Nicolas Grekas
+ * @author Alexandre Daubois
*
* @final
+ *
+ * @internal since Symfony 7.3
*/
class SplCaster
{
diff --git a/Caster/SqliteCaster.php b/Caster/SqliteCaster.php
new file mode 100644
index 00000000..25d47ac4
--- /dev/null
+++ b/Caster/SqliteCaster.php
@@ -0,0 +1,32 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\VarDumper\Caster;
+
+use Symfony\Component\VarDumper\Cloner\Stub;
+
+/**
+ * @author Alexandre Daubois
*
* @final
+ *
+ * @internal since Symfony 7.3
*/
class StubCaster
{
diff --git a/Caster/SymfonyCaster.php b/Caster/SymfonyCaster.php
index d8422e05..42dc901a 100644
--- a/Caster/SymfonyCaster.php
+++ b/Caster/SymfonyCaster.php
@@ -19,6 +19,8 @@
/**
* @final
+ *
+ * @internal since Symfony 7.3
*/
class SymfonyCaster
{
diff --git a/Caster/UuidCaster.php b/Caster/UuidCaster.php
index b1027745..732ad7cc 100644
--- a/Caster/UuidCaster.php
+++ b/Caster/UuidCaster.php
@@ -16,6 +16,8 @@
/**
* @author Grégoire Pineau
*
* @final
+ *
+ * @internal since Symfony 7.3
*/
class XmlResourceCaster
{
diff --git a/Cloner/AbstractCloner.php b/Cloner/AbstractCloner.php
index 3cd46942..9038d2c0 100644
--- a/Cloner/AbstractCloner.php
+++ b/Cloner/AbstractCloner.php
@@ -24,6 +24,9 @@ abstract class AbstractCloner implements ClonerInterface
public static array $defaultCasters = [
'__PHP_Incomplete_Class' => ['Symfony\Component\VarDumper\Caster\Caster', 'castPhpIncompleteClass'],
+ 'AddressInfo' => ['Symfony\Component\VarDumper\Caster\AddressInfoCaster', 'castAddressInfo'],
+ 'Socket' => ['Symfony\Component\VarDumper\Caster\SocketCaster', 'castSocket'],
+
'Symfony\Component\VarDumper\Caster\CutStub' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'castStub'],
'Symfony\Component\VarDumper\Caster\CutArrayStub' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'castCutArray'],
'Symfony\Component\VarDumper\Caster\ConstStub' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'castStub'],
@@ -174,29 +177,33 @@ abstract class AbstractCloner implements ClonerInterface
'mysqli_driver' => ['Symfony\Component\VarDumper\Caster\MysqliCaster', 'castMysqliDriver'],
- 'CurlHandle' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castCurl'],
+ 'CurlHandle' => ['Symfony\Component\VarDumper\Caster\CurlCaster', 'castCurl'],
+ 'Dba\Connection' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castDba'],
':dba' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castDba'],
':dba persistent' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castDba'],
'GdImage' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castGd'],
- ':gd' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castGd'],
- ':pgsql large object' => ['Symfony\Component\VarDumper\Caster\PgSqlCaster', 'castLargeObject'],
- ':pgsql link' => ['Symfony\Component\VarDumper\Caster\PgSqlCaster', 'castLink'],
- ':pgsql link persistent' => ['Symfony\Component\VarDumper\Caster\PgSqlCaster', 'castLink'],
- ':pgsql result' => ['Symfony\Component\VarDumper\Caster\PgSqlCaster', 'castResult'],
+ 'SQLite3Result' => ['Symfony\Component\VarDumper\Caster\SqliteCaster', 'castSqlite3Result'],
+
+ 'PgSql\Lob' => ['Symfony\Component\VarDumper\Caster\PgSqlCaster', 'castLargeObject'],
+ 'PgSql\Connection' => ['Symfony\Component\VarDumper\Caster\PgSqlCaster', 'castLink'],
+ 'PgSql\Result' => ['Symfony\Component\VarDumper\Caster\PgSqlCaster', 'castResult'],
+
':process' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castProcess'],
':stream' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castStream'],
- 'OpenSSLCertificate' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castOpensslX509'],
- ':OpenSSL X.509' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castOpensslX509'],
+ 'OpenSSLAsymmetricKey' => ['Symfony\Component\VarDumper\Caster\OpenSSLCaster', 'castOpensslAsymmetricKey'],
+ 'OpenSSLCertificateSigningRequest' => ['Symfony\Component\VarDumper\Caster\OpenSSLCaster', 'castOpensslCsr'],
+ 'OpenSSLCertificate' => ['Symfony\Component\VarDumper\Caster\OpenSSLCaster', 'castOpensslX509'],
':persistent stream' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castStream'],
':stream-context' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castStreamContext'],
'XmlParser' => ['Symfony\Component\VarDumper\Caster\XmlResourceCaster', 'castXml'],
- ':xml' => ['Symfony\Component\VarDumper\Caster\XmlResourceCaster', 'castXml'],
+
+ 'Socket' => ['Symfony\Component\VarDumper\Caster\SocketCaster', 'castSocket'],
'RdKafka' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castRdKafka'],
'RdKafka\Conf' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castConf'],
diff --git a/Cloner/VarCloner.php b/Cloner/VarCloner.php
index 170c8b40..6a7ec282 100644
--- a/Cloner/VarCloner.php
+++ b/Cloner/VarCloner.php
@@ -28,14 +28,12 @@ protected function doClone(mixed $var): array
$objRefs = []; // Map of original object handles to their stub object counterpart
$objects = []; // Keep a ref to objects to ensure their handle cannot be reused while cloning
$resRefs = []; // Map of original resource handles to their stub object counterpart
- $values = []; // Map of stub objects' ids to original values
$maxItems = $this->maxItems;
$maxString = $this->maxString;
$minDepth = $this->minDepth;
$currentDepth = 0; // Current tree depth
$currentDepthFinalIndex = 0; // Final $queue index for current tree depth
$minimumDepthReached = 0 === $minDepth; // Becomes true when minimum tree depth has been reached
- $cookie = (object) []; // Unique object used to detect hard references
$a = null; // Array cast for nested structures
$stub = null; // Stub capturing the main properties of an original item value
// or null if the original value is used directly
@@ -53,7 +51,7 @@ protected function doClone(mixed $var): array
}
}
- $refs = $vals = $queue[$i];
+ $vals = $queue[$i];
foreach ($vals as $k => $v) {
// $v is the original value or a stub object in case of hard references
@@ -215,10 +213,6 @@ protected function doClone(mixed $var): array
$queue[$i] = $vals;
}
- foreach ($values as $h => $v) {
- $hardRefs[$h] = $v;
- }
-
return $queue;
}
}
diff --git a/Dumper/HtmlDumper.php b/Dumper/HtmlDumper.php
index 1f24852c..835d6d94 100644
--- a/Dumper/HtmlDumper.php
+++ b/Dumper/HtmlDumper.php
@@ -867,20 +867,20 @@ protected function style(string $style, string $value, array $attr = []): string
}
$label = esc(substr($value, -$attr['ellipsis']));
$dumpTitle = $v."\n".$dumpTitle;
- $v = sprintf('%s', $ellipsisClass, substr($v, 0, -\strlen($label)));
+ $v = \sprintf('%s', $ellipsisClass, substr($v, 0, -\strlen($label)));
if (!empty($attr['ellipsis-tail'])) {
$tail = \strlen(esc(substr($value, -$attr['ellipsis'], $attr['ellipsis-tail'])));
- $v .= sprintf('%s%s', $ellipsisClass, substr($label, 0, $tail), substr($label, $tail));
+ $v .= \sprintf('%s%s', $ellipsisClass, substr($label, 0, $tail), substr($label, $tail));
} else {
- $v .= sprintf('%s', $label);
+ $v .= \sprintf('%s', $label);
}
}
$map = static::$controlCharsMap;
- $v = sprintf(
+ $v = \sprintf(
'%s',
- 1 === count($dumpClasses) ? '' : '"',
+ 1 === \count($dumpClasses) ? '' : '"',
implode(' ', $dumpClasses),
$dumpTitle ? ' title="'.$dumpTitle.'"' : '',
preg_replace_callback(static::$controlCharsRx, function ($c) use ($map) {
diff --git a/Resources/functions/dump.php b/Resources/functions/dump.php
index e6ade0df..c9915514 100644
--- a/Resources/functions/dump.php
+++ b/Resources/functions/dump.php
@@ -45,7 +45,7 @@ function dump(mixed ...$vars): mixed
if (!function_exists('dd')) {
function dd(mixed ...$vars): never
{
- if (!\in_array(\PHP_SAPI, ['cli', 'phpdbg', 'embed'], true) && !headers_sent()) {
+ if (!in_array(\PHP_SAPI, ['cli', 'phpdbg', 'embed'], true) && !headers_sent()) {
header('HTTP/1.1 500 Internal Server Error');
}
diff --git a/Tests/Caster/AddressInfoCasterTest.php b/Tests/Caster/AddressInfoCasterTest.php
new file mode 100644
index 00000000..1a95ab7e
--- /dev/null
+++ b/Tests/Caster/AddressInfoCasterTest.php
@@ -0,0 +1,35 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\VarDumper\Tests\Caster;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\VarDumper\Test\VarDumperTestTrait;
+
+/**
+ * @requires extension sockets
+ */
+class AddressInfoCasterTest extends TestCase
+{
+ use VarDumperTestTrait;
+
+ public function testCaster()
+ {
+ $xDump = <<