Repository: symfony/var-dumper Branch: 8.1 Commit: bbd278a21804 Files: 162 Total size: 560.8 KB Directory structure: gitextract_7b4gxnj_/ ├── .gitattributes ├── .github/ │ ├── PULL_REQUEST_TEMPLATE.md │ └── workflows/ │ └── close-pull-request.yml ├── .gitignore ├── CHANGELOG.md ├── Caster/ │ ├── AddressInfoCaster.php │ ├── AmqpCaster.php │ ├── ArgsStub.php │ ├── Caster.php │ ├── ClassStub.php │ ├── ConstStub.php │ ├── CurlCaster.php │ ├── CutArrayStub.php │ ├── CutStub.php │ ├── DOMCaster.php │ ├── DateCaster.php │ ├── DoctrineCaster.php │ ├── DsCaster.php │ ├── DsPairStub.php │ ├── EnumStub.php │ ├── ExceptionCaster.php │ ├── FFICaster.php │ ├── FiberCaster.php │ ├── FrameStub.php │ ├── GdCaster.php │ ├── GmpCaster.php │ ├── ImagineCaster.php │ ├── ImgStub.php │ ├── IntlCaster.php │ ├── LinkStub.php │ ├── MemcachedCaster.php │ ├── MysqliCaster.php │ ├── OpenSSLCaster.php │ ├── PdoCaster.php │ ├── PgSqlCaster.php │ ├── ProxyManagerCaster.php │ ├── RdKafkaCaster.php │ ├── RedisCaster.php │ ├── ReflectionCaster.php │ ├── ResourceCaster.php │ ├── ScalarStub.php │ ├── SocketCaster.php │ ├── SplCaster.php │ ├── SqliteCaster.php │ ├── StubCaster.php │ ├── SymfonyCaster.php │ ├── TraceStub.php │ ├── UninitializedStub.php │ ├── UuidCaster.php │ ├── VirtualStub.php │ ├── XmlReaderCaster.php │ └── XmlResourceCaster.php ├── Cloner/ │ ├── AbstractCloner.php │ ├── ClonerInterface.php │ ├── Cursor.php │ ├── Data.php │ ├── DumperInterface.php │ ├── Stub.php │ └── VarCloner.php ├── Command/ │ ├── Descriptor/ │ │ ├── CliDescriptor.php │ │ ├── DumpDescriptorInterface.php │ │ └── HtmlDescriptor.php │ └── ServerDumpCommand.php ├── Dumper/ │ ├── AbstractDumper.php │ ├── CliDumper.php │ ├── ContextProvider/ │ │ ├── CliContextProvider.php │ │ ├── ContextProviderInterface.php │ │ ├── RequestContextProvider.php │ │ └── SourceContextProvider.php │ ├── ContextualizedDumper.php │ ├── DataDumperInterface.php │ ├── HtmlDumper.php │ └── ServerDumper.php ├── Exception/ │ └── ThrowingCasterException.php ├── LICENSE ├── README.md ├── Resources/ │ ├── bin/ │ │ └── var-dump-server │ ├── css/ │ │ └── htmlDescriptor.css │ ├── functions/ │ │ └── dump.php │ └── js/ │ └── htmlDescriptor.js ├── Server/ │ ├── Connection.php │ └── DumpServer.php ├── Test/ │ └── VarDumperTestTrait.php ├── Tests/ │ ├── Caster/ │ │ ├── AddressInfoCasterTest.php │ │ ├── CasterTest.php │ │ ├── CurlCasterTest.php │ │ ├── DOMCasterTest.php │ │ ├── DateCasterTest.php │ │ ├── DoctrineCasterTest.php │ │ ├── ExceptionCasterTest.php │ │ ├── FFICasterTest.php │ │ ├── FiberCasterTest.php │ │ ├── GmpCasterTest.php │ │ ├── IntlCasterTest.php │ │ ├── MemcachedCasterTest.php │ │ ├── MysqliCasterTest.php │ │ ├── OpenSSLCasterTest.php │ │ ├── PdoCasterTest.php │ │ ├── RdKafkaCasterTest.php │ │ ├── RedisCasterTest.php │ │ ├── ReflectionCasterTest.php │ │ ├── ResourceCasterTest.php │ │ ├── SocketCasterTest.php │ │ ├── SplCasterTest.php │ │ ├── SqliteCasterTest.php │ │ ├── StubCasterTest.php │ │ ├── SymfonyCasterTest.php │ │ └── XmlReaderCasterTest.php │ ├── Cloner/ │ │ ├── DataTest.php │ │ ├── StubTest.php │ │ └── VarClonerTest.php │ ├── Command/ │ │ ├── Descriptor/ │ │ │ ├── CliDescriptorTest.php │ │ │ └── HtmlDescriptorTest.php │ │ └── ServerDumpCommandTest.php │ ├── Dumper/ │ │ ├── CliDumperTest.php │ │ ├── ContextProvider/ │ │ │ └── RequestContextProviderTest.php │ │ ├── ContextualizedDumperTest.php │ │ ├── FunctionsTest.php │ │ ├── HtmlDumperTest.php │ │ ├── ServerDumperTest.php │ │ └── functions/ │ │ ├── dd_with_accept_header_json_array.phpt │ │ ├── dd_with_multiple_args.phpt │ │ ├── dd_with_named_args.phpt │ │ ├── dd_with_null.phpt │ │ ├── dd_with_single_arg.phpt │ │ ├── dump_data_collector_with_spl_array.phpt │ │ ├── dump_with_accept_header_html.phpt │ │ ├── dump_with_accept_header_json_array.phpt │ │ ├── dump_with_accept_header_json_multiple_args.phpt │ │ ├── dump_with_accept_header_json_string.phpt │ │ ├── dump_with_accept_header_wildcard.phpt │ │ ├── dump_with_multiple_args.phpt │ │ ├── dump_with_named_args.phpt │ │ ├── dump_with_null.phpt │ │ ├── dump_with_single_arg.phpt │ │ └── dump_without_args.phpt │ ├── Fixtures/ │ │ ├── BackedEnumFixture.php │ │ ├── DateTimeChild.php │ │ ├── ExtendsReflectionTypeFixture.php │ │ ├── FooInterface.php │ │ ├── GeneratorDemo.php │ │ ├── LotsOfAttributes.php │ │ ├── MyAttribute.php │ │ ├── NotLoadableClass.php │ │ ├── Php74.php │ │ ├── Php81Enums.php │ │ ├── Php82NullStandaloneReturnType.php │ │ ├── ReflectionIntersectionTypeFixture.php │ │ ├── ReflectionNamedTypeFixture.php │ │ ├── ReflectionUnionTypeFixture.php │ │ ├── ReflectionUnionTypeWithIntersectionFixture.php │ │ ├── Twig.php │ │ ├── UnitEnumFixture.php │ │ ├── VirtualProperty.php │ │ ├── dumb-var.php │ │ ├── dump_server.php │ │ └── xml_reader.xml │ ├── Server/ │ │ └── ConnectionTest.php │ └── Test/ │ └── VarDumperTestTraitTest.php ├── VarDumper.php ├── composer.json └── phpunit.xml.dist ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ /Tests export-ignore /phpunit.xml.dist export-ignore /.git* export-ignore ================================================ FILE: .github/PULL_REQUEST_TEMPLATE.md ================================================ Please do not submit any Pull Requests here. They will be closed. --- Please submit your PR here instead: https://github.com/symfony/symfony This repository is what we call a "subtree split": a read-only subset of that main repository. We're looking forward to your PR there! ================================================ FILE: .github/workflows/close-pull-request.yml ================================================ name: Close Pull Request on: pull_request_target: types: [opened] jobs: run: runs-on: ubuntu-latest steps: - uses: superbrothers/close-pull-request@v3 with: comment: | Thanks for your Pull Request! We love contributions. However, you should instead open your PR on the main repository: https://github.com/symfony/symfony This repository is what we call a "subtree split": a read-only subset of that main repository. We're looking forward to your PR there! ================================================ FILE: .gitignore ================================================ composer.lock phpunit.xml vendor/ ================================================ FILE: CHANGELOG.md ================================================ CHANGELOG ========= 7.4 --- * Add support for adding more default casters to `AbstractCloner::addDefaultCasters()` * Select HtmlDumper only if `Accept` header contains "html" 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 --- * Add support for `FORCE_COLOR` environment variable * Add support for virtual properties 7.1 --- * Add support for new DOM extension classes in `DOMCaster` 7.0 --- * Add argument `$label` to `VarDumper::dump()` * Require explicit argument when calling `VarDumper::setHandler()` * Remove display of backtrace in `Twig_Template`, only `Twig\Template` is supported 6.4 --- * Dump uninitialized properties 6.3 --- * Add caster for `WeakMap` * Add support of named arguments to `dd()` and `dump()` to display the argument name * Add support for `Relay\Relay` * Add display of invisible characters 6.2 --- * Add support for `FFI\CData` and `FFI\CType` * Deprecate calling `VarDumper::setHandler()` without arguments 5.4 --- * Add ability to style integer and double values independently * Add casters for Symfony's UUIDs and ULIDs * Add support for `Fiber` 5.2.0 ----- * added support for PHPUnit `--colors` option * added `VAR_DUMPER_FORMAT=server` env var value support * prevent replacing the handler when the `VAR_DUMPER_FORMAT` env var is set 5.1.0 ----- * added `RdKafka` support 4.4.0 ----- * added `VarDumperTestTrait::setUpVarDumper()` and `VarDumperTestTrait::tearDownVarDumper()` to configure casters & flags to use in tests * added `ImagineCaster` and infrastructure to dump images * added the stamps of a message after it is dispatched in `TraceableMessageBus` and `MessengerDataCollector` collected data * added `UuidCaster` * made all casters final * added support for the `NO_COLOR` env var (https://no-color.org/) 4.3.0 ----- * added `DsCaster` to support dumping the contents of data structures from the Ds extension 4.2.0 ----- * support selecting the format to use by setting the environment variable `VAR_DUMPER_FORMAT` to `html` or `cli` 4.1.0 ----- * added a `ServerDumper` to send serialized Data clones to a server * added a `ServerDumpCommand` and `DumpServer` to run a server collecting and displaying dumps on a single place with multiple formats support * added `CliDescriptor` and `HtmlDescriptor` descriptors for `server:dump` CLI and HTML formats support 4.0.0 ----- * support for passing `\ReflectionClass` instances to the `Caster::castObject()` method has been dropped, pass class names as strings instead * the `Data::getRawData()` method has been removed * the `VarDumperTestTrait::assertDumpEquals()` method expects a 3rd `$filter = 0` argument and moves `$message = ''` argument at 4th position. * the `VarDumperTestTrait::assertDumpMatchesFormat()` method expects a 3rd `$filter = 0` argument and moves `$message = ''` argument at 4th position. 3.4.0 ----- * added `AbstractCloner::setMinDepth()` function to ensure minimum tree depth * deprecated `MongoCaster` 2.7.0 ----- * deprecated `Cloner\Data::getLimitedClone()`. Use `withMaxDepth`, `withMaxItemsPerDepth` or `withRefHandles` instead. ================================================ FILE: Caster/AddressInfoCaster.php ================================================ * * 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 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; } } ================================================ FILE: Caster/AmqpCaster.php ================================================ * * 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; /** * Casts Amqp related classes to array representation. * * @author Grégoire Pineau * * @final * * @internal */ class AmqpCaster { private const FLAGS = [ \AMQP_DURABLE => 'AMQP_DURABLE', \AMQP_PASSIVE => 'AMQP_PASSIVE', \AMQP_EXCLUSIVE => 'AMQP_EXCLUSIVE', \AMQP_AUTODELETE => 'AMQP_AUTODELETE', \AMQP_INTERNAL => 'AMQP_INTERNAL', \AMQP_NOLOCAL => 'AMQP_NOLOCAL', \AMQP_AUTOACK => 'AMQP_AUTOACK', \AMQP_IFEMPTY => 'AMQP_IFEMPTY', \AMQP_IFUNUSED => 'AMQP_IFUNUSED', \AMQP_MANDATORY => 'AMQP_MANDATORY', \AMQP_IMMEDIATE => 'AMQP_IMMEDIATE', \AMQP_MULTIPLE => 'AMQP_MULTIPLE', \AMQP_NOWAIT => 'AMQP_NOWAIT', \AMQP_REQUEUE => 'AMQP_REQUEUE', ]; private const EXCHANGE_TYPES = [ \AMQP_EX_TYPE_DIRECT => 'AMQP_EX_TYPE_DIRECT', \AMQP_EX_TYPE_FANOUT => 'AMQP_EX_TYPE_FANOUT', \AMQP_EX_TYPE_TOPIC => 'AMQP_EX_TYPE_TOPIC', \AMQP_EX_TYPE_HEADERS => 'AMQP_EX_TYPE_HEADERS', ]; public static function castConnection(\AMQPConnection $c, array $a, Stub $stub, bool $isNested): array { $prefix = Caster::PREFIX_VIRTUAL; $a += [ $prefix.'is_connected' => $c->isConnected(), ]; // Recent version of the extension already expose private properties if (isset($a["\x00AMQPConnection\x00login"])) { return $a; } // BC layer in the amqp lib if (method_exists($c, 'getReadTimeout')) { $timeout = $c->getReadTimeout(); } else { $timeout = $c->getTimeout(); } $a += [ $prefix.'is_connected' => $c->isConnected(), $prefix.'login' => $c->getLogin(), $prefix.'password' => $c->getPassword(), $prefix.'host' => $c->getHost(), $prefix.'vhost' => $c->getVhost(), $prefix.'port' => $c->getPort(), $prefix.'read_timeout' => $timeout, ]; return $a; } public static function castChannel(\AMQPChannel $c, array $a, Stub $stub, bool $isNested): array { $prefix = Caster::PREFIX_VIRTUAL; $a += [ $prefix.'is_connected' => $c->isConnected(), $prefix.'channel_id' => $c->getChannelId(), ]; // Recent version of the extension already expose private properties if (isset($a["\x00AMQPChannel\x00connection"])) { return $a; } $a += [ $prefix.'connection' => $c->getConnection(), $prefix.'prefetch_size' => $c->getPrefetchSize(), $prefix.'prefetch_count' => $c->getPrefetchCount(), ]; return $a; } public static function castQueue(\AMQPQueue $c, array $a, Stub $stub, bool $isNested): array { $prefix = Caster::PREFIX_VIRTUAL; $a += [ $prefix.'flags' => self::extractFlags($c->getFlags()), ]; // Recent version of the extension already expose private properties if (isset($a["\x00AMQPQueue\x00name"])) { return $a; } $a += [ $prefix.'connection' => $c->getConnection(), $prefix.'channel' => $c->getChannel(), $prefix.'name' => $c->getName(), $prefix.'arguments' => $c->getArguments(), ]; return $a; } public static function castExchange(\AMQPExchange $c, array $a, Stub $stub, bool $isNested): array { $prefix = Caster::PREFIX_VIRTUAL; $a += [ $prefix.'flags' => self::extractFlags($c->getFlags()), ]; $type = isset(self::EXCHANGE_TYPES[$c->getType()]) ? new ConstStub(self::EXCHANGE_TYPES[$c->getType()], $c->getType()) : $c->getType(); // Recent version of the extension already expose private properties if (isset($a["\x00AMQPExchange\x00name"])) { $a["\x00AMQPExchange\x00type"] = $type; return $a; } $a += [ $prefix.'connection' => $c->getConnection(), $prefix.'channel' => $c->getChannel(), $prefix.'name' => $c->getName(), $prefix.'type' => $type, $prefix.'arguments' => $c->getArguments(), ]; return $a; } public static function castEnvelope(\AMQPEnvelope $c, array $a, Stub $stub, bool $isNested, int $filter = 0): array { $prefix = Caster::PREFIX_VIRTUAL; $deliveryMode = new ConstStub($c->getDeliveryMode().(2 === $c->getDeliveryMode() ? ' (persistent)' : ' (non-persistent)'), $c->getDeliveryMode()); // Recent version of the extension already expose private properties if (isset($a["\x00AMQPEnvelope\x00body"])) { $a["\0AMQPEnvelope\0delivery_mode"] = $deliveryMode; return $a; } if (!($filter & Caster::EXCLUDE_VERBOSE)) { $a += [$prefix.'body' => $c->getBody()]; } $a += [ $prefix.'delivery_tag' => $c->getDeliveryTag(), $prefix.'is_redelivery' => $c->isRedelivery(), $prefix.'exchange_name' => $c->getExchangeName(), $prefix.'routing_key' => $c->getRoutingKey(), $prefix.'content_type' => $c->getContentType(), $prefix.'content_encoding' => $c->getContentEncoding(), $prefix.'headers' => $c->getHeaders(), $prefix.'delivery_mode' => $deliveryMode, $prefix.'priority' => $c->getPriority(), $prefix.'correlation_id' => $c->getCorrelationId(), $prefix.'reply_to' => $c->getReplyTo(), $prefix.'expiration' => $c->getExpiration(), $prefix.'message_id' => $c->getMessageId(), $prefix.'timestamp' => $c->getTimeStamp(), $prefix.'type' => $c->getType(), $prefix.'user_id' => $c->getUserId(), $prefix.'app_id' => $c->getAppId(), ]; return $a; } private static function extractFlags(int $flags): ConstStub { $flagsArray = []; foreach (self::FLAGS as $value => $name) { if ($flags & $value) { $flagsArray[] = $name; } } if (!$flagsArray) { $flagsArray = ['AMQP_NOPARAM']; } return new ConstStub(implode('|', $flagsArray), $flags); } } ================================================ FILE: Caster/ArgsStub.php ================================================ * * 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; /** * Represents a list of function arguments. * * @author Nicolas Grekas */ class ArgsStub extends EnumStub { private static array $parameters = []; public function __construct(array $args, string $function, ?string $class) { [$variadic, $params] = self::getParameters($function, $class); $values = []; foreach ($args as $k => $v) { $values[$k] = !\is_scalar($v) && !$v instanceof Stub ? new CutStub($v) : $v; } if (null === $params) { parent::__construct($values, false); return; } if (\count($values) < \count($params)) { $params = \array_slice($params, 0, \count($values)); } elseif (\count($values) > \count($params)) { $values[] = new EnumStub(array_splice($values, \count($params)), false); $params[] = $variadic; } if (['...'] === $params) { parent::__construct($values[0]->value, false); } else { parent::__construct(array_combine($params, $values)); } } private static function getParameters(string $function, ?string $class): array { if (isset(self::$parameters[$k = $class.'::'.$function])) { return self::$parameters[$k]; } try { $r = null !== $class ? new \ReflectionMethod($class, $function) : new \ReflectionFunction($function); } catch (\ReflectionException) { return [null, null]; } $variadic = '...'; $params = []; foreach ($r->getParameters() as $v) { $k = '$'.$v->name; if ($v->isPassedByReference()) { $k = '&'.$k; } if ($v->isVariadic()) { $variadic .= $k; } else { $params[] = $k; } } return self::$parameters[$k] = [$variadic, $params]; } } ================================================ FILE: Caster/Caster.php ================================================ * * 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; /** * Helper for filtering out properties in casters. * * @author Nicolas Grekas * * @final */ class Caster { public const EXCLUDE_VERBOSE = 1; public const EXCLUDE_VIRTUAL = 2; public const EXCLUDE_DYNAMIC = 4; public const EXCLUDE_PUBLIC = 8; public const EXCLUDE_PROTECTED = 16; public const EXCLUDE_PRIVATE = 32; public const EXCLUDE_NULL = 64; public const EXCLUDE_EMPTY = 128; public const EXCLUDE_NOT_IMPORTANT = 256; public const EXCLUDE_STRICT = 512; public const EXCLUDE_UNINITIALIZED = 1024; public const PREFIX_VIRTUAL = "\0~\0"; public const PREFIX_DYNAMIC = "\0+\0"; public const PREFIX_PROTECTED = "\0*\0"; // usage: sprintf(Caster::PATTERN_PRIVATE, $class, $property) public const PATTERN_PRIVATE = "\0%s\0%s"; private static array $classProperties = []; /** * Casts objects to arrays and adds the dynamic property prefix. * * @param bool $hasDebugInfo Whether the __debugInfo method exists on $obj or not * * @internal */ public static function castObject(object $obj, string $class, bool $hasDebugInfo = false, ?string $debugClass = null): array { if ($hasDebugInfo) { try { $debugInfo = $obj->__debugInfo(); } catch (\Throwable) { // ignore failing __debugInfo() $hasDebugInfo = false; } } $a = $obj instanceof \Closure ? [] : (array) $obj; if ($obj instanceof \__PHP_Incomplete_Class) { return $a; } $classProperties = self::$classProperties[$class] ??= self::getClassProperties(new \ReflectionClass($class)); $a = array_replace($classProperties, $a); if ($a) { $debugClass ??= get_debug_type($obj); $i = 0; $prefixedKeys = []; foreach ($a as $k => $v) { if ("\0" !== ($k[0] ?? '')) { if (!isset($classProperties[$k])) { $prefixedKeys[$i] = self::PREFIX_DYNAMIC.$k; } } elseif ($debugClass !== $class && 1 === strpos($k, $class)) { $prefixedKeys[$i] = "\0".$debugClass.strrchr($k, "\0"); } ++$i; } if ($prefixedKeys) { $keys = array_keys($a); foreach ($prefixedKeys as $i => $k) { $keys[$i] = $k; } $a = array_combine($keys, $a); } } if ($hasDebugInfo && \is_array($debugInfo)) { foreach ($debugInfo as $k => $v) { if (!isset($k[0]) || "\0" !== $k[0]) { if (\array_key_exists(self::PREFIX_DYNAMIC.$k, $a)) { continue; } $k = self::PREFIX_VIRTUAL.$k; } unset($a[$k]); $a[$k] = $v; } } return $a; } /** * Filters out the specified properties. * * By default, a single match in the $filter bit field filters properties out, following an "or" logic. * When EXCLUDE_STRICT is set, an "and" logic is applied: all bits must match for a property to be removed. * * @param array $a The array containing the properties to filter * @param int $filter A bit field of Caster::EXCLUDE_* constants specifying which properties to filter out * @param string[] $listedProperties List of properties to exclude when Caster::EXCLUDE_VERBOSE is set, and to preserve when Caster::EXCLUDE_NOT_IMPORTANT is set * @param int|null &$count Set to the number of removed properties */ public static function filter(array $a, int $filter, array $listedProperties = [], ?int &$count = 0): array { $count = 0; foreach ($a as $k => $v) { $type = self::EXCLUDE_STRICT & $filter; if (null === $v) { $type |= self::EXCLUDE_NULL & $filter; $type |= self::EXCLUDE_EMPTY & $filter; } elseif (false === $v || '' === $v || '0' === $v || 0 === $v || 0.0 === $v || [] === $v) { $type |= self::EXCLUDE_EMPTY & $filter; } elseif ($v instanceof UninitializedStub) { $type |= self::EXCLUDE_UNINITIALIZED & $filter; } if ((self::EXCLUDE_NOT_IMPORTANT & $filter) && !\in_array($k, $listedProperties, true)) { $type |= self::EXCLUDE_NOT_IMPORTANT; } if ((self::EXCLUDE_VERBOSE & $filter) && \in_array($k, $listedProperties, true)) { $type |= self::EXCLUDE_VERBOSE; } if (!isset($k[1]) || "\0" !== $k[0]) { $type |= self::EXCLUDE_PUBLIC & $filter; } elseif ('~' === $k[1]) { $type |= self::EXCLUDE_VIRTUAL & $filter; } elseif ('+' === $k[1]) { $type |= self::EXCLUDE_DYNAMIC & $filter; } elseif ('*' === $k[1]) { $type |= self::EXCLUDE_PROTECTED & $filter; } else { $type |= self::EXCLUDE_PRIVATE & $filter; } if ((self::EXCLUDE_STRICT & $filter) ? $type === $filter : $type) { unset($a[$k]); ++$count; } } return $a; } /** * @internal */ public static function castPhpIncompleteClass(\__PHP_Incomplete_Class $c, array $a, Stub $stub, bool $isNested): array { if (isset($a['__PHP_Incomplete_Class_Name'])) { $stub->class .= '('.$a['__PHP_Incomplete_Class_Name'].')'; unset($a['__PHP_Incomplete_Class_Name']); } return $a; } private static function getClassProperties(\ReflectionClass $class): array { $classProperties = []; $className = $class->name; if ($parent = $class->getParentClass()) { $classProperties += self::$classProperties[$parent->name] ??= self::getClassProperties($parent); } foreach ($class->getProperties() as $p) { if ($p->isStatic()) { continue; } $classProperties[match (true) { $p->isPublic() => $p->name, $p->isProtected() => self::PREFIX_PROTECTED.$p->name, default => "\0".$className."\0".$p->name, }] = $p->isVirtual() ? new VirtualStub($p) : new UninitializedStub($p); } return $classProperties; } } ================================================ FILE: Caster/ClassStub.php ================================================ * * 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; /** * Represents a PHP class identifier. * * @author Nicolas Grekas */ class ClassStub extends ConstStub { /** * @param string $identifier A PHP identifier, e.g. a class, method, interface, etc. name * @param callable $callable The callable targeted by the identifier when it is ambiguous or not a real PHP identifier */ public function __construct(string $identifier, callable|array|string|null $callable = null) { $this->value = $identifier; try { if (null !== $callable) { if ($callable instanceof \Closure) { $r = new \ReflectionFunction($callable); } elseif (\is_object($callable)) { $r = [$callable, '__invoke']; } elseif (\is_array($callable)) { $r = $callable; } elseif (false !== $i = strpos($callable, '::')) { $r = [substr($callable, 0, $i), substr($callable, 2 + $i)]; } else { $r = new \ReflectionFunction($callable); } } elseif (0 < $i = strpos($identifier, '::') ?: strpos($identifier, '->')) { $r = [substr($identifier, 0, $i), substr($identifier, 2 + $i)]; } else { $r = new \ReflectionClass($identifier); } if (\is_array($r)) { try { $r = new \ReflectionMethod($r[0], $r[1]); } catch (\ReflectionException) { $r = new \ReflectionClass($r[0]); } } if (str_contains($identifier, "@anonymous\0")) { $this->value = $identifier = preg_replace_callback('/[a-zA-Z_\x7f-\xff][\\\\a-zA-Z0-9_\x7f-\xff]*+@anonymous\x00.*?\.php(?:0x?|:[0-9]++\$)?[0-9a-fA-F]++/', static fn ($m) => class_exists($m[0], false) ? (get_parent_class($m[0]) ?: key(class_implements($m[0])) ?: 'class').'@anonymous' : $m[0], $identifier); } if (null !== $callable && $r instanceof \ReflectionFunctionAbstract) { $s = ReflectionCaster::castFunctionAbstract($r, [], new Stub(), true, Caster::EXCLUDE_VERBOSE); $s = ReflectionCaster::getSignature($s); if (str_ends_with($identifier, '()')) { $this->value = substr_replace($identifier, $s, -2); } else { $this->value .= $s; } } } catch (\ReflectionException) { return; } finally { if (0 < $i = strrpos($this->value, '\\')) { $this->attr['ellipsis'] = \strlen($this->value) - $i; $this->attr['ellipsis-type'] = 'class'; $this->attr['ellipsis-tail'] = 1; } } if ($f = $r->getFileName()) { $this->attr['file'] = $f; $this->attr['line'] = $r->getStartLine(); } } public static function wrapCallable(mixed $callable): mixed { if (\is_object($callable) || !\is_callable($callable)) { return $callable; } if (!\is_array($callable)) { $callable = new static($callable, $callable); } elseif (\is_string($callable[0])) { $callable[0] = new static($callable[0], $callable); } else { $callable[1] = new static($callable[1], $callable); } return $callable; } } ================================================ FILE: Caster/ConstStub.php ================================================ * * 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; /** * Represents a PHP constant and its value. * * @author Nicolas Grekas */ class ConstStub extends Stub { public function __construct(string $name, string|int|float|null $value = null) { $this->class = $name; $this->value = 1 < \func_num_args() ? $value : $name; } public function __toString(): string { return (string) $this->value; } /** * @param array $values */ public static function fromBitfield(int $value, array $values): self { $names = []; foreach ($values as $v => $name) { if ($value & $v) { $names[] = $name; } } if (!$names) { $names[] = $values[0] ?? 0; } return new self(implode(' | ', $names), $value); } } ================================================ FILE: Caster/CurlCaster.php ================================================ * * 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 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; } } ================================================ FILE: Caster/CutArrayStub.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; /** * Represents a cut array. * * @author Nicolas Grekas */ class CutArrayStub extends CutStub { public array $preservedSubset; public function __construct(array $value, array $preservedKeys) { parent::__construct($value); $this->preservedSubset = array_intersect_key($value, array_flip($preservedKeys)); $this->cut -= \count($this->preservedSubset); } } ================================================ FILE: Caster/CutStub.php ================================================ * * 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; /** * Represents the main properties of a PHP variable, pre-casted by a caster. * * @author Nicolas Grekas */ class CutStub extends Stub { public function __construct(mixed $value) { $this->value = $value; switch (\gettype($value)) { case 'object': $this->type = self::TYPE_OBJECT; $this->class = get_debug_type($value); if ($value instanceof \Closure) { ReflectionCaster::castClosure($value, [], $this, true, Caster::EXCLUDE_VERBOSE); } $this->cut = -1; break; case 'array': $this->type = self::TYPE_ARRAY; $this->class = self::ARRAY_ASSOC; $this->cut = $this->value = \count($value); break; case 'resource': case 'unknown type': case 'resource (closed)': $this->type = self::TYPE_RESOURCE; $this->handle = (int) $value; if ('Unknown' === $this->class = @get_resource_type($value)) { $this->class = 'Closed'; } $this->cut = -1; break; case 'string': $this->type = self::TYPE_STRING; $this->class = preg_match('//u', $value) ? self::STRING_UTF8 : self::STRING_BINARY; $this->cut = self::STRING_BINARY === $this->class ? \strlen($value) : mb_strlen($value, 'UTF-8'); $this->value = ''; break; } } } ================================================ FILE: Caster/DOMCaster.php ================================================ * * 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; /** * Casts DOM related classes to array representation. * * @author Nicolas Grekas * * @final * * @internal */ class DOMCaster { private const ERROR_CODES = [ 0 => 'DOM_PHP_ERR', \DOM_INDEX_SIZE_ERR => 'DOM_INDEX_SIZE_ERR', \DOMSTRING_SIZE_ERR => 'DOMSTRING_SIZE_ERR', \DOM_HIERARCHY_REQUEST_ERR => 'DOM_HIERARCHY_REQUEST_ERR', \DOM_WRONG_DOCUMENT_ERR => 'DOM_WRONG_DOCUMENT_ERR', \DOM_INVALID_CHARACTER_ERR => 'DOM_INVALID_CHARACTER_ERR', \DOM_NO_DATA_ALLOWED_ERR => 'DOM_NO_DATA_ALLOWED_ERR', \DOM_NO_MODIFICATION_ALLOWED_ERR => 'DOM_NO_MODIFICATION_ALLOWED_ERR', \DOM_NOT_FOUND_ERR => 'DOM_NOT_FOUND_ERR', \DOM_NOT_SUPPORTED_ERR => 'DOM_NOT_SUPPORTED_ERR', \DOM_INUSE_ATTRIBUTE_ERR => 'DOM_INUSE_ATTRIBUTE_ERR', \DOM_INVALID_STATE_ERR => 'DOM_INVALID_STATE_ERR', \DOM_SYNTAX_ERR => 'DOM_SYNTAX_ERR', \DOM_INVALID_MODIFICATION_ERR => 'DOM_INVALID_MODIFICATION_ERR', \DOM_NAMESPACE_ERR => 'DOM_NAMESPACE_ERR', \DOM_INVALID_ACCESS_ERR => 'DOM_INVALID_ACCESS_ERR', \DOM_VALIDATION_ERR => 'DOM_VALIDATION_ERR', ]; private const NODE_TYPES = [ \XML_ELEMENT_NODE => 'XML_ELEMENT_NODE', \XML_ATTRIBUTE_NODE => 'XML_ATTRIBUTE_NODE', \XML_TEXT_NODE => 'XML_TEXT_NODE', \XML_CDATA_SECTION_NODE => 'XML_CDATA_SECTION_NODE', \XML_ENTITY_REF_NODE => 'XML_ENTITY_REF_NODE', \XML_ENTITY_NODE => 'XML_ENTITY_NODE', \XML_PI_NODE => 'XML_PI_NODE', \XML_COMMENT_NODE => 'XML_COMMENT_NODE', \XML_DOCUMENT_NODE => 'XML_DOCUMENT_NODE', \XML_DOCUMENT_TYPE_NODE => 'XML_DOCUMENT_TYPE_NODE', \XML_DOCUMENT_FRAG_NODE => 'XML_DOCUMENT_FRAG_NODE', \XML_NOTATION_NODE => 'XML_NOTATION_NODE', \XML_HTML_DOCUMENT_NODE => 'XML_HTML_DOCUMENT_NODE', \XML_DTD_NODE => 'XML_DTD_NODE', \XML_ELEMENT_DECL_NODE => 'XML_ELEMENT_DECL_NODE', \XML_ATTRIBUTE_DECL_NODE => 'XML_ATTRIBUTE_DECL_NODE', \XML_ENTITY_DECL_NODE => 'XML_ENTITY_DECL_NODE', \XML_NAMESPACE_DECL_NODE => 'XML_NAMESPACE_DECL_NODE', ]; public static function castException(\DOMException|\Dom\Exception $e, array $a, Stub $stub, bool $isNested): array { $k = Caster::PREFIX_PROTECTED.'code'; if (isset($a[$k], self::ERROR_CODES[$a[$k]])) { $a[$k] = new ConstStub(self::ERROR_CODES[$a[$k]], $a[$k]); } return $a; } public static function castLength($dom, array $a, Stub $stub, bool $isNested): array { return $a; } public static function castImplementation(\DOMImplementation|\Dom\Implementation $dom, array $a, Stub $stub, bool $isNested): array { $a += [ Caster::PREFIX_VIRTUAL.'Core' => '1.0', Caster::PREFIX_VIRTUAL.'XML' => '2.0', ]; return $a; } public static function castNode(\DOMNode|\Dom\Node $dom, array $a, Stub $stub, bool $isNested): array { return self::castDom($dom, $a, $stub, $isNested); } public static function castNameSpaceNode(\DOMNameSpaceNode $dom, array $a, Stub $stub, bool $isNested): array { return self::castDom($dom, $a, $stub, $isNested); } public static function castDocument(\DOMDocument $dom, array $a, Stub $stub, bool $isNested, int $filter = 0): array { if (!($filter & Caster::EXCLUDE_VERBOSE)) { $formatOutput = $dom->formatOutput; $dom->formatOutput = true; $a += [Caster::PREFIX_VIRTUAL.'xml' => $dom->saveXML()]; $dom->formatOutput = $formatOutput; } return $a; } public static function castXMLDocument(\Dom\XMLDocument $dom, array $a, Stub $stub, bool $isNested, int $filter = 0): array { if (!($filter & Caster::EXCLUDE_VERBOSE)) { $formatOutput = $dom->formatOutput; $dom->formatOutput = true; $a += [Caster::PREFIX_VIRTUAL.'xml' => $dom->saveXML()]; $dom->formatOutput = $formatOutput; } return $a; } public static function castHTMLDocument(\Dom\HTMLDocument $dom, array $a, Stub $stub, bool $isNested, int $filter = 0): array { if (!($filter & Caster::EXCLUDE_VERBOSE)) { $a += [Caster::PREFIX_VIRTUAL.'html' => $dom->saveHTML()]; } return $a; } public static function castCharacterData(\DOMCharacterData|\Dom\CharacterData $dom, array $a, Stub $stub, bool $isNested): array { return $a; } public static function castAttr(\DOMAttr|\Dom\Attr $dom, array $a, Stub $stub, bool $isNested): array { return $a; } public static function castElement(\DOMElement|\Dom\Element $dom, array $a, Stub $stub, bool $isNested): array { return $a; } public static function castText(\DOMText|\Dom\Text $dom, array $a, Stub $stub, bool $isNested): array { return $a; } public static function castDocumentType(\DOMDocumentType|\Dom\DocumentType $dom, array $a, Stub $stub, bool $isNested): array { return $a; } public static function castNotation(\DOMNotation|\Dom\Notation $dom, array $a, Stub $stub, bool $isNested): array { return $a; } public static function castEntity(\DOMEntity|\Dom\Entity $dom, array $a, Stub $stub, bool $isNested): array { return $a; } public static function castProcessingInstruction(\DOMProcessingInstruction|\Dom\ProcessingInstruction $dom, array $a, Stub $stub, bool $isNested): array { return $a; } public static function castXPath(\DOMXPath|\Dom\XPath $dom, array $a, Stub $stub, bool $isNested): array { return self::castDom($dom, $a, $stub, $isNested); } public static function castDom($dom, array $a, Stub $stub, bool $isNested, int $filter = 0): array { foreach ($a as $k => $v) { if ('encoding' === $k && $dom instanceof \DOMEntity || \in_array($k, ['actualEncoding', 'config', 'standalone', 'version'], true) ) { continue; // deprecated properties } $v = $dom->$k; $a[$k] = match (true) { $v instanceof \DOMNode || $v instanceof \Dom\Node => new CutStub($v), 'nodeType' === $k => new ConstStub(self::NODE_TYPES[$v], $v), 'baseURI' === $k && $v, 'documentURI' === $k && $v => new LinkStub($v), default => $v, }; } if ($dom instanceof \IteratorAggregate) { foreach ($dom as $k => $v) { $a[Caster::PREFIX_VIRTUAL.$k] = $v; } } return $a; } } ================================================ FILE: Caster/DateCaster.php ================================================ * * 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; /** * Casts DateTimeInterface related classes to array representation. * * @author Dany Maillard * * @final * * @internal */ class DateCaster { private const PERIOD_LIMIT = 3; public static function castDateTime(\DateTimeInterface $d, array $a, Stub $stub, bool $isNested, int $filter): array { $prefix = Caster::PREFIX_VIRTUAL; $location = $d->getTimezone() ? $d->getTimezone()->getLocation() : null; $fromNow = (new \DateTimeImmutable())->diff($d); $title = $d->format('l, F j, Y') ."\n".self::formatInterval($fromNow).' from now' .($location ? ($d->format('I') ? "\nDST On" : "\nDST Off") : '') ; unset( $a[Caster::PREFIX_DYNAMIC.'date'], $a[Caster::PREFIX_DYNAMIC.'timezone'], $a[Caster::PREFIX_DYNAMIC.'timezone_type'] ); $a[$prefix.'date'] = new ConstStub(self::formatDateTime($d, $location ? ' e (P)' : ' P'), $title); $stub->class .= $d->format(' @U'); return $a; } public static function castInterval(\DateInterval $interval, array $a, Stub $stub, bool $isNested, int $filter): array { $now = new \DateTimeImmutable('@0', new \DateTimeZone('UTC')); $numberOfSeconds = $now->add($interval)->getTimestamp() - $now->getTimestamp(); $title = number_format($numberOfSeconds, 0, '.', ' ').'s'; $i = [Caster::PREFIX_VIRTUAL.'interval' => new ConstStub(self::formatInterval($interval), $title)]; return $filter & Caster::EXCLUDE_VERBOSE ? $i : $i + $a; } private static function formatInterval(\DateInterval $i): string { $format = '%R '; if (0 === $i->y && 0 === $i->m && ($i->h >= 24 || $i->i >= 60 || $i->s >= 60)) { $d = new \DateTimeImmutable('@0', new \DateTimeZone('UTC')); $i = $d->diff($d->add($i)); // recalculate carry over points $format .= 0 < $i->days ? '%ad ' : ''; } else { $format .= ($i->y ? '%yy ' : '').($i->m ? '%mm ' : '').($i->d ? '%dd ' : ''); } $format .= $i->h || $i->i || $i->s || $i->f ? '%H:%I:'.self::formatSeconds($i->s, substr($i->f, 2)) : ''; $format = '%R ' === $format ? '0s' : $format; return $i->format(rtrim($format)); } public static function castTimeZone(\DateTimeZone $timeZone, array $a, Stub $stub, bool $isNested, int $filter): array { $location = $timeZone->getLocation(); $formatted = (new \DateTimeImmutable('now', $timeZone))->format($location ? 'e (P)' : 'P'); $title = $location && \extension_loaded('intl') ? \Locale::getDisplayRegion('-'.$location['country_code']) : ''; $z = [Caster::PREFIX_VIRTUAL.'timezone' => new ConstStub($formatted, $title)]; return $filter & Caster::EXCLUDE_VERBOSE ? $z : $z + $a; } public static function castPeriod(\DatePeriod $p, array $a, Stub $stub, bool $isNested, int $filter): array { $dates = []; foreach (clone $p as $i => $d) { if (self::PERIOD_LIMIT === $i) { $now = new \DateTimeImmutable('now', new \DateTimeZone('UTC')); $dates[] = \sprintf('%s more', ($end = $p->getEndDate()) ? ceil(($end->format('U.u') - $d->format('U.u')) / ((int) $now->add($p->getDateInterval())->format('U.u') - (int) $now->format('U.u'))) : $p->recurrences - $i ); break; } $dates[] = \sprintf('%s) %s', $i + 1, self::formatDateTime($d)); } $period = \sprintf( 'every %s, from %s%s %s', self::formatInterval($p->getDateInterval()), $p->include_start_date ? '[' : ']', self::formatDateTime($p->getStartDate()), ($end = $p->getEndDate()) ? 'to '.self::formatDateTime($end).($p->include_end_date ? ']' : '[') : 'recurring '.$p->recurrences.' time/s' ); $p = [Caster::PREFIX_VIRTUAL.'period' => new ConstStub($period, implode("\n", $dates))]; return $filter & Caster::EXCLUDE_VERBOSE ? $p : $p + $a; } private static function formatDateTime(\DateTimeInterface $d, string $extra = ''): string { return $d->format('Y-m-d H:i:'.self::formatSeconds($d->format('s'), $d->format('u')).$extra); } private static function formatSeconds(string $s, string $us): string { return \sprintf('%02d.%s', $s, 0 === ($len = \strlen($t = rtrim($us, '0'))) ? '0' : ($len <= 3 ? str_pad($t, 3, '0') : $us)); } } ================================================ FILE: Caster/DoctrineCaster.php ================================================ * * 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 Doctrine\Common\Proxy\Proxy as CommonProxy; use Doctrine\ORM\PersistentCollection; use Doctrine\ORM\Proxy\Proxy as OrmProxy; use Symfony\Component\VarDumper\Cloner\Stub; /** * Casts Doctrine related classes to array representation. * * @author Nicolas Grekas * * @final * * @internal */ class DoctrineCaster { public static function castCommonProxy(CommonProxy $proxy, array $a, Stub $stub, bool $isNested): array { foreach (['__cloner__', '__initializer__'] as $k) { if (\array_key_exists($k, $a)) { unset($a[$k]); ++$stub->cut; } } return $a; } public static function castOrmProxy(OrmProxy $proxy, array $a, Stub $stub, bool $isNested): array { foreach (['_entityPersister', '_identifier'] as $k) { if (\array_key_exists($k = "\0Doctrine\\ORM\\Proxy\\Proxy\0".$k, $a)) { unset($a[$k]); ++$stub->cut; } } return $a; } public static function castPersistentCollection(PersistentCollection $coll, array $a, Stub $stub, bool $isNested): array { foreach (['snapshot', 'association', 'typeClass'] as $k) { if (\array_key_exists($k = "\0Doctrine\\ORM\\PersistentCollection\0".$k, $a)) { $a[$k] = new CutStub($a[$k]); } } return $a; } } ================================================ FILE: Caster/DsCaster.php ================================================ * * 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 Ds\Collection; use Ds\Map; use Ds\Pair; use Symfony\Component\VarDumper\Cloner\Stub; /** * Casts Ds extension classes to array representation. * * @author Jáchym Toušek * * @final */ class DsCaster { public static function castCollection(Collection $c, array $a, Stub $stub, bool $isNested): array { $a[Caster::PREFIX_VIRTUAL.'count'] = $c->count(); $a[Caster::PREFIX_VIRTUAL.'capacity'] = $c->capacity(); if (!$c instanceof Map) { $a += $c->toArray(); } return $a; } public static function castMap(Map $c, array $a, Stub $stub, bool $isNested): array { foreach ($c as $k => $v) { $a[] = new DsPairStub($k, $v); } return $a; } public static function castPair(Pair $c, array $a, Stub $stub, bool $isNested): array { foreach ($c->toArray() as $k => $v) { $a[Caster::PREFIX_VIRTUAL.$k] = $v; } return $a; } public static function castPairStub(DsPairStub $c, array $a, Stub $stub, bool $isNested): array { if ($isNested) { $stub->class = Pair::class; $stub->value = null; $stub->handle = 0; $a = $c->value; } return $a; } } ================================================ FILE: Caster/DsPairStub.php ================================================ * * 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 */ class DsPairStub extends Stub { public function __construct(mixed $key, mixed $value) { $this->value = [ Caster::PREFIX_VIRTUAL.'key' => $key, Caster::PREFIX_VIRTUAL.'value' => $value, ]; } } ================================================ FILE: Caster/EnumStub.php ================================================ * * 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; /** * Represents an enumeration of values. * * @author Nicolas Grekas */ class EnumStub extends Stub { public function __construct( array $values, public bool $dumpKeys = true, ) { $this->value = $values; } } ================================================ FILE: Caster/ExceptionCaster.php ================================================ * * 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\ErrorHandler\Exception\FlattenException; use Symfony\Component\ErrorHandler\Exception\SilencedErrorContext; use Symfony\Component\VarDumper\Cloner\Stub; use Symfony\Component\VarDumper\Exception\ThrowingCasterException; /** * Casts common Exception classes to array representation. * * @author Nicolas Grekas * * @final * * @internal */ class ExceptionCaster { public static int $srcContext = 1; public static bool $traceArgs = true; public static array $errorTypes = [ \E_DEPRECATED => 'E_DEPRECATED', \E_USER_DEPRECATED => 'E_USER_DEPRECATED', \E_RECOVERABLE_ERROR => 'E_RECOVERABLE_ERROR', \E_ERROR => 'E_ERROR', \E_WARNING => 'E_WARNING', \E_PARSE => 'E_PARSE', \E_NOTICE => 'E_NOTICE', \E_CORE_ERROR => 'E_CORE_ERROR', \E_CORE_WARNING => 'E_CORE_WARNING', \E_COMPILE_ERROR => 'E_COMPILE_ERROR', \E_COMPILE_WARNING => 'E_COMPILE_WARNING', \E_USER_ERROR => 'E_USER_ERROR', \E_USER_WARNING => 'E_USER_WARNING', \E_USER_NOTICE => 'E_USER_NOTICE', 2048 => 'E_STRICT', ]; private static array $framesCache = []; public static function castError(\Error $e, array $a, Stub $stub, bool $isNested, int $filter = 0): array { return self::filterExceptionArray($stub->class, $a, "\0Error\0", $filter); } public static function castException(\Exception $e, array $a, Stub $stub, bool $isNested, int $filter = 0): array { return self::filterExceptionArray($stub->class, $a, "\0Exception\0", $filter); } public static function castErrorException(\ErrorException $e, array $a, Stub $stub, bool $isNested): array { if (isset($a[$s = Caster::PREFIX_PROTECTED.'severity'], self::$errorTypes[$a[$s]])) { $a[$s] = new ConstStub(self::$errorTypes[$a[$s]], $a[$s]); } return $a; } public static function castThrowingCasterException(ThrowingCasterException $e, array $a, Stub $stub, bool $isNested): array { $trace = Caster::PREFIX_VIRTUAL.'trace'; $prefix = Caster::PREFIX_PROTECTED; $xPrefix = "\0Exception\0"; if (isset($a[$xPrefix.'previous'], $a[$trace]) && $a[$xPrefix.'previous'] instanceof \Exception) { $b = (array) $a[$xPrefix.'previous']; $class = get_debug_type($a[$xPrefix.'previous']); self::traceUnshift($b[$xPrefix.'trace'], $class, $b[$prefix.'file'], $b[$prefix.'line']); $a[$trace] = new TraceStub($b[$xPrefix.'trace'], false, 0, -\count($a[$trace]->value)); } unset($a[$xPrefix.'previous'], $a[$prefix.'code'], $a[$prefix.'file'], $a[$prefix.'line']); return $a; } public static function castSilencedErrorContext(SilencedErrorContext $e, array $a, Stub $stub, bool $isNested): array { $sPrefix = "\0".SilencedErrorContext::class."\0"; if (!isset($a[$s = $sPrefix.'severity'])) { return $a; } if (isset(self::$errorTypes[$a[$s]])) { $a[$s] = new ConstStub(self::$errorTypes[$a[$s]], $a[$s]); } $trace = [[ 'file' => $a[$sPrefix.'file'], 'line' => $a[$sPrefix.'line'], ]]; if (isset($a[$sPrefix.'trace'])) { $trace = array_merge($trace, $a[$sPrefix.'trace']); } unset($a[$sPrefix.'file'], $a[$sPrefix.'line'], $a[$sPrefix.'trace']); $a[Caster::PREFIX_VIRTUAL.'trace'] = new TraceStub($trace, self::$traceArgs); return $a; } public static function castTraceStub(TraceStub $trace, array $a, Stub $stub, bool $isNested): array { if (!$isNested) { return $a; } $stub->class = ''; $stub->handle = 0; $frames = $trace->value; $prefix = Caster::PREFIX_VIRTUAL; $a = []; $j = \count($frames); if (0 > $i = $trace->sliceOffset) { $i = max(0, $j + $i); } if (!isset($trace->value[$i])) { return []; } $lastCall = isset($frames[$i]['function']) ? (isset($frames[$i]['class']) ? $frames[0]['class'].$frames[$i]['type'] : '').$frames[$i]['function'].'()' : ''; $frames[] = ['function' => '']; $collapse = false; for ($j += $trace->numberingOffset - $i++; isset($frames[$i]); ++$i, --$j) { $f = $frames[$i]; $call = isset($f['function']) ? (isset($f['class']) ? $f['class'].$f['type'] : '').$f['function'] : '???'; $frame = new FrameStub( [ 'object' => $f['object'] ?? null, 'class' => $f['class'] ?? null, 'type' => $f['type'] ?? null, 'function' => $f['function'] ?? null, ] + $frames[$i - 1], false, true ); $f = self::castFrameStub($frame, [], $frame, true); if (isset($f[$prefix.'src'])) { foreach ($f[$prefix.'src']->value as $label => $frame) { if (str_starts_with($label, "\0~collapse=0")) { if ($collapse) { $label = substr_replace($label, '1', 11, 1); } else { $collapse = true; } } $label = substr_replace($label, "title=Stack level $j.&", 2, 0); } $f = $frames[$i - 1]; if ($trace->keepArgs && !empty($f['args']) && $frame instanceof EnumStub) { $frame->value['arguments'] = new ArgsStub($f['args'], $f['function'] ?? null, $f['class'] ?? null); } } elseif ('???' !== $lastCall) { $label = new ClassStub($lastCall); if (isset($label->attr['ellipsis'])) { $label->attr['ellipsis'] += 2; $label = substr_replace($prefix, "ellipsis-type=class&ellipsis={$label->attr['ellipsis']}&ellipsis-tail=1&title=Stack level $j.", 2, 0).$label->value.'()'; } else { $label = substr_replace($prefix, "title=Stack level $j.", 2, 0).$label->value.'()'; } } else { $label = substr_replace($prefix, "title=Stack level $j.", 2, 0).$lastCall; } $a[substr_replace($label, \sprintf('separator=%s&', $frame instanceof EnumStub ? ' ' : ':'), 2, 0)] = $frame; $lastCall = $call; } if (null !== $trace->sliceLength) { $a = \array_slice($a, 0, $trace->sliceLength, true); } return $a; } public static function castFrameStub(FrameStub $frame, array $a, Stub $stub, bool $isNested): array { if (!$isNested) { return $a; } $f = $frame->value; $prefix = Caster::PREFIX_VIRTUAL; if (isset($f['file'], $f['line'])) { $cacheKey = $f; unset($cacheKey['object'], $cacheKey['args']); $cacheKey[] = self::$srcContext; $cacheKey = implode('-', $cacheKey); if (isset(self::$framesCache[$cacheKey])) { $a[$prefix.'src'] = self::$framesCache[$cacheKey]; } else { if (preg_match('/\((\d+)\)(?:\([\da-f]{32}\))? : (?:eval\(\)\'d code|runtime-created function)$/', $f['file'], $match)) { $f['file'] = substr($f['file'], 0, -\strlen($match[0])); $f['line'] = (int) $match[1]; } $src = $f['line']; $srcKey = $f['file']; $ellipsis = new LinkStub($srcKey, 0); $srcAttr = 'collapse='.(int) $ellipsis->inVendor; $ellipsisTail = $ellipsis->attr['ellipsis-tail'] ?? 0; $ellipsis = $ellipsis->attr['ellipsis'] ?? 0; if (is_file($f['file']) && 0 <= self::$srcContext) { if (!empty($f['class']) && is_subclass_of($f['class'], 'Twig\Template')) { $template = null; if (isset($f['object'])) { $template = $f['object']; } elseif ((new \ReflectionClass($f['class']))->isInstantiable()) { $template = unserialize(\sprintf('O:%d:"%s":0:{}', \strlen($f['class']), $f['class'])); } if (null !== $template) { $ellipsis = 0; $templateSrc = method_exists($template, 'getSourceContext') ? $template->getSourceContext()->getCode() : (method_exists($template, 'getSource') ? $template->getSource() : ''); $templateInfo = $template->getDebugInfo(); if (isset($templateInfo[$f['line']])) { if (!method_exists($template, 'getSourceContext') || !is_file($templatePath = $template->getSourceContext()->getPath())) { $templatePath = null; } if ($templateSrc) { $src = self::extractSource($templateSrc, $templateInfo[$f['line']], self::$srcContext, 'twig', $templatePath, $f); $srcKey = ($templatePath ?: $template->getTemplateName()).':'.$templateInfo[$f['line']]; } } } } if ($srcKey == $f['file']) { $src = self::extractSource(file_get_contents($f['file']), $f['line'], self::$srcContext, 'php', $f['file'], $f); $srcKey .= ':'.$f['line']; if ($ellipsis) { $ellipsis += 1 + \strlen($f['line']); } } $srcAttr .= \sprintf('&separator= &file=%s&line=%d', rawurlencode($f['file']), $f['line']); } else { $srcAttr .= '&separator=:'; } $srcAttr .= $ellipsis ? '&ellipsis-type=path&ellipsis='.$ellipsis.'&ellipsis-tail='.$ellipsisTail : ''; self::$framesCache[$cacheKey] = $a[$prefix.'src'] = new EnumStub(["\0~$srcAttr\0$srcKey" => $src]); } } unset($a[$prefix.'args'], $a[$prefix.'line'], $a[$prefix.'file']); if ($frame->inTraceStub) { unset($a[$prefix.'class'], $a[$prefix.'type'], $a[$prefix.'function']); } foreach ($a as $k => $v) { if (!$v) { unset($a[$k]); } } if ($frame->keepArgs && !empty($f['args'])) { $a[$prefix.'arguments'] = new ArgsStub($f['args'], $f['function'], $f['class']); } return $a; } public static function castFlattenException(FlattenException $e, array $a, Stub $stub, bool $isNested): array { if ($isNested) { $k = \sprintf(Caster::PATTERN_PRIVATE, FlattenException::class, 'traceAsString'); $a[$k] = new CutStub($a[$k]); } return $a; } private static function filterExceptionArray(string $xClass, array $a, string $xPrefix, int $filter): array { if (isset($a[$xPrefix.'trace'])) { $trace = $a[$xPrefix.'trace']; unset($a[$xPrefix.'trace']); // Ensures the trace is always last } else { $trace = []; } if (!($filter & Caster::EXCLUDE_VERBOSE) && $trace) { if (isset($a[Caster::PREFIX_PROTECTED.'file'], $a[Caster::PREFIX_PROTECTED.'line'])) { self::traceUnshift($trace, $xClass, $a[Caster::PREFIX_PROTECTED.'file'], $a[Caster::PREFIX_PROTECTED.'line']); } $a[Caster::PREFIX_VIRTUAL.'trace'] = new TraceStub($trace, self::$traceArgs); } if (empty($a[$xPrefix.'previous'])) { unset($a[$xPrefix.'previous']); } unset($a[$xPrefix.'string'], $a[Caster::PREFIX_DYNAMIC.'xdebug_message']); if (isset($a[Caster::PREFIX_PROTECTED.'message']) && str_contains($a[Caster::PREFIX_PROTECTED.'message'], "@anonymous\0")) { $a[Caster::PREFIX_PROTECTED.'message'] = preg_replace_callback('/[a-zA-Z_\x7f-\xff][\\\\a-zA-Z0-9_\x7f-\xff]*+@anonymous\x00.*?\.php(?:0x?|:[0-9]++\$)?[0-9a-fA-F]++/', static fn ($m) => class_exists($m[0], false) ? (get_parent_class($m[0]) ?: key(class_implements($m[0])) ?: 'class').'@anonymous' : $m[0], $a[Caster::PREFIX_PROTECTED.'message']); } if (isset($a[Caster::PREFIX_PROTECTED.'file'], $a[Caster::PREFIX_PROTECTED.'line'])) { $a[Caster::PREFIX_PROTECTED.'file'] = new LinkStub($a[Caster::PREFIX_PROTECTED.'file'], $a[Caster::PREFIX_PROTECTED.'line']); } return $a; } private static function traceUnshift(array &$trace, ?string $class, string $file, int $line): void { if (isset($trace[0]['file'], $trace[0]['line']) && $trace[0]['file'] === $file && $trace[0]['line'] === $line) { return; } array_unshift($trace, [ 'function' => $class ? 'new '.$class : null, 'file' => $file, 'line' => $line, ]); } private static function extractSource(string $srcLines, int $line, int $srcContext, string $lang, ?string $file, array $frame): EnumStub { $srcLines = explode("\n", $srcLines); $src = []; for ($i = $line - 1 - $srcContext; $i <= $line - 1 + $srcContext; ++$i) { $src[] = ($srcLines[$i] ?? '')."\n"; } if ($frame['function'] ?? false) { $stub = new CutStub(new \stdClass()); $stub->class = (isset($frame['class']) ? $frame['class'].$frame['type'] : '').$frame['function']; $stub->type = Stub::TYPE_OBJECT; $stub->attr['cut_hash'] = true; $stub->attr['file'] = $frame['file']; $stub->attr['line'] = $frame['line']; try { $caller = isset($frame['class']) ? new \ReflectionMethod($frame['class'], $frame['function']) : new \ReflectionFunction($frame['function']); $stub->class .= ReflectionCaster::getSignature(ReflectionCaster::castFunctionAbstract($caller, [], $stub, true, Caster::EXCLUDE_VERBOSE)); if ($f = $caller->getFileName()) { $stub->attr['file'] = $f; $stub->attr['line'] = $caller->getStartLine(); } } catch (\ReflectionException) { // ignore fake class/function } $srcLines = ["\0~separator=\0" => $stub]; } else { $stub = null; $srcLines = []; } $ltrim = 0; do { $pad = null; for ($i = $srcContext << 1; $i >= 0; --$i) { if (isset($src[$i][$ltrim]) && "\r" !== ($c = $src[$i][$ltrim]) && "\n" !== $c) { $pad ??= $c; if ((' ' !== $c && "\t" !== $c) || $pad !== $c) { break; } } } ++$ltrim; } while (0 > $i && null !== $pad); --$ltrim; foreach ($src as $i => $c) { if ($ltrim) { $c = isset($c[$ltrim]) && "\r" !== $c[$ltrim] ? substr($c, $ltrim) : ltrim($c, " \t"); } $c = substr($c, 0, -1); if ($i !== $srcContext) { $c = new ConstStub('default', $c); } else { $c = new ConstStub($c, $stub ? 'in '.$stub->class : ''); if (null !== $file) { $c->attr['file'] = $file; $c->attr['line'] = $line; } } $c->attr['lang'] = $lang; $srcLines[\sprintf("\0~separator=› &%d\0", $i + $line - $srcContext)] = $c; } return new EnumStub($srcLines); } } ================================================ FILE: Caster/FFICaster.php ================================================ * * 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 FFI\CData; use FFI\CType; use Symfony\Component\VarDumper\Cloner\Stub; /** * Casts FFI extension classes to array representation. * * @author Nesmeyanov Kirill */ final class FFICaster { /** * In case of "char*" contains a string, the length of which depends on * some other parameter, then during the generation of the string it is * possible to go beyond the allowable memory area. * * This restriction serves to ensure that processing does not take * up the entire allowable PHP memory limit. */ private const MAX_STRING_LENGTH = 255; public static function castCTypeOrCData(CData|CType $data, array $args, Stub $stub): array { if ($data instanceof CType) { $type = $data; $data = null; } else { $type = \FFI::typeof($data); } $stub->class = \sprintf('%s<%s> size %d align %d', ($data ?? $type)::class, $type->getName(), $type->getSize(), $type->getAlignment()); return match ($type->getKind()) { CType::TYPE_FLOAT, CType::TYPE_DOUBLE, \defined('\FFI\CType::TYPE_LONGDOUBLE') ? CType::TYPE_LONGDOUBLE : -1, CType::TYPE_UINT8, CType::TYPE_SINT8, CType::TYPE_UINT16, CType::TYPE_SINT16, CType::TYPE_UINT32, CType::TYPE_SINT32, CType::TYPE_UINT64, CType::TYPE_SINT64, CType::TYPE_BOOL, CType::TYPE_CHAR, CType::TYPE_ENUM => null !== $data ? [Caster::PREFIX_VIRTUAL.'cdata' => $data->cdata] : [], CType::TYPE_POINTER => self::castFFIPointer($stub, $type, $data), CType::TYPE_STRUCT => self::castFFIStructLike($type, $data), CType::TYPE_FUNC => self::castFFIFunction($stub, $type), default => $args, }; } private static function castFFIFunction(Stub $stub, CType $type): array { $arguments = []; for ($i = 0, $count = $type->getFuncParameterCount(); $i < $count; ++$i) { $param = $type->getFuncParameterType($i); $arguments[] = $param->getName(); } $abi = match ($type->getFuncABI()) { CType::ABI_DEFAULT, CType::ABI_CDECL => '[cdecl]', CType::ABI_FASTCALL => '[fastcall]', CType::ABI_THISCALL => '[thiscall]', CType::ABI_STDCALL => '[stdcall]', CType::ABI_PASCAL => '[pascal]', CType::ABI_REGISTER => '[register]', CType::ABI_MS => '[ms]', CType::ABI_SYSV => '[sysv]', CType::ABI_VECTORCALL => '[vectorcall]', default => '[unknown abi]', }; $returnType = $type->getFuncReturnType(); $stub->class = $abi.' callable('.implode(', ', $arguments).'): ' .$returnType->getName(); return [Caster::PREFIX_VIRTUAL.'returnType' => $returnType]; } private static function castFFIPointer(Stub $stub, CType $type, ?CData $data = null): array { $ptr = $type->getPointerType(); if (null === $data) { return [Caster::PREFIX_VIRTUAL.'0' => $ptr]; } return match ($ptr->getKind()) { CType::TYPE_CHAR => [Caster::PREFIX_VIRTUAL.'cdata' => self::castFFIStringValue($data)], CType::TYPE_FUNC => self::castFFIFunction($stub, $ptr), default => [Caster::PREFIX_VIRTUAL.'cdata' => $data[0]], }; } private static function castFFIStringValue(CData $data): string|CutStub { $result = []; $ffi = \FFI::cdef(<<zend_get_page_size(); // get cdata address $start = $ffi->cast('uintptr_t', $ffi->cast('char*', $data))->cdata; // accessing memory in the same page as $start is safe $max = min(self::MAX_STRING_LENGTH, ($start | ($pageSize - 1)) - $start); for ($i = 0; $i < $max; ++$i) { $result[$i] = $data[$i]; if ("\0" === $data[$i]) { return implode('', $result); } } $string = implode('', $result); $stub = new CutStub($string); $stub->cut = -1; $stub->value = $string; return $stub; } private static function castFFIStructLike(CType $type, ?CData $data = null): array { $isUnion = ($type->getAttributes() & CType::ATTR_UNION) === CType::ATTR_UNION; $result = []; foreach ($type->getStructFieldNames() as $name) { $field = $type->getStructFieldType($name); // Retrieving the value of a field from a union containing // a pointer is not a safe operation, because may contain // incorrect data. $isUnsafe = $isUnion && CType::TYPE_POINTER === $field->getKind(); if ($isUnsafe) { $result[Caster::PREFIX_VIRTUAL.$name.'?'] = $field; } elseif (null === $data) { $result[Caster::PREFIX_VIRTUAL.$name] = $field; } else { $fieldName = $data->{$name} instanceof CData ? '' : $field->getName().' '; $result[Caster::PREFIX_VIRTUAL.$fieldName.$name] = $data->{$name}; } } return $result; } } ================================================ FILE: Caster/FiberCaster.php ================================================ * * 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; /** * Casts Fiber related classes to array representation. * * @author Grégoire Pineau */ final class FiberCaster { public static function castFiber(\Fiber $fiber, array $a, Stub $stub, bool $isNested, int $filter = 0): array { $prefix = Caster::PREFIX_VIRTUAL; if ($fiber->isTerminated()) { $status = 'terminated'; } elseif ($fiber->isRunning()) { $status = 'running'; } elseif ($fiber->isSuspended()) { $status = 'suspended'; } elseif ($fiber->isStarted()) { $status = 'started'; } else { $status = 'not started'; } $a[$prefix.'status'] = $status; return $a; } } ================================================ FILE: Caster/FrameStub.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; /** * Represents a single backtrace frame as returned by debug_backtrace() or Exception->getTrace(). * * @author Nicolas Grekas */ class FrameStub extends EnumStub { public function __construct( array $frame, public bool $keepArgs = true, public bool $inTraceStub = false, ) { parent::__construct($frame); } } ================================================ FILE: Caster/GdCaster.php ================================================ * * 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; } } ================================================ FILE: Caster/GmpCaster.php ================================================ * * 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; /** * Casts GMP objects to array representation. * * @author Hamza Amrouche * @author Nicolas Grekas * * @final * * @internal */ class GmpCaster { public static function castGmp(\GMP $gmp, array $a, Stub $stub, bool $isNested, int $filter): array { $a[Caster::PREFIX_VIRTUAL.'value'] = new ConstStub(gmp_strval($gmp), gmp_strval($gmp)); return $a; } } ================================================ FILE: Caster/ImagineCaster.php ================================================ * * 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 Imagine\Image\ImageInterface; use Symfony\Component\VarDumper\Cloner\Stub; /** * @author Grégoire Pineau * * @internal */ final class ImagineCaster { public static function castImage(ImageInterface $c, array $a, Stub $stub, bool $isNested): array { $imgData = $c->get('png'); if (\strlen($imgData) > 1 * 1000 * 1000) { $a += [ Caster::PREFIX_VIRTUAL.'image' => new ConstStub($c->getSize()), ]; } else { $a += [ Caster::PREFIX_VIRTUAL.'image' => new ImgStub($imgData, 'image/png', $c->getSize()), ]; } return $a; } } ================================================ FILE: Caster/ImgStub.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; /** * @author Grégoire Pineau */ class ImgStub extends ConstStub { public function __construct(string $data, string $contentType, string $size = '') { $this->value = ''; $this->attr['img-data'] = $data; $this->attr['img-size'] = $size; $this->attr['content-type'] = $contentType; } } ================================================ FILE: Caster/IntlCaster.php ================================================ * * 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 Jan Schädlich * * @final * * @internal */ class IntlCaster { public static function castMessageFormatter(\MessageFormatter $c, array $a, Stub $stub, bool $isNested): array { $a += [ Caster::PREFIX_VIRTUAL.'locale' => $c->getLocale(), Caster::PREFIX_VIRTUAL.'pattern' => $c->getPattern(), ]; return self::castError($c, $a); } public static function castNumberFormatter(\NumberFormatter $c, array $a, Stub $stub, bool $isNested, int $filter = 0): array { $a += [ Caster::PREFIX_VIRTUAL.'locale' => $c->getLocale(), Caster::PREFIX_VIRTUAL.'pattern' => $c->getPattern(), ]; if ($filter & Caster::EXCLUDE_VERBOSE) { $stub->cut += 3; return self::castError($c, $a); } $a += [ Caster::PREFIX_VIRTUAL.'attributes' => new EnumStub( [ 'PARSE_INT_ONLY' => $c->getAttribute(\NumberFormatter::PARSE_INT_ONLY), 'GROUPING_USED' => $c->getAttribute(\NumberFormatter::GROUPING_USED), 'DECIMAL_ALWAYS_SHOWN' => $c->getAttribute(\NumberFormatter::DECIMAL_ALWAYS_SHOWN), 'MAX_INTEGER_DIGITS' => $c->getAttribute(\NumberFormatter::MAX_INTEGER_DIGITS), 'MIN_INTEGER_DIGITS' => $c->getAttribute(\NumberFormatter::MIN_INTEGER_DIGITS), 'INTEGER_DIGITS' => $c->getAttribute(\NumberFormatter::INTEGER_DIGITS), 'MAX_FRACTION_DIGITS' => $c->getAttribute(\NumberFormatter::MAX_FRACTION_DIGITS), 'MIN_FRACTION_DIGITS' => $c->getAttribute(\NumberFormatter::MIN_FRACTION_DIGITS), 'FRACTION_DIGITS' => $c->getAttribute(\NumberFormatter::FRACTION_DIGITS), 'MULTIPLIER' => $c->getAttribute(\NumberFormatter::MULTIPLIER), 'GROUPING_SIZE' => $c->getAttribute(\NumberFormatter::GROUPING_SIZE), 'ROUNDING_MODE' => $c->getAttribute(\NumberFormatter::ROUNDING_MODE), 'ROUNDING_INCREMENT' => $c->getAttribute(\NumberFormatter::ROUNDING_INCREMENT), 'FORMAT_WIDTH' => $c->getAttribute(\NumberFormatter::FORMAT_WIDTH), 'PADDING_POSITION' => $c->getAttribute(\NumberFormatter::PADDING_POSITION), 'SECONDARY_GROUPING_SIZE' => $c->getAttribute(\NumberFormatter::SECONDARY_GROUPING_SIZE), 'SIGNIFICANT_DIGITS_USED' => $c->getAttribute(\NumberFormatter::SIGNIFICANT_DIGITS_USED), 'MIN_SIGNIFICANT_DIGITS' => $c->getAttribute(\NumberFormatter::MIN_SIGNIFICANT_DIGITS), 'MAX_SIGNIFICANT_DIGITS' => $c->getAttribute(\NumberFormatter::MAX_SIGNIFICANT_DIGITS), 'LENIENT_PARSE' => $c->getAttribute(\NumberFormatter::LENIENT_PARSE), ] ), Caster::PREFIX_VIRTUAL.'text_attributes' => new EnumStub( [ 'POSITIVE_PREFIX' => $c->getTextAttribute(\NumberFormatter::POSITIVE_PREFIX), 'POSITIVE_SUFFIX' => $c->getTextAttribute(\NumberFormatter::POSITIVE_SUFFIX), 'NEGATIVE_PREFIX' => $c->getTextAttribute(\NumberFormatter::NEGATIVE_PREFIX), 'NEGATIVE_SUFFIX' => $c->getTextAttribute(\NumberFormatter::NEGATIVE_SUFFIX), 'PADDING_CHARACTER' => $c->getTextAttribute(\NumberFormatter::PADDING_CHARACTER), 'CURRENCY_CODE' => $c->getTextAttribute(\NumberFormatter::CURRENCY_CODE), 'DEFAULT_RULESET' => $c->getTextAttribute(\NumberFormatter::DEFAULT_RULESET), 'PUBLIC_RULESETS' => $c->getTextAttribute(\NumberFormatter::PUBLIC_RULESETS), ] ), Caster::PREFIX_VIRTUAL.'symbols' => new EnumStub( [ 'DECIMAL_SEPARATOR_SYMBOL' => $c->getSymbol(\NumberFormatter::DECIMAL_SEPARATOR_SYMBOL), 'GROUPING_SEPARATOR_SYMBOL' => $c->getSymbol(\NumberFormatter::GROUPING_SEPARATOR_SYMBOL), 'PATTERN_SEPARATOR_SYMBOL' => $c->getSymbol(\NumberFormatter::PATTERN_SEPARATOR_SYMBOL), 'PERCENT_SYMBOL' => $c->getSymbol(\NumberFormatter::PERCENT_SYMBOL), 'ZERO_DIGIT_SYMBOL' => $c->getSymbol(\NumberFormatter::ZERO_DIGIT_SYMBOL), 'DIGIT_SYMBOL' => $c->getSymbol(\NumberFormatter::DIGIT_SYMBOL), 'MINUS_SIGN_SYMBOL' => $c->getSymbol(\NumberFormatter::MINUS_SIGN_SYMBOL), 'PLUS_SIGN_SYMBOL' => $c->getSymbol(\NumberFormatter::PLUS_SIGN_SYMBOL), 'CURRENCY_SYMBOL' => $c->getSymbol(\NumberFormatter::CURRENCY_SYMBOL), 'INTL_CURRENCY_SYMBOL' => $c->getSymbol(\NumberFormatter::INTL_CURRENCY_SYMBOL), 'MONETARY_SEPARATOR_SYMBOL' => $c->getSymbol(\NumberFormatter::MONETARY_SEPARATOR_SYMBOL), 'EXPONENTIAL_SYMBOL' => $c->getSymbol(\NumberFormatter::EXPONENTIAL_SYMBOL), 'PERMILL_SYMBOL' => $c->getSymbol(\NumberFormatter::PERMILL_SYMBOL), 'PAD_ESCAPE_SYMBOL' => $c->getSymbol(\NumberFormatter::PAD_ESCAPE_SYMBOL), 'INFINITY_SYMBOL' => $c->getSymbol(\NumberFormatter::INFINITY_SYMBOL), 'NAN_SYMBOL' => $c->getSymbol(\NumberFormatter::NAN_SYMBOL), 'SIGNIFICANT_DIGIT_SYMBOL' => $c->getSymbol(\NumberFormatter::SIGNIFICANT_DIGIT_SYMBOL), 'MONETARY_GROUPING_SEPARATOR_SYMBOL' => $c->getSymbol(\NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL), ] ), ]; return self::castError($c, $a); } public static function castIntlTimeZone(\IntlTimeZone $c, array $a, Stub $stub, bool $isNested): array { $a += [ Caster::PREFIX_VIRTUAL.'display_name' => $c->getDisplayName(), Caster::PREFIX_VIRTUAL.'id' => $c->getID(), Caster::PREFIX_VIRTUAL.'raw_offset' => $c->getRawOffset(), ]; if ($c->useDaylightTime()) { $a += [ Caster::PREFIX_VIRTUAL.'dst_savings' => $c->getDSTSavings(), ]; } return self::castError($c, $a); } public static function castIntlCalendar(\IntlCalendar $c, array $a, Stub $stub, bool $isNested, int $filter = 0): array { $a += [ Caster::PREFIX_VIRTUAL.'type' => $c->getType(), Caster::PREFIX_VIRTUAL.'first_day_of_week' => $c->getFirstDayOfWeek(), Caster::PREFIX_VIRTUAL.'minimal_days_in_first_week' => $c->getMinimalDaysInFirstWeek(), Caster::PREFIX_VIRTUAL.'repeated_wall_time_option' => $c->getRepeatedWallTimeOption(), Caster::PREFIX_VIRTUAL.'skipped_wall_time_option' => $c->getSkippedWallTimeOption(), Caster::PREFIX_VIRTUAL.'time' => $c->getTime(), Caster::PREFIX_VIRTUAL.'in_daylight_time' => $c->inDaylightTime(), Caster::PREFIX_VIRTUAL.'is_lenient' => $c->isLenient(), Caster::PREFIX_VIRTUAL.'time_zone' => ($filter & Caster::EXCLUDE_VERBOSE) ? new CutStub($c->getTimeZone()) : $c->getTimeZone(), ]; return self::castError($c, $a); } public static function castIntlDateFormatter(\IntlDateFormatter $c, array $a, Stub $stub, bool $isNested, int $filter = 0): array { $a += [ Caster::PREFIX_VIRTUAL.'locale' => $c->getLocale(), Caster::PREFIX_VIRTUAL.'pattern' => $c->getPattern(), Caster::PREFIX_VIRTUAL.'calendar' => $c->getCalendar(), Caster::PREFIX_VIRTUAL.'time_zone_id' => $c->getTimeZoneId(), Caster::PREFIX_VIRTUAL.'time_type' => $c->getTimeType(), Caster::PREFIX_VIRTUAL.'date_type' => $c->getDateType(), Caster::PREFIX_VIRTUAL.'calendar_object' => ($filter & Caster::EXCLUDE_VERBOSE) ? new CutStub($c->getCalendarObject()) : $c->getCalendarObject(), Caster::PREFIX_VIRTUAL.'time_zone' => ($filter & Caster::EXCLUDE_VERBOSE) ? new CutStub($c->getTimeZone()) : $c->getTimeZone(), ]; return self::castError($c, $a); } private static function castError(object $c, array $a): array { if ($errorCode = $c->getErrorCode()) { $a += [ Caster::PREFIX_VIRTUAL.'error_code' => $errorCode, Caster::PREFIX_VIRTUAL.'error_message' => $c->getErrorMessage(), ]; } return $a; } } ================================================ FILE: Caster/LinkStub.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; /** * Represents a file or a URL. * * @author Nicolas Grekas */ class LinkStub extends ConstStub { public bool $inVendor = false; private static array $vendorRoots; private static array $composerRoots = []; public function __construct(string $label, int $line = 0, ?string $href = null) { $this->value = $label; if (!\is_string($href ??= $label)) { return; } if (str_starts_with($href, 'file://')) { if ($href === $label) { $label = substr($label, 7); } $href = substr($href, 7); } elseif (str_contains($href, '://')) { $this->attr['href'] = $href; return; } if (!is_file($href)) { return; } if ($line) { $this->attr['line'] = $line; } if ($label !== $this->attr['file'] = realpath($href) ?: $href) { return; } if ($composerRoot = $this->getComposerRoot($href, $this->inVendor)) { $this->attr['ellipsis'] = \strlen($href) - \strlen($composerRoot) + 1; $this->attr['ellipsis-type'] = 'path'; $this->attr['ellipsis-tail'] = 1 + ($this->inVendor ? 2 + \strlen(implode('', \array_slice(explode(\DIRECTORY_SEPARATOR, substr($href, 1 - $this->attr['ellipsis'])), 0, 2))) : 0); } elseif (3 < \count($ellipsis = explode(\DIRECTORY_SEPARATOR, $href))) { $this->attr['ellipsis'] = 2 + \strlen(implode('', \array_slice($ellipsis, -2))); $this->attr['ellipsis-type'] = 'path'; $this->attr['ellipsis-tail'] = 1; } } private function getComposerRoot(string $file, bool &$inVendor): string|false { if (!isset(self::$vendorRoots)) { self::$vendorRoots = []; foreach (get_declared_classes() as $class) { if ('C' === $class[0] && str_starts_with($class, 'ComposerAutoloaderInit')) { $r = new \ReflectionClass($class); $v = \dirname($r->getFileName(), 2); if (is_file($v.'/composer/installed.json')) { self::$vendorRoots[] = $v.\DIRECTORY_SEPARATOR; } } } } $inVendor = false; if (isset(self::$composerRoots[$dir = \dirname($file)])) { return self::$composerRoots[$dir]; } foreach (self::$vendorRoots as $root) { if ($inVendor = str_starts_with($file, $root)) { return $root; } } $parent = $dir; while (!@is_file($parent.'/composer.json')) { if (!@file_exists($parent)) { // open_basedir restriction in effect break; } if ($parent === \dirname($parent)) { return self::$composerRoots[$dir] = false; } $parent = \dirname($parent); } return self::$composerRoots[$dir] = $parent.\DIRECTORY_SEPARATOR; } } ================================================ FILE: Caster/MemcachedCaster.php ================================================ * * 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 Jan Schädlich * * @final * * @internal */ class MemcachedCaster { private static array $optionConstants; private static array $defaultOptions; public static function castMemcached(\Memcached $c, array $a, Stub $stub, bool $isNested): array { $a += [ Caster::PREFIX_VIRTUAL.'servers' => $c->getServerList(), Caster::PREFIX_VIRTUAL.'options' => new EnumStub( self::getNonDefaultOptions($c) ), ]; return $a; } private static function getNonDefaultOptions(\Memcached $c): array { self::$defaultOptions ??= self::discoverDefaultOptions(); self::$optionConstants ??= self::getOptionConstants(); $nonDefaultOptions = []; foreach (self::$optionConstants as $constantKey => $value) { if (self::$defaultOptions[$constantKey] !== $option = $c->getOption($value)) { $nonDefaultOptions[$constantKey] = $option; } } return $nonDefaultOptions; } private static function discoverDefaultOptions(): array { $defaultMemcached = new \Memcached(); $defaultMemcached->addServer('127.0.0.1', 11211); $defaultOptions = []; self::$optionConstants ??= self::getOptionConstants(); foreach (self::$optionConstants as $constantKey => $value) { $defaultOptions[$constantKey] = $defaultMemcached->getOption($value); } return $defaultOptions; } private static function getOptionConstants(): array { $reflectedMemcached = new \ReflectionClass(\Memcached::class); $optionConstants = []; foreach ($reflectedMemcached->getConstants() as $constantKey => $value) { if (str_starts_with($constantKey, 'OPT_')) { $optionConstants[$constantKey] = $value; } } return $optionConstants; } } ================================================ FILE: Caster/MysqliCaster.php ================================================ * * 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 MysqliCaster { public static function castMysqliDriver(\mysqli_driver $c, array $a, Stub $stub, bool $isNested): array { foreach ($a as $k => $v) { if (isset($c->$k)) { $a[$k] = $c->$k; } } return $a; } } ================================================ FILE: Caster/OpenSSLCaster.php ================================================ * * 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 * * @internal */ final class OpenSSLCaster { 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 += [ Caster::PREFIX_VIRTUAL.'subject' => new EnumStub(array_intersect_key($info['subject'], ['organizationName' => true, 'commonName' => true])), Caster::PREFIX_VIRTUAL.'issuer' => new EnumStub(array_intersect_key($info['issuer'], ['organizationName' => true, 'commonName' => true])), Caster::PREFIX_VIRTUAL.'expiry' => new ConstStub(date(\DateTimeInterface::ISO8601, $info['validTo_time_t']), $info['validTo_time_t']), Caster::PREFIX_VIRTUAL.'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), ]), ]; return $a; } public static function castOpensslAsymmetricKey(\OpenSSLAsymmetricKey $key, array $a, Stub $stub, bool $isNested): array { foreach (openssl_pkey_get_details($key) as $k => $v) { $a[Caster::PREFIX_VIRTUAL.$k] = $v; } unset($a[Caster::PREFIX_VIRTUAL.'rsa']); // binary data return $a; } public static function castOpensslCsr(\OpenSSLCertificateSigningRequest $csr, array $a, Stub $stub, bool $isNested): array { foreach (openssl_csr_get_subject($csr, false) as $k => $v) { $a[Caster::PREFIX_VIRTUAL.$k] = $v; } return $a; } } ================================================ FILE: Caster/PdoCaster.php ================================================ * * 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; /** * Casts PDO related classes to array representation. * * @author Nicolas Grekas * * @final * * @internal */ class PdoCaster { private const PDO_ATTRIBUTES = [ 'CASE' => [ \PDO::CASE_LOWER => 'LOWER', \PDO::CASE_NATURAL => 'NATURAL', \PDO::CASE_UPPER => 'UPPER', ], 'ERRMODE' => [ \PDO::ERRMODE_SILENT => 'SILENT', \PDO::ERRMODE_WARNING => 'WARNING', \PDO::ERRMODE_EXCEPTION => 'EXCEPTION', ], 'TIMEOUT', 'PREFETCH', 'AUTOCOMMIT', 'PERSISTENT', 'DRIVER_NAME', 'SERVER_INFO', 'ORACLE_NULLS' => [ \PDO::NULL_NATURAL => 'NATURAL', \PDO::NULL_EMPTY_STRING => 'EMPTY_STRING', \PDO::NULL_TO_STRING => 'TO_STRING', ], 'CLIENT_VERSION', 'SERVER_VERSION', 'STATEMENT_CLASS', 'EMULATE_PREPARES', 'CONNECTION_STATUS', 'STRINGIFY_FETCHES', 'DEFAULT_FETCH_MODE' => [ \PDO::FETCH_ASSOC => 'ASSOC', \PDO::FETCH_BOTH => 'BOTH', \PDO::FETCH_LAZY => 'LAZY', \PDO::FETCH_NUM => 'NUM', \PDO::FETCH_OBJ => 'OBJ', ], ]; public static function castPdo(\PDO $c, array $a, Stub $stub, bool $isNested): array { $attr = []; $errmode = $c->getAttribute(\PDO::ATTR_ERRMODE); $c->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); foreach (self::PDO_ATTRIBUTES as $k => $v) { if (!isset($k[0])) { $k = $v; $v = []; } try { $attr[$k] = 'ERRMODE' === $k ? $errmode : $c->getAttribute(\constant('PDO::ATTR_'.$k)); if ($v && isset($v[$attr[$k]])) { $attr[$k] = new ConstStub($v[$attr[$k]], $attr[$k]); } } catch (\Exception) { } } if (isset($attr[$k = 'STATEMENT_CLASS'][1])) { if ($attr[$k][1]) { $attr[$k][1] = new ArgsStub($attr[$k][1], '__construct', $attr[$k][0]); } $attr[$k][0] = new ClassStub($attr[$k][0]); } $prefix = Caster::PREFIX_VIRTUAL; $a += [ $prefix.'inTransaction' => method_exists($c, 'inTransaction'), $prefix.'errorInfo' => $c->errorInfo(), $prefix.'attributes' => new EnumStub($attr), ]; if ($a[$prefix.'inTransaction']) { $a[$prefix.'inTransaction'] = $c->inTransaction(); } else { unset($a[$prefix.'inTransaction']); } if (!isset($a[$prefix.'errorInfo'][1], $a[$prefix.'errorInfo'][2])) { unset($a[$prefix.'errorInfo']); } $c->setAttribute(\PDO::ATTR_ERRMODE, $errmode); return $a; } public static function castPdoStatement(\PDOStatement $c, array $a, Stub $stub, bool $isNested): array { $prefix = Caster::PREFIX_VIRTUAL; $a[$prefix.'errorInfo'] = $c->errorInfo(); if (!isset($a[$prefix.'errorInfo'][1], $a[$prefix.'errorInfo'][2])) { unset($a[$prefix.'errorInfo']); } return $a; } } ================================================ FILE: Caster/PgSqlCaster.php ================================================ * * 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; /** * Casts pgsql resources to array representation. * * @author Nicolas Grekas * * @final * * @internal */ class PgSqlCaster { private const PARAM_CODES = [ 'server_encoding', 'client_encoding', 'is_superuser', 'session_authorization', 'DateStyle', 'TimeZone', 'IntervalStyle', 'integer_datetimes', 'application_name', 'standard_conforming_strings', ]; private const TRANSACTION_STATUS = [ \PGSQL_TRANSACTION_IDLE => 'PGSQL_TRANSACTION_IDLE', \PGSQL_TRANSACTION_ACTIVE => 'PGSQL_TRANSACTION_ACTIVE', \PGSQL_TRANSACTION_INTRANS => 'PGSQL_TRANSACTION_INTRANS', \PGSQL_TRANSACTION_INERROR => 'PGSQL_TRANSACTION_INERROR', \PGSQL_TRANSACTION_UNKNOWN => 'PGSQL_TRANSACTION_UNKNOWN', ]; private const RESULT_STATUS = [ \PGSQL_EMPTY_QUERY => 'PGSQL_EMPTY_QUERY', \PGSQL_COMMAND_OK => 'PGSQL_COMMAND_OK', \PGSQL_TUPLES_OK => 'PGSQL_TUPLES_OK', \PGSQL_COPY_OUT => 'PGSQL_COPY_OUT', \PGSQL_COPY_IN => 'PGSQL_COPY_IN', \PGSQL_BAD_RESPONSE => 'PGSQL_BAD_RESPONSE', \PGSQL_NONFATAL_ERROR => 'PGSQL_NONFATAL_ERROR', \PGSQL_FATAL_ERROR => 'PGSQL_FATAL_ERROR', ]; private const DIAG_CODES = [ 'severity' => \PGSQL_DIAG_SEVERITY, 'sqlstate' => \PGSQL_DIAG_SQLSTATE, 'message' => \PGSQL_DIAG_MESSAGE_PRIMARY, 'detail' => \PGSQL_DIAG_MESSAGE_DETAIL, 'hint' => \PGSQL_DIAG_MESSAGE_HINT, 'statement position' => \PGSQL_DIAG_STATEMENT_POSITION, 'internal position' => \PGSQL_DIAG_INTERNAL_POSITION, 'internal query' => \PGSQL_DIAG_INTERNAL_QUERY, 'context' => \PGSQL_DIAG_CONTEXT, 'file' => \PGSQL_DIAG_SOURCE_FILE, 'line' => \PGSQL_DIAG_SOURCE_LINE, 'function' => \PGSQL_DIAG_SOURCE_FUNCTION, ]; public static function castLargeObject($lo, array $a, Stub $stub, bool $isNested): array { $a['seek position'] = pg_lo_tell($lo); return $a; } public static function castLink($link, array $a, Stub $stub, bool $isNested): array { $a['status'] = pg_connection_status($link); $a['status'] = new ConstStub(\PGSQL_CONNECTION_OK === $a['status'] ? 'PGSQL_CONNECTION_OK' : 'PGSQL_CONNECTION_BAD', $a['status']); $a['busy'] = pg_connection_busy($link); $a['transaction'] = pg_transaction_status($link); if (isset(self::TRANSACTION_STATUS[$a['transaction']])) { $a['transaction'] = new ConstStub(self::TRANSACTION_STATUS[$a['transaction']], $a['transaction']); } $a['pid'] = pg_get_pid($link); $a['last error'] = pg_last_error($link); $a['last notice'] = pg_last_notice($link); $a['host'] = pg_host($link); $a['port'] = pg_port($link); $a['dbname'] = pg_dbname($link); $a['options'] = pg_options($link); $a['version'] = pg_version($link); foreach (self::PARAM_CODES as $v) { if (false !== $s = pg_parameter_status($link, $v)) { $a['param'][$v] = $s; } } $a['param']['client_encoding'] = pg_client_encoding($link); $a['param'] = new EnumStub($a['param']); return $a; } public static function castResult($result, array $a, Stub $stub, bool $isNested): array { $a['num rows'] = pg_num_rows($result); $a['status'] = pg_result_status($result); if (isset(self::RESULT_STATUS[$a['status']])) { $a['status'] = new ConstStub(self::RESULT_STATUS[$a['status']], $a['status']); } $a['command-completion tag'] = pg_result_status($result, \PGSQL_STATUS_STRING); if (-1 === $a['num rows']) { foreach (self::DIAG_CODES as $k => $v) { $a['error'][$k] = pg_result_error_field($result, $v); } } $a['affected rows'] = pg_affected_rows($result); $a['last OID'] = pg_last_oid($result); $fields = pg_num_fields($result); for ($i = 0; $i < $fields; ++$i) { $field = [ 'name' => pg_field_name($result, $i), 'table' => \sprintf('%s (OID: %s)', pg_field_table($result, $i), pg_field_table($result, $i, true)), 'type' => \sprintf('%s (OID: %s)', pg_field_type($result, $i), pg_field_type_oid($result, $i)), 'nullable' => (bool) pg_field_is_null($result, null, $i), 'storage' => pg_field_size($result, $i).' bytes', 'display' => pg_field_prtlen($result, null, $i).' chars', ]; if (' (OID: )' === $field['table']) { $field['table'] = null; } if ('-1 bytes' === $field['storage']) { $field['storage'] = 'variable size'; } elseif ('1 bytes' === $field['storage']) { $field['storage'] = '1 byte'; } if ('1 chars' === $field['display']) { $field['display'] = '1 char'; } $a['fields'][] = new EnumStub($field); } return $a; } } ================================================ FILE: Caster/ProxyManagerCaster.php ================================================ * * 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 ProxyManager\Proxy\ProxyInterface; use Symfony\Component\VarDumper\Cloner\Stub; /** * @author Nicolas Grekas * * @final * * @internal */ class ProxyManagerCaster { public static function castProxy(ProxyInterface $c, array $a, Stub $stub, bool $isNested): array { if ($parent = get_parent_class($c)) { $stub->class .= ' - '.$parent; } $stub->class .= '@proxy'; return $a; } } ================================================ FILE: Caster/RdKafkaCaster.php ================================================ * * 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 RdKafka\Conf; use RdKafka\Exception as RdKafkaException; use RdKafka\KafkaConsumer; use RdKafka\Message; use RdKafka\Metadata\Broker as BrokerMetadata; use RdKafka\Metadata\Collection as CollectionMetadata; use RdKafka\Metadata\Partition as PartitionMetadata; use RdKafka\Metadata\Topic as TopicMetadata; use RdKafka\Topic; use RdKafka\TopicConf; use RdKafka\TopicPartition; use Symfony\Component\VarDumper\Cloner\Stub; /** * Casts RdKafka related classes to array representation. * * @author Romain Neutron * * @internal */ class RdKafkaCaster { public static function castKafkaConsumer(KafkaConsumer $c, array $a, Stub $stub, bool $isNested): array { $prefix = Caster::PREFIX_VIRTUAL; try { $assignment = $c->getAssignment(); } catch (RdKafkaException) { $assignment = []; } $a += [ $prefix.'subscription' => $c->getSubscription(), $prefix.'assignment' => $assignment, ]; $a += self::extractMetadata($c); return $a; } public static function castTopic(Topic $c, array $a, Stub $stub, bool $isNested): array { $prefix = Caster::PREFIX_VIRTUAL; $a += [ $prefix.'name' => $c->getName(), ]; return $a; } public static function castTopicPartition(TopicPartition $c, array $a): array { $prefix = Caster::PREFIX_VIRTUAL; $a += [ $prefix.'offset' => $c->getOffset(), $prefix.'partition' => $c->getPartition(), $prefix.'topic' => $c->getTopic(), ]; return $a; } public static function castMessage(Message $c, array $a, Stub $stub, bool $isNested): array { $prefix = Caster::PREFIX_VIRTUAL; $a += [ $prefix.'errstr' => $c->errstr(), ]; return $a; } public static function castConf(Conf $c, array $a, Stub $stub, bool $isNested): array { $prefix = Caster::PREFIX_VIRTUAL; foreach ($c->dump() as $key => $value) { $a[$prefix.$key] = $value; } return $a; } public static function castTopicConf(TopicConf $c, array $a, Stub $stub, bool $isNested): array { $prefix = Caster::PREFIX_VIRTUAL; foreach ($c->dump() as $key => $value) { $a[$prefix.$key] = $value; } return $a; } public static function castRdKafka(\RdKafka $c, array $a, Stub $stub, bool $isNested): array { $prefix = Caster::PREFIX_VIRTUAL; $a += [ $prefix.'out_q_len' => $c->getOutQLen(), ]; $a += self::extractMetadata($c); return $a; } public static function castCollectionMetadata(CollectionMetadata $c, array $a, Stub $stub, bool $isNested): array { $a += iterator_to_array($c); return $a; } public static function castTopicMetadata(TopicMetadata $c, array $a, Stub $stub, bool $isNested): array { $prefix = Caster::PREFIX_VIRTUAL; $a += [ $prefix.'name' => $c->getTopic(), $prefix.'partitions' => $c->getPartitions(), ]; return $a; } public static function castPartitionMetadata(PartitionMetadata $c, array $a, Stub $stub, bool $isNested): array { $prefix = Caster::PREFIX_VIRTUAL; $a += [ $prefix.'id' => $c->getId(), $prefix.'err' => $c->getErr(), $prefix.'leader' => $c->getLeader(), ]; return $a; } public static function castBrokerMetadata(BrokerMetadata $c, array $a, Stub $stub, bool $isNested): array { $prefix = Caster::PREFIX_VIRTUAL; $a += [ $prefix.'id' => $c->getId(), $prefix.'host' => $c->getHost(), $prefix.'port' => $c->getPort(), ]; return $a; } private static function extractMetadata(KafkaConsumer|\RdKafka $c): array { $prefix = Caster::PREFIX_VIRTUAL; try { $m = $c->getMetadata(true, null, 500); } catch (RdKafkaException) { return []; } return [ $prefix.'orig_broker_id' => $m->getOrigBrokerId(), $prefix.'orig_broker_name' => $m->getOrigBrokerName(), $prefix.'brokers' => $m->getBrokers(), $prefix.'topics' => $m->getTopics(), ]; } } ================================================ FILE: Caster/RedisCaster.php ================================================ * * 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 Relay\Relay; use Symfony\Component\VarDumper\Cloner\Stub; /** * Casts Redis class from ext-redis to array representation. * * @author Nicolas Grekas * * @final * * @internal */ class RedisCaster { private const SERIALIZERS = [ 0 => 'NONE', // Redis::SERIALIZER_NONE 1 => 'PHP', // Redis::SERIALIZER_PHP 2 => 'IGBINARY', // Optional Redis::SERIALIZER_IGBINARY ]; private const MODES = [ 0 => 'ATOMIC', // Redis::ATOMIC 1 => 'MULTI', // Redis::MULTI 2 => 'PIPELINE', // Redis::PIPELINE ]; private const COMPRESSION_MODES = [ 0 => 'NONE', // Redis::COMPRESSION_NONE 1 => 'LZF', // Redis::COMPRESSION_LZF ]; private const FAILOVER_OPTIONS = [ \RedisCluster::FAILOVER_NONE => 'NONE', \RedisCluster::FAILOVER_ERROR => 'ERROR', \RedisCluster::FAILOVER_DISTRIBUTE => 'DISTRIBUTE', \RedisCluster::FAILOVER_DISTRIBUTE_SLAVES => 'DISTRIBUTE_SLAVES', ]; public static function castRedis(\Redis|Relay $c, array $a, Stub $stub, bool $isNested): array { $prefix = Caster::PREFIX_VIRTUAL; if (!$connected = $c->isConnected()) { return $a + [ $prefix.'isConnected' => $connected, ]; } $mode = $c->getMode(); return $a + [ $prefix.'isConnected' => $connected, $prefix.'host' => $c->getHost(), $prefix.'port' => $c->getPort(), $prefix.'auth' => $c->getAuth(), $prefix.'mode' => isset(self::MODES[$mode]) ? new ConstStub(self::MODES[$mode], $mode) : $mode, $prefix.'dbNum' => $c->getDbNum(), $prefix.'timeout' => $c->getTimeout(), $prefix.'lastError' => $c->getLastError(), $prefix.'persistentId' => $c->getPersistentID(), $prefix.'options' => self::getRedisOptions($c), ]; } public static function castRedisArray(\RedisArray $c, array $a, Stub $stub, bool $isNested): array { $prefix = Caster::PREFIX_VIRTUAL; return $a + [ $prefix.'hosts' => $c->_hosts(), $prefix.'function' => ClassStub::wrapCallable($c->_function()), $prefix.'lastError' => $c->getLastError(), $prefix.'options' => self::getRedisOptions($c), ]; } public static function castRedisCluster(\RedisCluster $c, array $a, Stub $stub, bool $isNested): array { $prefix = Caster::PREFIX_VIRTUAL; $failover = $c->getOption(\RedisCluster::OPT_SLAVE_FAILOVER); $a += [ $prefix.'_masters' => $c->_masters(), $prefix.'_redir' => $c->_redir(), $prefix.'mode' => new ConstStub($c->getMode() ? 'MULTI' : 'ATOMIC', $c->getMode()), $prefix.'lastError' => $c->getLastError(), $prefix.'options' => self::getRedisOptions($c, [ 'SLAVE_FAILOVER' => isset(self::FAILOVER_OPTIONS[$failover]) ? new ConstStub(self::FAILOVER_OPTIONS[$failover], $failover) : $failover, ]), ]; return $a; } private static function getRedisOptions(\Redis|Relay|\RedisArray|\RedisCluster $redis, array $options = []): EnumStub { $serializer = $redis->getOption(\defined('Redis::OPT_SERIALIZER') ? \Redis::OPT_SERIALIZER : 1); if (\is_array($serializer)) { foreach ($serializer as &$v) { if (isset(self::SERIALIZERS[$v])) { $v = new ConstStub(self::SERIALIZERS[$v], $v); } } } elseif (isset(self::SERIALIZERS[$serializer])) { $serializer = new ConstStub(self::SERIALIZERS[$serializer], $serializer); } $compression = \defined('Redis::OPT_COMPRESSION') ? $redis->getOption(\Redis::OPT_COMPRESSION) : 0; if (\is_array($compression)) { foreach ($compression as &$v) { if (isset(self::COMPRESSION_MODES[$v])) { $v = new ConstStub(self::COMPRESSION_MODES[$v], $v); } } } elseif (isset(self::COMPRESSION_MODES[$compression])) { $compression = new ConstStub(self::COMPRESSION_MODES[$compression], $compression); } $retry = \defined('Redis::OPT_SCAN') ? $redis->getOption(\Redis::OPT_SCAN) : 0; if (\is_array($retry)) { foreach ($retry as &$v) { $v = new ConstStub($v ? 'RETRY' : 'NORETRY', $v); } } else { $retry = new ConstStub($retry ? 'RETRY' : 'NORETRY', $retry); } $options += [ 'TCP_KEEPALIVE' => \defined('Redis::OPT_TCP_KEEPALIVE') ? $redis->getOption(\Redis::OPT_TCP_KEEPALIVE) : Relay::OPT_TCP_KEEPALIVE, 'READ_TIMEOUT' => $redis->getOption(\defined('Redis::OPT_READ_TIMEOUT') ? \Redis::OPT_READ_TIMEOUT : Relay::OPT_READ_TIMEOUT), 'COMPRESSION' => $compression, 'SERIALIZER' => $serializer, 'PREFIX' => $redis->getOption(\defined('Redis::OPT_PREFIX') ? \Redis::OPT_PREFIX : Relay::OPT_PREFIX), 'SCAN' => $retry, ]; return new EnumStub($options); } } ================================================ FILE: Caster/ReflectionCaster.php ================================================ * * 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; /** * Casts Reflector related classes to array representation. * * @author Nicolas Grekas * * @final * * @internal */ class ReflectionCaster { public const UNSET_CLOSURE_FILE_INFO = ['Closure' => __CLASS__.'::unsetClosureFileInfo']; private const EXTRA_MAP = [ 'docComment' => 'getDocComment', 'extension' => 'getExtensionName', 'isDisabled' => 'isDisabled', 'isDeprecated' => 'isDeprecated', 'isInternal' => 'isInternal', 'isUserDefined' => 'isUserDefined', 'isGenerator' => 'isGenerator', 'isVariadic' => 'isVariadic', ]; public static function castClosure(\Closure $c, array $a, Stub $stub, bool $isNested, int $filter = 0): array { $prefix = Caster::PREFIX_VIRTUAL; $c = new \ReflectionFunction($c); $a = static::castFunctionAbstract($c, $a, $stub, $isNested, $filter); if (!$c->isAnonymous()) { $stub->class = isset($a[$prefix.'class']) ? $a[$prefix.'class']->value.'::'.$c->name : $c->name; unset($a[$prefix.'class']); } unset($a[$prefix.'extra']); $stub->class .= self::getSignature($a); if ($f = $c->getFileName()) { $stub->attr['file'] = $f; $stub->attr['line'] = $c->getStartLine(); } unset($a[$prefix.'parameters']); if ($filter & Caster::EXCLUDE_VERBOSE) { $stub->cut += ($c->getFileName() ? 2 : 0) + \count($a); return []; } if ($f) { $a[$prefix.'file'] = new LinkStub($f, $c->getStartLine()); $a[$prefix.'line'] = $c->getStartLine().' to '.$c->getEndLine(); } return $a; } public static function unsetClosureFileInfo(\Closure $c, array $a): array { unset($a[Caster::PREFIX_VIRTUAL.'file'], $a[Caster::PREFIX_VIRTUAL.'line']); return $a; } public static function castGenerator(\Generator $c, array $a, Stub $stub, bool $isNested): array { // Cannot create ReflectionGenerator based on a terminated Generator try { $reflectionGenerator = new \ReflectionGenerator($c); return self::castReflectionGenerator($reflectionGenerator, $a, $stub, $isNested); } catch (\Exception) { $a[Caster::PREFIX_VIRTUAL.'closed'] = true; return $a; } } public static function castType(\ReflectionType $c, array $a, Stub $stub, bool $isNested): array { $prefix = Caster::PREFIX_VIRTUAL; if ($c instanceof \ReflectionNamedType) { $a += [ $prefix.'name' => $c->getName(), $prefix.'allowsNull' => $c->allowsNull(), $prefix.'isBuiltin' => $c->isBuiltin(), ]; } elseif ($c instanceof \ReflectionUnionType || $c instanceof \ReflectionIntersectionType) { $a[$prefix.'allowsNull'] = $c->allowsNull(); self::addMap($a, $c, [ 'types' => 'getTypes', ]); } else { $a[$prefix.'allowsNull'] = $c->allowsNull(); } return $a; } public static function castAttribute(\ReflectionAttribute $c, array $a, Stub $stub, bool $isNested): array { $map = [ 'arguments' => 'getArguments', ]; self::addMap($a, $c, $map); return $a; } public static function castReflectionGenerator(\ReflectionGenerator $c, array $a, Stub $stub, bool $isNested): array { $prefix = Caster::PREFIX_VIRTUAL; if ($c->getThis()) { $a[$prefix.'this'] = new CutStub($c->getThis()); } $function = $c->getFunction(); $frame = [ 'class' => $function->class ?? null, 'type' => isset($function->class) ? ($function->isStatic() ? '::' : '->') : null, 'function' => $function->name, 'file' => $c->getExecutingFile(), 'line' => $c->getExecutingLine(), ]; if ($trace = $c->getTrace(\DEBUG_BACKTRACE_IGNORE_ARGS)) { $function = new \ReflectionGenerator($c->getExecutingGenerator()); array_unshift($trace, [ 'function' => 'yield', 'file' => $function->getExecutingFile(), 'line' => $function->getExecutingLine(), ]); $trace[] = $frame; $a[$prefix.'trace'] = new TraceStub($trace, false, 0, -1, -1); } else { $function = new FrameStub($frame, false, true); $function = ExceptionCaster::castFrameStub($function, [], $function, true); $a[$prefix.'executing'] = $function[$prefix.'src']; } $a[Caster::PREFIX_VIRTUAL.'closed'] = false; return $a; } public static function castClass(\ReflectionClass $c, array $a, Stub $stub, bool $isNested, int $filter = 0): array { $prefix = Caster::PREFIX_VIRTUAL; if ($n = \Reflection::getModifierNames($c->getModifiers())) { $a[$prefix.'modifiers'] = implode(' ', $n); } self::addMap($a, $c, [ 'extends' => 'getParentClass', 'implements' => 'getInterfaceNames', 'constants' => 'getReflectionConstants', ]); foreach ($c->getProperties() as $n) { $a[$prefix.'properties'][$n->name] = $n; } foreach ($c->getMethods() as $n) { $a[$prefix.'methods'][$n->name] = $n; } self::addAttributes($a, $c, $prefix); if (!($filter & Caster::EXCLUDE_VERBOSE) && !$isNested) { self::addExtra($a, $c); } return $a; } public static function castFunctionAbstract(\ReflectionFunctionAbstract $c, array $a, Stub $stub, bool $isNested, int $filter = 0): array { $prefix = Caster::PREFIX_VIRTUAL; self::addMap($a, $c, [ 'returnsReference' => 'returnsReference', 'returnType' => 'getReturnType', 'class' => 'getClosureCalledClass', 'this' => 'getClosureThis', ]); if (isset($a[$prefix.'returnType'])) { $v = $a[$prefix.'returnType']; $v = $v instanceof \ReflectionNamedType ? $v->getName() : (string) $v; $a[$prefix.'returnType'] = new ClassStub($a[$prefix.'returnType'] instanceof \ReflectionNamedType && $a[$prefix.'returnType']->allowsNull() && !\in_array($v, ['mixed', 'null'], true) ? '?'.$v : $v, [class_exists($v, false) || interface_exists($v, false) || trait_exists($v, false) ? $v : '', '']); } if (isset($a[$prefix.'class'])) { $a[$prefix.'class'] = new ClassStub($a[$prefix.'class']); } if (isset($a[$prefix.'this'])) { $a[$prefix.'this'] = new CutStub($a[$prefix.'this']); } foreach ($c->getParameters() as $v) { $k = '$'.$v->name; if ($v->isVariadic()) { $k = '...'.$k; } if ($v->isPassedByReference()) { $k = '&'.$k; } $a[$prefix.'parameters'][$k] = $v; } if (isset($a[$prefix.'parameters'])) { $a[$prefix.'parameters'] = new EnumStub($a[$prefix.'parameters']); } self::addAttributes($a, $c, $prefix); if (!($filter & Caster::EXCLUDE_VERBOSE) && $v = $c->getStaticVariables()) { foreach ($v as $k => &$v) { if (\is_object($v)) { $a[$prefix.'use']['$'.$k] = new CutStub($v); } else { $a[$prefix.'use']['$'.$k] = &$v; } } unset($v); $a[$prefix.'use'] = new EnumStub($a[$prefix.'use']); } if (!($filter & Caster::EXCLUDE_VERBOSE) && !$isNested) { self::addExtra($a, $c); } return $a; } public static function castClassConstant(\ReflectionClassConstant $c, array $a, Stub $stub, bool $isNested): array { $a[Caster::PREFIX_VIRTUAL.'modifiers'] = implode(' ', \Reflection::getModifierNames($c->getModifiers())); $a[Caster::PREFIX_VIRTUAL.'value'] = $c->getValue(); self::addAttributes($a, $c); return $a; } public static function castMethod(\ReflectionMethod $c, array $a, Stub $stub, bool $isNested): array { $a[Caster::PREFIX_VIRTUAL.'modifiers'] = implode(' ', \Reflection::getModifierNames($c->getModifiers())); return $a; } public static function castParameter(\ReflectionParameter $c, array $a, Stub $stub, bool $isNested): array { $prefix = Caster::PREFIX_VIRTUAL; self::addMap($a, $c, [ 'position' => 'getPosition', 'isVariadic' => 'isVariadic', 'byReference' => 'isPassedByReference', 'allowsNull' => 'allowsNull', ]); self::addAttributes($a, $c, $prefix); if ($v = $c->getType()) { $a[$prefix.'typeHint'] = $v instanceof \ReflectionNamedType ? $v->getName() : (string) $v; } if (isset($a[$prefix.'typeHint'])) { $v = $a[$prefix.'typeHint']; $a[$prefix.'typeHint'] = new ClassStub($v, [class_exists($v, false) || interface_exists($v, false) || trait_exists($v, false) ? $v : '', '']); } else { unset($a[$prefix.'allowsNull']); } if ($c->isOptional()) { try { $a[$prefix.'default'] = $v = $c->getDefaultValue(); if ($c->isDefaultValueConstant() && !\is_object($v)) { $a[$prefix.'default'] = new ConstStub($c->getDefaultValueConstantName(), $v); } if (null === $v) { unset($a[$prefix.'allowsNull']); } } catch (\ReflectionException) { } } return $a; } public static function castProperty(\ReflectionProperty $c, array $a, Stub $stub, bool $isNested): array { $a[Caster::PREFIX_VIRTUAL.'modifiers'] = implode(' ', \Reflection::getModifierNames($c->getModifiers())); self::addAttributes($a, $c); self::addExtra($a, $c); return $a; } public static function castReference(\ReflectionReference $c, array $a, Stub $stub, bool $isNested): array { $a[Caster::PREFIX_VIRTUAL.'id'] = $c->getId(); return $a; } public static function castExtension(\ReflectionExtension $c, array $a, Stub $stub, bool $isNested): array { self::addMap($a, $c, [ 'version' => 'getVersion', 'dependencies' => 'getDependencies', 'iniEntries' => 'getIniEntries', 'isPersistent' => 'isPersistent', 'isTemporary' => 'isTemporary', 'constants' => 'getConstants', 'functions' => 'getFunctions', 'classes' => 'getClasses', ]); return $a; } public static function castZendExtension(\ReflectionZendExtension $c, array $a, Stub $stub, bool $isNested): array { self::addMap($a, $c, [ 'version' => 'getVersion', 'author' => 'getAuthor', 'copyright' => 'getCopyright', 'url' => 'getURL', ]); return $a; } public static function getSignature(array $a): string { $prefix = Caster::PREFIX_VIRTUAL; $signature = ''; if (isset($a[$prefix.'parameters'])) { foreach ($a[$prefix.'parameters']->value as $k => $param) { $signature .= ', '; if ($type = $param->getType()) { if (!$type instanceof \ReflectionNamedType) { $signature .= $type.' '; } else { if ($param->allowsNull() && !\in_array($type->getName(), ['mixed', 'null'], true)) { $signature .= '?'; } $signature .= substr(strrchr('\\'.$type->getName(), '\\'), 1).' '; } } $signature .= $k; if (!$param->isDefaultValueAvailable()) { continue; } $v = $param->getDefaultValue(); $signature .= ' = '; if ($param->isDefaultValueConstant()) { $signature .= substr(strrchr('\\'.$param->getDefaultValueConstantName(), '\\'), 1); } elseif (null === $v) { $signature .= 'null'; } elseif (\is_array($v)) { $signature .= $v ? '[…'.\count($v).']' : '[]'; } elseif (\is_string($v)) { $signature .= 10 > \strlen($v) && !str_contains($v, '\\') ? "'{$v}'" : "'…".\strlen($v)."'"; } elseif (\is_bool($v)) { $signature .= $v ? 'true' : 'false'; } elseif (\is_object($v)) { $signature .= 'new '.substr(strrchr('\\'.get_debug_type($v), '\\'), 1); } else { $signature .= $v; } } } $signature = (empty($a[$prefix.'returnsReference']) ? '' : '&').'('.substr($signature, 2).')'; if (isset($a[$prefix.'returnType'])) { $signature .= ': '.substr(strrchr('\\'.$a[$prefix.'returnType'], '\\'), 1); } return $signature; } private static function addExtra(array &$a, \Reflector $c): void { $x = isset($a[Caster::PREFIX_VIRTUAL.'extra']) ? $a[Caster::PREFIX_VIRTUAL.'extra']->value : []; if (method_exists($c, 'getFileName') && $m = $c->getFileName()) { $x['file'] = new LinkStub($m, $c->getStartLine()); $x['line'] = $c->getStartLine().' to '.$c->getEndLine(); } self::addMap($x, $c, self::EXTRA_MAP, ''); if ($x) { $a[Caster::PREFIX_VIRTUAL.'extra'] = new EnumStub($x); } } private static function addMap(array &$a, object $c, array $map, string $prefix = Caster::PREFIX_VIRTUAL): void { foreach ($map as $k => $m) { if ('isDisabled' === $k) { continue; } if (method_exists($c, $m) && false !== ($m = $c->$m()) && null !== $m) { $a[$prefix.$k] = $m instanceof \Reflector ? $m->name : $m; } } } private static function addAttributes(array &$a, \Reflector $c, string $prefix = Caster::PREFIX_VIRTUAL): void { foreach ($c->getAttributes() as $n) { $a[$prefix.'attributes'][] = $n; } } } ================================================ FILE: Caster/ResourceCaster.php ================================================ * * 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; /** * Casts common resource types to array representation. * * @author Nicolas Grekas * * @final * * @internal */ class ResourceCaster { public static function castDba(\Dba\Connection $dba, array $a, Stub $stub, bool $isNested): array { if (\PHP_VERSION_ID < 80402) { // @see https://github.com/php/php-src/issues/16990 return $a; } $list = dba_list(); $a['file'] = $list[(int) $dba]; return $a; } public static function castProcess($process, array $a, Stub $stub, bool $isNested): array { return proc_get_status($process); } public static function castStream($stream, array $a, Stub $stub, bool $isNested): array { $a = stream_get_meta_data($stream) + static::castStreamContext($stream, $a, $stub, $isNested); if ($a['uri'] ?? false) { $a['uri'] = new LinkStub($a['uri']); } return $a; } public static function castStreamContext($stream, array $a, Stub $stub, bool $isNested): array { return @stream_context_get_params($stream) ?: $a; } } ================================================ FILE: Caster/ScalarStub.php ================================================ * * 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; /** * Represents any arbitrary value. * * @author Alexandre Daubois */ class ScalarStub extends Stub { public function __construct(mixed $value) { $this->value = $value; } } ================================================ FILE: Caster/SocketCaster.php ================================================ * * 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 * * @internal */ final class SocketCaster { public static function castSocket(\Socket $socket, array $a, Stub $stub, bool $isNested): array { socket_getsockname($socket, $addr, $port); $info = stream_get_meta_data(socket_export_stream($socket)); $uri = ($info['uri'] ?? '//'); if (str_starts_with($uri, 'unix://')) { $uri .= $addr; } else { $uri .= \sprintf(str_contains($addr, ':') ? '[%s]:%s' : '%s:%s', $addr, $port); } $a[Caster::PREFIX_VIRTUAL.'uri'] = $uri; if (@socket_atmark($socket)) { $a[Caster::PREFIX_VIRTUAL.'atmark'] = true; } $a += [ Caster::PREFIX_VIRTUAL.'timed_out' => $info['timed_out'], Caster::PREFIX_VIRTUAL.'blocked' => $info['blocked'], ]; if (!$lastError = socket_last_error($socket)) { return $a; } static $errors; if (!$errors) { $errors = get_defined_constants(true)['sockets'] ?? []; $errors = array_flip(array_filter($errors, static fn ($k) => str_starts_with($k, 'SOCKET_E'), \ARRAY_FILTER_USE_KEY)); } $a[Caster::PREFIX_VIRTUAL.'last_error'] = new ConstStub($errors[$lastError], socket_strerror($lastError)); return $a; } } ================================================ FILE: Caster/SplCaster.php ================================================ * * 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; /** * Casts SPL related classes to array representation. * * @author Nicolas Grekas * * @final * * @internal */ class SplCaster { private const SPL_FILE_OBJECT_FLAGS = [ \SplFileObject::DROP_NEW_LINE => 'DROP_NEW_LINE', \SplFileObject::READ_AHEAD => 'READ_AHEAD', \SplFileObject::SKIP_EMPTY => 'SKIP_EMPTY', \SplFileObject::READ_CSV => 'READ_CSV', ]; public static function castArrayObject(\ArrayObject $c, array $a, Stub $stub, bool $isNested): array { return self::castSplArray($c, $a, $stub, $isNested); } public static function castArrayIterator(\ArrayIterator $c, array $a, Stub $stub, bool $isNested): array { return self::castSplArray($c, $a, $stub, $isNested); } public static function castHeap(\Iterator $c, array $a, Stub $stub, bool $isNested): array { $a += [ Caster::PREFIX_VIRTUAL.'heap' => iterator_to_array(clone $c), ]; return $a; } public static function castDoublyLinkedList(\SplDoublyLinkedList $c, array $a, Stub $stub, bool $isNested): array { $prefix = Caster::PREFIX_VIRTUAL; $mode = $c->getIteratorMode(); $c->setIteratorMode(\SplDoublyLinkedList::IT_MODE_KEEP | $mode & ~\SplDoublyLinkedList::IT_MODE_DELETE); $a += [ $prefix.'mode' => new ConstStub((($mode & \SplDoublyLinkedList::IT_MODE_LIFO) ? 'IT_MODE_LIFO' : 'IT_MODE_FIFO').' | '.(($mode & \SplDoublyLinkedList::IT_MODE_DELETE) ? 'IT_MODE_DELETE' : 'IT_MODE_KEEP'), $mode), $prefix.'dllist' => iterator_to_array($c), ]; $c->setIteratorMode($mode); return $a; } public static function castFileInfo(\SplFileInfo $c, array $a, Stub $stub, bool $isNested): array { static $map = [ 'path' => 'getPath', 'filename' => 'getFilename', 'basename' => 'getBasename', 'pathname' => 'getPathname', 'extension' => 'getExtension', 'realPath' => 'getRealPath', 'aTime' => 'getATime', 'mTime' => 'getMTime', 'cTime' => 'getCTime', 'inode' => 'getInode', 'size' => 'getSize', 'perms' => 'getPerms', 'owner' => 'getOwner', 'group' => 'getGroup', 'type' => 'getType', 'writable' => 'isWritable', 'readable' => 'isReadable', 'executable' => 'isExecutable', 'file' => 'isFile', 'dir' => 'isDir', 'link' => 'isLink', 'linkTarget' => 'getLinkTarget', ]; $prefix = Caster::PREFIX_VIRTUAL; unset($a["\0SplFileInfo\0fileName"]); unset($a["\0SplFileInfo\0pathName"]); try { $c->isReadable(); } catch (\RuntimeException $e) { if ('Object not initialized' !== $e->getMessage()) { throw $e; } $a[$prefix.'⚠'] = 'The parent constructor was not called: the object is in an invalid state'; return $a; } catch (\Error $e) { if ('Object not initialized' !== $e->getMessage()) { throw $e; } $a[$prefix.'⚠'] = 'The parent constructor was not called: the object is in an invalid state'; return $a; } foreach ($map as $key => $accessor) { try { $a[$prefix.$key] = $c->$accessor(); } catch (\Exception) { } } if ($a[$prefix.'realPath'] ?? false) { $a[$prefix.'realPath'] = new LinkStub($a[$prefix.'realPath']); } if (isset($a[$prefix.'perms'])) { $a[$prefix.'perms'] = new ConstStub(\sprintf('0%o', $a[$prefix.'perms']), $a[$prefix.'perms']); } static $mapDate = ['aTime', 'mTime', 'cTime']; foreach ($mapDate as $key) { if (isset($a[$prefix.$key])) { $a[$prefix.$key] = new ConstStub(date('Y-m-d H:i:s', $a[$prefix.$key]), $a[$prefix.$key]); } } return $a; } public static function castFileObject(\SplFileObject $c, array $a, Stub $stub, bool $isNested): array { static $map = [ 'csvControl' => 'getCsvControl', 'flags' => 'getFlags', 'maxLineLen' => 'getMaxLineLen', 'fstat' => 'fstat', 'eof' => 'eof', 'key' => 'key', ]; $prefix = Caster::PREFIX_VIRTUAL; foreach ($map as $key => $accessor) { try { $a[$prefix.$key] = $c->$accessor(); } catch (\Exception) { } } if (isset($a[$prefix.'flags'])) { $flagsArray = []; foreach (self::SPL_FILE_OBJECT_FLAGS as $value => $name) { if ($a[$prefix.'flags'] & $value) { $flagsArray[] = $name; } } $a[$prefix.'flags'] = new ConstStub(implode('|', $flagsArray), $a[$prefix.'flags']); } if (isset($a[$prefix.'fstat'])) { $a[$prefix.'fstat'] = new CutArrayStub($a[$prefix.'fstat'], ['dev', 'ino', 'nlink', 'rdev', 'blksize', 'blocks']); } return $a; } public static function castObjectStorage(\SplObjectStorage $c, array $a, Stub $stub, bool $isNested): array { $storage = []; unset($a[Caster::PREFIX_DYNAMIC."\0gcdata"]); // Don't hit https://bugs.php.net/65967 unset($a["\0SplObjectStorage\0storage"]); $clone = clone $c; foreach ($clone as $obj) { $storage[] = new EnumStub([ 'object' => $obj, 'info' => $clone->getInfo(), ]); } $a += [ Caster::PREFIX_VIRTUAL.'storage' => $storage, ]; return $a; } public static function castOuterIterator(\OuterIterator $c, array $a, Stub $stub, bool $isNested): array { $a[Caster::PREFIX_VIRTUAL.'innerIterator'] = $c->getInnerIterator(); return $a; } public static function castWeakReference(\WeakReference $c, array $a, Stub $stub, bool $isNested): array { $a[Caster::PREFIX_VIRTUAL.'object'] = $c->get(); return $a; } public static function castWeakMap(\WeakMap $c, array $a, Stub $stub, bool $isNested): array { $map = []; foreach (clone $c as $obj => $data) { $map[] = new EnumStub([ 'object' => $obj, 'data' => $data, ]); } $a += [ Caster::PREFIX_VIRTUAL.'map' => $map, ]; return $a; } private static function castSplArray(\ArrayObject|\ArrayIterator $c, array $a, Stub $stub, bool $isNested): array { $prefix = Caster::PREFIX_VIRTUAL; $flags = $c->getFlags(); if (!($flags & \ArrayObject::STD_PROP_LIST)) { $c->setFlags(\ArrayObject::STD_PROP_LIST); $a = Caster::castObject($c, $c::class, method_exists($c, '__debugInfo'), $stub->class); $c->setFlags($flags); } unset($a["\0ArrayObject\0storage"], $a["\0ArrayIterator\0storage"]); $a += [ $prefix.'storage' => $c->getArrayCopy(), $prefix.'flag::STD_PROP_LIST' => (bool) ($flags & \ArrayObject::STD_PROP_LIST), $prefix.'flag::ARRAY_AS_PROPS' => (bool) ($flags & \ArrayObject::ARRAY_AS_PROPS), ]; if ($c instanceof \ArrayObject) { $a[$prefix.'iteratorClass'] = new ClassStub($c->getIteratorClass()); } return $a; } } ================================================ FILE: Caster/SqliteCaster.php ================================================ * * 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 * * @internal */ final class SqliteCaster { public static function castSqlite3Result(\SQLite3Result $result, array $a, Stub $stub, bool $isNested): array { $numColumns = $result->numColumns(); for ($i = 0; $i < $numColumns; ++$i) { $a[Caster::PREFIX_VIRTUAL.'columnNames'][$i] = $result->columnName($i); } return $a; } } ================================================ FILE: Caster/StubCaster.php ================================================ * * 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; /** * Casts a caster's Stub. * * @author Nicolas Grekas * * @final * * @internal */ class StubCaster { public static function castStub(Stub $c, array $a, Stub $stub, bool $isNested): array { if ($isNested) { $stub->type = $c->type; $stub->class = $c->class; $stub->value = $c->value; $stub->handle = $c->handle; $stub->cut = $c->cut; $stub->attr = $c->attr; if (Stub::TYPE_REF === $c->type && !$c->class && \is_string($c->value) && !preg_match('//u', $c->value)) { $stub->type = Stub::TYPE_STRING; $stub->class = Stub::STRING_BINARY; } $a = []; } return $a; } public static function castCutArray(CutArrayStub $c, array $a, Stub $stub, bool $isNested): array { return $isNested ? $c->preservedSubset : $a; } public static function cutInternals($obj, array $a, Stub $stub, bool $isNested): array { if ($isNested) { $stub->cut += \count($a); return []; } return $a; } public static function castEnum(EnumStub $c, array $a, Stub $stub, bool $isNested): array { if ($isNested) { $stub->class = $c->dumpKeys ? '' : null; $stub->handle = 0; $stub->value = null; $stub->cut = $c->cut; $stub->attr = $c->attr; $a = []; if ($c->value) { foreach (array_keys($c->value) as $k) { $keys[] = !isset($k[0]) || "\0" !== $k[0] ? Caster::PREFIX_VIRTUAL.$k : $k; } // Preserve references with array_combine() $a = array_combine($keys, $c->value); } } return $a; } public static function castScalar(ScalarStub $scalarStub, array $a, Stub $stub): array { $stub->type = Stub::TYPE_SCALAR; $stub->attr['value'] = $scalarStub->value; return $a; } } ================================================ FILE: Caster/SymfonyCaster.php ================================================ * * 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\HttpFoundation\Request; use Symfony\Component\Uid\TimeBasedUidInterface; use Symfony\Component\Uid\Ulid; use Symfony\Component\Uid\Uuid; use Symfony\Component\VarDumper\Cloner\Stub; use Symfony\Component\VarExporter\Internal\LazyObjectState; /** * @final * * @internal */ class SymfonyCaster { private const REQUEST_GETTERS = [ 'pathInfo' => 'getPathInfo', 'requestUri' => 'getRequestUri', 'baseUrl' => 'getBaseUrl', 'basePath' => 'getBasePath', 'method' => 'getMethod', 'format' => 'getRequestFormat', ]; public static function castRequest(Request $request, array $a, Stub $stub, bool $isNested): array { $clone = null; foreach (self::REQUEST_GETTERS as $prop => $getter) { $key = Caster::PREFIX_PROTECTED.$prop; if (\array_key_exists($key, $a) && null === $a[$key]) { $clone ??= clone $request; $a[Caster::PREFIX_VIRTUAL.$prop] = $clone->{$getter}(); } } return $a; } public static function castHttpClient($client, array $a, Stub $stub, bool $isNested): array { $multiKey = \sprintf("\0%s\0multi", $client::class); if (isset($a[$multiKey]) && !$a[$multiKey] instanceof Stub) { $a[$multiKey] = new CutStub($a[$multiKey]); } return $a; } public static function castHttpClientResponse($response, array $a, Stub $stub, bool $isNested): array { $stub->cut += \count($a); $a = []; foreach ($response->getInfo() as $k => $v) { $a[Caster::PREFIX_VIRTUAL.$k] = $v; } return $a; } public static function castLazyObjectState($state, array $a, Stub $stub, bool $isNested): array { if (!$isNested) { return $a; } $stub->cut += \count($a) - 1; $instance = $a['realInstance'] ?? null; if (isset($a['status'])) { // forward-compat with Symfony 8 $a = ['status' => new ConstStub(match ($a['status']) { LazyObjectState::STATUS_INITIALIZED_FULL => 'INITIALIZED_FULL', LazyObjectState::STATUS_INITIALIZED_PARTIAL => 'INITIALIZED_PARTIAL', LazyObjectState::STATUS_UNINITIALIZED_FULL => 'UNINITIALIZED_FULL', LazyObjectState::STATUS_UNINITIALIZED_PARTIAL => 'UNINITIALIZED_PARTIAL', }, $a['status'])]; } if ($instance) { $a['realInstance'] = $instance; --$stub->cut; } return $a; } public static function castUuid(Uuid $uuid, array $a, Stub $stub, bool $isNested): array { $a[Caster::PREFIX_VIRTUAL.'toBase58'] = $uuid->toBase58(); $a[Caster::PREFIX_VIRTUAL.'toBase32'] = $uuid->toBase32(); if ($uuid instanceof TimeBasedUidInterface) { $a[Caster::PREFIX_VIRTUAL.'time'] = $uuid->getDateTime()->format('Y-m-d H:i:s.u \U\T\C'); } return $a; } public static function castUlid(Ulid $ulid, array $a, Stub $stub, bool $isNested): array { $a[Caster::PREFIX_VIRTUAL.'toBase58'] = $ulid->toBase58(); $a[Caster::PREFIX_VIRTUAL.'toRfc4122'] = $ulid->toRfc4122(); if ($ulid instanceof TimeBasedUidInterface) { $a[Caster::PREFIX_VIRTUAL.'time'] = $ulid->getDateTime()->format('Y-m-d H:i:s.v \U\T\C'); } return $a; } } ================================================ FILE: Caster/TraceStub.php ================================================ * * 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; /** * Represents a backtrace as returned by debug_backtrace() or Exception->getTrace(). * * @author Nicolas Grekas */ class TraceStub extends Stub { public function __construct( array $trace, public bool $keepArgs = true, public int $sliceOffset = 0, public ?int $sliceLength = null, public int $numberingOffset = 0, ) { $this->value = $trace; } } ================================================ FILE: Caster/UninitializedStub.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; /** * Represents an uninitialized property. * * @author Nicolas Grekas */ class UninitializedStub extends ConstStub { public function __construct(\ReflectionProperty $property) { parent::__construct('?'.($property->hasType() ? ' '.$property->getType() : ''), 'Uninitialized property'); } } ================================================ FILE: Caster/UuidCaster.php ================================================ * * 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 Ramsey\Uuid\UuidInterface; use Symfony\Component\VarDumper\Cloner\Stub; /** * @author Grégoire Pineau * * @internal */ final class UuidCaster { public static function castRamseyUuid(UuidInterface $c, array $a, Stub $stub, bool $isNested): array { $a += [ Caster::PREFIX_VIRTUAL.'uuid' => (string) $c, ]; return $a; } } ================================================ FILE: Caster/VirtualStub.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; class VirtualStub extends ConstStub { public function __construct(\ReflectionProperty $property) { parent::__construct('~'.($property->hasType() ? ' '.$property->getType() : ''), 'Virtual property'); $this->attr['virtual'] = true; } } ================================================ FILE: Caster/XmlReaderCaster.php ================================================ * * 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; /** * Casts XmlReader class to array representation. * * @author Baptiste Clavié * * @final * * @internal */ class XmlReaderCaster { private const NODE_TYPES = [ \XMLReader::NONE => 'NONE', \XMLReader::ELEMENT => 'ELEMENT', \XMLReader::ATTRIBUTE => 'ATTRIBUTE', \XMLReader::TEXT => 'TEXT', \XMLReader::CDATA => 'CDATA', \XMLReader::ENTITY_REF => 'ENTITY_REF', \XMLReader::ENTITY => 'ENTITY', \XMLReader::PI => 'PI (Processing Instruction)', \XMLReader::COMMENT => 'COMMENT', \XMLReader::DOC => 'DOC', \XMLReader::DOC_TYPE => 'DOC_TYPE', \XMLReader::DOC_FRAGMENT => 'DOC_FRAGMENT', \XMLReader::NOTATION => 'NOTATION', \XMLReader::WHITESPACE => 'WHITESPACE', \XMLReader::SIGNIFICANT_WHITESPACE => 'SIGNIFICANT_WHITESPACE', \XMLReader::END_ELEMENT => 'END_ELEMENT', \XMLReader::END_ENTITY => 'END_ENTITY', \XMLReader::XML_DECLARATION => 'XML_DECLARATION', ]; public static function castXmlReader(\XMLReader $reader, array $a, Stub $stub, bool $isNested): array { try { $properties = [ 'LOADDTD' => @$reader->getParserProperty(\XMLReader::LOADDTD), 'DEFAULTATTRS' => @$reader->getParserProperty(\XMLReader::DEFAULTATTRS), 'VALIDATE' => @$reader->getParserProperty(\XMLReader::VALIDATE), 'SUBST_ENTITIES' => @$reader->getParserProperty(\XMLReader::SUBST_ENTITIES), ]; } catch (\Error) { $properties = [ 'LOADDTD' => false, 'DEFAULTATTRS' => false, 'VALIDATE' => false, 'SUBST_ENTITIES' => false, ]; } $props = Caster::PREFIX_VIRTUAL.'parserProperties'; $info = [ 'localName' => $reader->localName, 'prefix' => $reader->prefix, 'nodeType' => new ConstStub(self::NODE_TYPES[$reader->nodeType], $reader->nodeType), 'depth' => $reader->depth, 'isDefault' => $reader->isDefault, 'isEmptyElement' => \XMLReader::NONE === $reader->nodeType ? null : $reader->isEmptyElement, 'xmlLang' => $reader->xmlLang, 'attributeCount' => $reader->attributeCount, 'value' => $reader->value, 'namespaceURI' => $reader->namespaceURI, 'baseURI' => $reader->baseURI ? new LinkStub($reader->baseURI) : $reader->baseURI, $props => $properties, ]; if ($info[$props] = Caster::filter($info[$props], Caster::EXCLUDE_EMPTY, [], $count)) { $info[$props] = new EnumStub($info[$props]); $info[$props]->cut = $count; } $a = Caster::filter($a, Caster::EXCLUDE_UNINITIALIZED, [], $count); $info = Caster::filter($info, Caster::EXCLUDE_EMPTY, [], $count); // +2 because hasValue and hasAttributes are always filtered $stub->cut += $count + 2; return $a + $info; } } ================================================ FILE: Caster/XmlResourceCaster.php ================================================ * * 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; /** * Casts XML resources to array representation. * * @author Nicolas Grekas * * @final * * @internal */ class XmlResourceCaster { private const XML_ERRORS = [ \XML_ERROR_NONE => 'XML_ERROR_NONE', \XML_ERROR_NO_MEMORY => 'XML_ERROR_NO_MEMORY', \XML_ERROR_SYNTAX => 'XML_ERROR_SYNTAX', \XML_ERROR_NO_ELEMENTS => 'XML_ERROR_NO_ELEMENTS', \XML_ERROR_INVALID_TOKEN => 'XML_ERROR_INVALID_TOKEN', \XML_ERROR_UNCLOSED_TOKEN => 'XML_ERROR_UNCLOSED_TOKEN', \XML_ERROR_PARTIAL_CHAR => 'XML_ERROR_PARTIAL_CHAR', \XML_ERROR_TAG_MISMATCH => 'XML_ERROR_TAG_MISMATCH', \XML_ERROR_DUPLICATE_ATTRIBUTE => 'XML_ERROR_DUPLICATE_ATTRIBUTE', \XML_ERROR_JUNK_AFTER_DOC_ELEMENT => 'XML_ERROR_JUNK_AFTER_DOC_ELEMENT', \XML_ERROR_PARAM_ENTITY_REF => 'XML_ERROR_PARAM_ENTITY_REF', \XML_ERROR_UNDEFINED_ENTITY => 'XML_ERROR_UNDEFINED_ENTITY', \XML_ERROR_RECURSIVE_ENTITY_REF => 'XML_ERROR_RECURSIVE_ENTITY_REF', \XML_ERROR_ASYNC_ENTITY => 'XML_ERROR_ASYNC_ENTITY', \XML_ERROR_BAD_CHAR_REF => 'XML_ERROR_BAD_CHAR_REF', \XML_ERROR_BINARY_ENTITY_REF => 'XML_ERROR_BINARY_ENTITY_REF', \XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF => 'XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF', \XML_ERROR_MISPLACED_XML_PI => 'XML_ERROR_MISPLACED_XML_PI', \XML_ERROR_UNKNOWN_ENCODING => 'XML_ERROR_UNKNOWN_ENCODING', \XML_ERROR_INCORRECT_ENCODING => 'XML_ERROR_INCORRECT_ENCODING', \XML_ERROR_UNCLOSED_CDATA_SECTION => 'XML_ERROR_UNCLOSED_CDATA_SECTION', \XML_ERROR_EXTERNAL_ENTITY_HANDLING => 'XML_ERROR_EXTERNAL_ENTITY_HANDLING', ]; public static function castXml($h, array $a, Stub $stub, bool $isNested): array { $a['current_byte_index'] = xml_get_current_byte_index($h); $a['current_column_number'] = xml_get_current_column_number($h); $a['current_line_number'] = xml_get_current_line_number($h); $a['error_code'] = xml_get_error_code($h); if (isset(self::XML_ERRORS[$a['error_code']])) { $a['error_code'] = new ConstStub(self::XML_ERRORS[$a['error_code']], $a['error_code']); } return $a; } } ================================================ FILE: Cloner/AbstractCloner.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Cloner; use Symfony\Component\VarDumper\Caster\Caster; use Symfony\Component\VarDumper\Exception\ThrowingCasterException; /** * AbstractCloner implements a generic caster mechanism for objects and resources. * * @author Nicolas Grekas */ 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'], 'Symfony\Component\VarDumper\Caster\EnumStub' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'castEnum'], 'Symfony\Component\VarDumper\Caster\ScalarStub' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'castScalar'], 'Fiber' => ['Symfony\Component\VarDumper\Caster\FiberCaster', 'castFiber'], 'Closure' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castClosure'], 'Generator' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castGenerator'], 'ReflectionType' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castType'], 'ReflectionAttribute' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castAttribute'], 'ReflectionGenerator' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castReflectionGenerator'], 'ReflectionClass' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castClass'], 'ReflectionClassConstant' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castClassConstant'], 'ReflectionFunctionAbstract' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castFunctionAbstract'], 'ReflectionMethod' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castMethod'], 'ReflectionParameter' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castParameter'], 'ReflectionProperty' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castProperty'], 'ReflectionReference' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castReference'], 'ReflectionExtension' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castExtension'], 'ReflectionZendExtension' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castZendExtension'], 'Doctrine\Common\Persistence\ObjectManager' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], 'Doctrine\Common\Proxy\Proxy' => ['Symfony\Component\VarDumper\Caster\DoctrineCaster', 'castCommonProxy'], 'Doctrine\ORM\Proxy\Proxy' => ['Symfony\Component\VarDumper\Caster\DoctrineCaster', 'castOrmProxy'], 'Doctrine\ORM\PersistentCollection' => ['Symfony\Component\VarDumper\Caster\DoctrineCaster', 'castPersistentCollection'], 'Doctrine\Persistence\ObjectManager' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], 'DOMException' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castException'], 'Dom\Exception' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castException'], 'DOMStringList' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castDom'], 'DOMNameList' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castDom'], 'DOMImplementation' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castImplementation'], 'Dom\Implementation' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castImplementation'], 'DOMImplementationList' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castDom'], 'DOMNode' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castDom'], 'Dom\Node' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castDom'], 'DOMNameSpaceNode' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castDom'], 'DOMDocument' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castDocument'], 'Dom\XMLDocument' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castXMLDocument'], 'Dom\HTMLDocument' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castHTMLDocument'], 'DOMNodeList' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castDom'], 'Dom\NodeList' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castDom'], 'DOMNamedNodeMap' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castDom'], 'Dom\DTDNamedNodeMap' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castDom'], 'DOMXPath' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castDom'], 'Dom\XPath' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castDom'], 'Dom\HTMLCollection' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castDom'], 'Dom\TokenList' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castDom'], 'XMLReader' => ['Symfony\Component\VarDumper\Caster\XmlReaderCaster', 'castXmlReader'], 'ErrorException' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castErrorException'], 'Exception' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castException'], 'Error' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castError'], 'Symfony\Bridge\Monolog\Logger' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], 'Symfony\Component\DependencyInjection\ContainerInterface' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], 'Symfony\Component\EventDispatcher\EventDispatcherInterface' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], 'Symfony\Component\HttpClient\AmpHttpClient' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClient'], 'Symfony\Component\HttpClient\CurlHttpClient' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClient'], 'Symfony\Component\HttpClient\NativeHttpClient' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClient'], 'Symfony\Component\HttpClient\Response\AmpResponse' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClientResponse'], 'Symfony\Component\HttpClient\Response\AmpResponseV4' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClientResponse'], 'Symfony\Component\HttpClient\Response\AmpResponseV5' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClientResponse'], 'Symfony\Component\HttpClient\Response\CurlResponse' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClientResponse'], 'Symfony\Component\HttpClient\Response\NativeResponse' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClientResponse'], 'Symfony\Component\HttpFoundation\Request' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castRequest'], 'Symfony\Component\Uid\Ulid' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castUlid'], 'Symfony\Component\Uid\Uuid' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castUuid'], 'Symfony\Component\VarExporter\Internal\LazyObjectState' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castLazyObjectState'], 'Symfony\Component\VarDumper\Exception\ThrowingCasterException' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castThrowingCasterException'], 'Symfony\Component\VarDumper\Caster\TraceStub' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castTraceStub'], 'Symfony\Component\VarDumper\Caster\FrameStub' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castFrameStub'], 'Symfony\Component\VarDumper\Cloner\AbstractCloner' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], 'Symfony\Component\ErrorHandler\Exception\FlattenException' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castFlattenException'], 'Symfony\Component\ErrorHandler\Exception\SilencedErrorContext' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castSilencedErrorContext'], 'Imagine\Image\ImageInterface' => ['Symfony\Component\VarDumper\Caster\ImagineCaster', 'castImage'], 'Ramsey\Uuid\UuidInterface' => ['Symfony\Component\VarDumper\Caster\UuidCaster', 'castRamseyUuid'], 'ProxyManager\Proxy\ProxyInterface' => ['Symfony\Component\VarDumper\Caster\ProxyManagerCaster', 'castProxy'], 'PHPUnit_Framework_MockObject_MockObject' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], 'PHPUnit\Framework\MockObject\MockObject' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], 'PHPUnit\Framework\MockObject\Stub' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], 'Prophecy\Prophecy\ProphecySubjectInterface' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], 'Mockery\MockInterface' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], 'PDO' => ['Symfony\Component\VarDumper\Caster\PdoCaster', 'castPdo'], 'PDOStatement' => ['Symfony\Component\VarDumper\Caster\PdoCaster', 'castPdoStatement'], 'AMQPConnection' => ['Symfony\Component\VarDumper\Caster\AmqpCaster', 'castConnection'], 'AMQPChannel' => ['Symfony\Component\VarDumper\Caster\AmqpCaster', 'castChannel'], 'AMQPQueue' => ['Symfony\Component\VarDumper\Caster\AmqpCaster', 'castQueue'], 'AMQPExchange' => ['Symfony\Component\VarDumper\Caster\AmqpCaster', 'castExchange'], 'AMQPEnvelope' => ['Symfony\Component\VarDumper\Caster\AmqpCaster', 'castEnvelope'], 'ArrayObject' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castArrayObject'], 'ArrayIterator' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castArrayIterator'], 'SplDoublyLinkedList' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castDoublyLinkedList'], 'SplFileInfo' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castFileInfo'], 'SplFileObject' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castFileObject'], 'SplHeap' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castHeap'], 'SplObjectStorage' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castObjectStorage'], 'SplPriorityQueue' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castHeap'], 'OuterIterator' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castOuterIterator'], 'WeakMap' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castWeakMap'], 'WeakReference' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castWeakReference'], 'Redis' => ['Symfony\Component\VarDumper\Caster\RedisCaster', 'castRedis'], 'Relay\Relay' => ['Symfony\Component\VarDumper\Caster\RedisCaster', 'castRedis'], 'RedisArray' => ['Symfony\Component\VarDumper\Caster\RedisCaster', 'castRedisArray'], 'RedisCluster' => ['Symfony\Component\VarDumper\Caster\RedisCaster', 'castRedisCluster'], 'DateTimeInterface' => ['Symfony\Component\VarDumper\Caster\DateCaster', 'castDateTime'], 'DateInterval' => ['Symfony\Component\VarDumper\Caster\DateCaster', 'castInterval'], 'DateTimeZone' => ['Symfony\Component\VarDumper\Caster\DateCaster', 'castTimeZone'], 'DatePeriod' => ['Symfony\Component\VarDumper\Caster\DateCaster', 'castPeriod'], 'GMP' => ['Symfony\Component\VarDumper\Caster\GmpCaster', 'castGmp'], 'MessageFormatter' => ['Symfony\Component\VarDumper\Caster\IntlCaster', 'castMessageFormatter'], 'NumberFormatter' => ['Symfony\Component\VarDumper\Caster\IntlCaster', 'castNumberFormatter'], 'IntlTimeZone' => ['Symfony\Component\VarDumper\Caster\IntlCaster', 'castIntlTimeZone'], 'IntlCalendar' => ['Symfony\Component\VarDumper\Caster\IntlCaster', 'castIntlCalendar'], 'IntlDateFormatter' => ['Symfony\Component\VarDumper\Caster\IntlCaster', 'castIntlDateFormatter'], 'Memcached' => ['Symfony\Component\VarDumper\Caster\MemcachedCaster', 'castMemcached'], 'Ds\Collection' => ['Symfony\Component\VarDumper\Caster\DsCaster', 'castCollection'], 'Ds\Map' => ['Symfony\Component\VarDumper\Caster\DsCaster', 'castMap'], 'Ds\Pair' => ['Symfony\Component\VarDumper\Caster\DsCaster', 'castPair'], 'Symfony\Component\VarDumper\Caster\DsPairStub' => ['Symfony\Component\VarDumper\Caster\DsCaster', 'castPairStub'], 'mysqli_driver' => ['Symfony\Component\VarDumper\Caster\MysqliCaster', 'castMysqliDriver'], 'CurlHandle' => ['Symfony\Component\VarDumper\Caster\CurlCaster', 'castCurl'], 'Dba\Connection' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castDba'], 'GdImage' => ['Symfony\Component\VarDumper\Caster\GdCaster', 'castGd'], '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'], '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'], 'RdKafka' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castRdKafka'], 'RdKafka\Conf' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castConf'], 'RdKafka\KafkaConsumer' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castKafkaConsumer'], 'RdKafka\Metadata\Broker' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castBrokerMetadata'], 'RdKafka\Metadata\Collection' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castCollectionMetadata'], 'RdKafka\Metadata\Partition' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castPartitionMetadata'], 'RdKafka\Metadata\Topic' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castTopicMetadata'], 'RdKafka\Message' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castMessage'], 'RdKafka\Topic' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castTopic'], 'RdKafka\TopicPartition' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castTopicPartition'], 'RdKafka\TopicConf' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castTopicConf'], 'FFI\CData' => ['Symfony\Component\VarDumper\Caster\FFICaster', 'castCTypeOrCData'], 'FFI\CType' => ['Symfony\Component\VarDumper\Caster\FFICaster', 'castCTypeOrCData'], ]; protected int $maxItems = 2500; protected int $maxString = -1; protected int $minDepth = 1; /** * @var array> */ private array $casters = []; /** * @var callable|null */ private $prevErrorHandler; private array $classInfo = []; private int $filter = 0; /** * @param callable[]|null $casters A map of casters * * @see addCasters */ public function __construct(?array $casters = null) { $this->addCasters($casters ?? static::$defaultCasters); } /** * Adds casters for resources and objects. * * Maps resources or object types to a callback. * Use types as keys and callable casters as values. * Prefix types with `::`, * see e.g. self::$defaultCasters. * * @param array $casters A map of casters */ public function addCasters(array $casters): void { foreach ($casters as $type => $callback) { $this->casters[$type][] = $callback; } } /** * Adds default casters for resources and objects. * * Maps resources or object types to a callback. * Use types as keys and callable casters as values. * Prefix types with `::`, * see e.g. self::$defaultCasters. * * @param array $casters A map of casters */ public static function addDefaultCasters(array $casters): void { self::$defaultCasters = [...self::$defaultCasters, ...$casters]; } /** * Sets the maximum number of items to clone past the minimum depth in nested structures. */ public function setMaxItems(int $maxItems): void { $this->maxItems = $maxItems; } /** * Sets the maximum cloned length for strings. */ public function setMaxString(int $maxString): void { $this->maxString = $maxString; } /** * Sets the minimum tree depth where we are guaranteed to clone all the items. After this * depth is reached, only setMaxItems items will be cloned. */ public function setMinDepth(int $minDepth): void { $this->minDepth = $minDepth; } /** * Clones a PHP variable. * * @param int $filter A bit field of Caster::EXCLUDE_* constants */ public function cloneVar(mixed $var, int $filter = 0): Data { $this->prevErrorHandler = set_error_handler(function ($type, $msg, $file, $line, $context = []) { if (\E_RECOVERABLE_ERROR === $type || \E_USER_ERROR === $type) { // Cloner never dies throw new \ErrorException($msg, 0, $type, $file, $line); } if ($this->prevErrorHandler) { return ($this->prevErrorHandler)($type, $msg, $file, $line, $context); } return false; }); $this->filter = $filter; if ($gc = gc_enabled()) { gc_disable(); } try { return new Data($this->doClone($var)); } finally { if ($gc) { gc_enable(); } restore_error_handler(); $this->prevErrorHandler = null; } } /** * Effectively clones the PHP variable. */ abstract protected function doClone(mixed $var): array; /** * Casts an object to an array representation. * * @param bool $isNested True if the object is nested in the dumped structure */ protected function castObject(Stub $stub, bool $isNested): array { $obj = $stub->value; $class = $stub->class; if (str_contains($class, "@anonymous\0")) { $stub->class = get_debug_type($obj); } if (isset($this->classInfo[$class])) { [$i, $parents, $hasDebugInfo, $fileInfo] = $this->classInfo[$class]; } else { $i = 2; $parents = [$class]; $hasDebugInfo = method_exists($class, '__debugInfo'); foreach (class_parents($class) as $p) { $parents[] = $p; ++$i; } foreach (class_implements($class) as $p) { $parents[] = $p; ++$i; } $parents[] = '*'; $r = new \ReflectionClass($class); $fileInfo = $r->isInternal() || $r->isSubclassOf(Stub::class) ? [] : [ 'file' => $r->getFileName(), 'line' => $r->getStartLine(), ]; $this->classInfo[$class] = [$i, $parents, $hasDebugInfo, $fileInfo]; } $stub->attr += $fileInfo; $a = Caster::castObject($obj, $class, $hasDebugInfo, $stub->class); try { while ($i--) { if (!empty($this->casters[$p = $parents[$i]])) { foreach ($this->casters[$p] as $callback) { $a = $callback($obj, $a, $stub, $isNested, $this->filter); } } } } catch (\Exception $e) { $a = [(Stub::TYPE_OBJECT === $stub->type ? Caster::PREFIX_VIRTUAL : '').'⚠' => new ThrowingCasterException($e)] + $a; } return $a; } /** * Casts a resource to an array representation. * * @param bool $isNested True if the object is nested in the dumped structure */ protected function castResource(Stub $stub, bool $isNested): array { $a = []; $res = $stub->value; $type = $stub->class; try { if (!empty($this->casters[':'.$type])) { foreach ($this->casters[':'.$type] as $callback) { $a = $callback($res, $a, $stub, $isNested, $this->filter); } } } catch (\Exception $e) { $a = [(Stub::TYPE_OBJECT === $stub->type ? Caster::PREFIX_VIRTUAL : '').'⚠' => new ThrowingCasterException($e)] + $a; } return $a; } } ================================================ FILE: Cloner/ClonerInterface.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Cloner; /** * @author Nicolas Grekas */ interface ClonerInterface { /** * Clones a PHP variable. */ public function cloneVar(mixed $var): Data; } ================================================ FILE: Cloner/Cursor.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Cloner; /** * Represents the current state of a dumper while dumping. * * @author Nicolas Grekas */ class Cursor { public const HASH_INDEXED = Stub::ARRAY_INDEXED; public const HASH_ASSOC = Stub::ARRAY_ASSOC; public const HASH_OBJECT = Stub::TYPE_OBJECT; public const HASH_RESOURCE = Stub::TYPE_RESOURCE; public int $depth = 0; public int $refIndex = 0; public int $softRefTo = 0; public int $softRefCount = 0; public int $softRefHandle = 0; public int $hardRefTo = 0; public int $hardRefCount = 0; public int $hardRefHandle = 0; public int $hashType; public string|int|null $hashKey = null; public bool $hashKeyIsBinary; public int $hashIndex = 0; public int $hashLength = 0; public int $hashCut = 0; public bool $stop = false; public array $attr = []; public bool $skipChildren = false; } ================================================ FILE: Cloner/Data.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Cloner; use Symfony\Component\VarDumper\Caster\Caster; use Symfony\Component\VarDumper\Dumper\ContextProvider\SourceContextProvider; /** * @author Nicolas Grekas */ class Data implements \ArrayAccess, \Countable, \IteratorAggregate, \Stringable { private array $data; private int $position = 0; private int|string $key = 0; private int $maxDepth = 20; private int $maxItemsPerDepth = -1; private int $useRefHandles = -1; private array $context = []; /** * @param array $data An array as returned by ClonerInterface::cloneVar() */ public function __construct(array $data) { $this->data = $data; } public function getType(): ?string { $item = $this->data[$this->position][$this->key]; if ($item instanceof Stub && Stub::TYPE_REF === $item->type && !$item->position) { $item = $item->value; } if (!$item instanceof Stub) { return \gettype($item); } if (Stub::TYPE_STRING === $item->type) { return 'string'; } if (Stub::TYPE_ARRAY === $item->type) { return 'array'; } if (Stub::TYPE_OBJECT === $item->type) { return $item->class; } if (Stub::TYPE_RESOURCE === $item->type) { return $item->class.' resource'; } return null; } /** * Returns a native representation of the original value. * * @param array|bool $recursive Whether values should be resolved recursively or not * * @return string|int|float|bool|array|Data[]|null */ public function getValue(array|bool $recursive = false): string|int|float|bool|array|null { $item = $this->data[$this->position][$this->key]; if ($item instanceof Stub && Stub::TYPE_REF === $item->type && !$item->position) { $item = $item->value; } if (!($item = $this->getStub($item)) instanceof Stub) { return $item; } if (Stub::TYPE_STRING === $item->type) { return $item->value; } $children = $item->position ? $this->data[$item->position] : []; foreach ($children as $k => $v) { if ($recursive && !($v = $this->getStub($v)) instanceof Stub) { continue; } $children[$k] = clone $this; $children[$k]->key = $k; $children[$k]->position = $item->position; if ($recursive) { if (Stub::TYPE_REF === $v->type && ($v = $this->getStub($v->value)) instanceof Stub) { $recursive = (array) $recursive; if (isset($recursive[$v->position])) { continue; } $recursive[$v->position] = true; } $children[$k] = $children[$k]->getValue($recursive); } } return $children; } public function count(): int { return \count($this->getValue()); } public function getIterator(): \Traversable { if (!\is_array($value = $this->getValue())) { throw new \LogicException(\sprintf('"%s" object holds non-iterable type "%s".', self::class, get_debug_type($value))); } yield from $value; } public function __get(string $key): mixed { if (null !== $data = $this->seek($key)) { $item = $this->getStub($data->data[$data->position][$data->key]); return $item instanceof Stub || [] === $item ? $data : $item; } return null; } public function __isset(string $key): bool { return null !== $this->seek($key); } public function offsetExists(mixed $key): bool { return $this->__isset($key); } public function offsetGet(mixed $key): mixed { return $this->__get($key); } public function offsetSet(mixed $key, mixed $value): void { throw new \BadMethodCallException(self::class.' objects are immutable.'); } public function offsetUnset(mixed $key): void { throw new \BadMethodCallException(self::class.' objects are immutable.'); } public function __toString(): string { $value = $this->getValue(); if (!\is_array($value)) { return (string) $value; } return \sprintf('%s (count=%d)', $this->getType(), \count($value)); } /** * Returns a depth limited clone of $this. */ public function withMaxDepth(int $maxDepth): static { $data = clone $this; $data->maxDepth = $maxDepth; return $data; } /** * Limits the number of elements per depth level. */ public function withMaxItemsPerDepth(int $maxItemsPerDepth): static { $data = clone $this; $data->maxItemsPerDepth = $maxItemsPerDepth; return $data; } /** * Enables/disables objects' identifiers tracking. * * @param bool $useRefHandles False to hide global ref. handles */ public function withRefHandles(bool $useRefHandles): static { $data = clone $this; $data->useRefHandles = $useRefHandles ? -1 : 0; return $data; } public function withContext(array $context): static { $data = clone $this; $data->context = $context; return $data; } public function getContext(): array { return $this->context; } /** * Seeks to a specific key in nested data structures. */ public function seek(string|int $key): ?static { $item = $this->data[$this->position][$this->key]; if ($item instanceof Stub && Stub::TYPE_REF === $item->type && !$item->position) { $item = $item->value; } if (!($item = $this->getStub($item)) instanceof Stub || !$item->position) { return null; } $keys = [$key]; switch ($item->type) { case Stub::TYPE_OBJECT: $keys[] = Caster::PREFIX_DYNAMIC.$key; $keys[] = Caster::PREFIX_PROTECTED.$key; $keys[] = Caster::PREFIX_VIRTUAL.$key; $keys[] = "\0$item->class\0$key"; // no break case Stub::TYPE_ARRAY: case Stub::TYPE_RESOURCE: break; default: return null; } $data = null; $children = $this->data[$item->position]; foreach ($keys as $key) { if (isset($children[$key]) || \array_key_exists($key, $children)) { $data = clone $this; $data->key = $key; $data->position = $item->position; break; } } return $data; } /** * Dumps data with a DumperInterface dumper. */ public function dump(DumperInterface $dumper): void { $refs = [0]; $cursor = new Cursor(); $cursor->hashType = -1; $cursor->attr = $this->context[SourceContextProvider::class] ?? []; $label = $this->context['label'] ?? ''; if ($cursor->attr || '' !== $label) { $dumper->dumpScalar($cursor, 'label', $label); } $cursor->hashType = 0; $this->dumpItem($dumper, $cursor, $refs, $this->data[$this->position][$this->key]); } /** * Depth-first dumping of items. * * @param mixed $item A Stub object or the original value being dumped */ private function dumpItem(DumperInterface $dumper, Cursor $cursor, array &$refs, mixed $item): void { $cursor->refIndex = 0; $cursor->softRefTo = $cursor->softRefHandle = $cursor->softRefCount = 0; $cursor->hardRefTo = $cursor->hardRefHandle = $cursor->hardRefCount = 0; $firstSeen = true; if (!$item instanceof Stub) { $cursor->attr = []; $type = \gettype($item); if ('array' === $type && $item) { $item = $this->getStub($item); } } elseif (Stub::TYPE_REF === $item->type) { if ($item->handle) { if (!isset($refs[$r = $item->handle - (\PHP_INT_MAX >> 1)])) { $cursor->refIndex = $refs[$r] = $cursor->refIndex ?: ++$refs[0]; } else { $firstSeen = false; } $cursor->hardRefTo = $refs[$r]; $cursor->hardRefHandle = $this->useRefHandles & $item->handle; $cursor->hardRefCount = 0 < $item->handle ? $item->refCount : 0; } $cursor->attr = $item->attr; $type = $item->class ?: \gettype($item->value); $item = $this->getStub($item->value); } if ($item instanceof Stub) { if ($item->refCount) { if (!isset($refs[$r = $item->handle])) { $cursor->refIndex = $refs[$r] = $cursor->refIndex ?: ++$refs[0]; } else { $firstSeen = false; } $cursor->softRefTo = $refs[$r]; } $cursor->softRefHandle = $this->useRefHandles & $item->handle; $cursor->softRefCount = $item->refCount; $cursor->attr = $item->attr; $cut = $item->cut; if ($item->position && $firstSeen) { $children = $this->data[$item->position]; if ($cursor->stop) { if ($cut >= 0) { $cut += \count($children); } $children = []; } } else { $children = []; } switch ($item->type) { case Stub::TYPE_STRING: $dumper->dumpString($cursor, $item->value, Stub::STRING_BINARY === $item->class, $cut); break; case Stub::TYPE_ARRAY: $item = clone $item; $item->type = $item->class; $item->class = $item->value; // no break case Stub::TYPE_OBJECT: case Stub::TYPE_RESOURCE: $withChildren = $children && $cursor->depth !== $this->maxDepth && $this->maxItemsPerDepth; $dumper->enterHash($cursor, $item->type, $item->class, $withChildren); if ($withChildren) { if ($cursor->skipChildren) { $withChildren = false; $cut = -1; } else { $cut = $this->dumpChildren($dumper, $cursor, $refs, $children, $cut, $item->type, null !== $item->class); } } elseif ($children && 0 <= $cut) { $cut += \count($children); } $cursor->skipChildren = false; $dumper->leaveHash($cursor, $item->type, $item->class, $withChildren, $cut); break; case Stub::TYPE_SCALAR: $dumper->dumpScalar($cursor, 'default', $item->attr['value']); break; default: throw new \RuntimeException(\sprintf('Unexpected Stub type: "%s".', $item->type)); } } elseif ('array' === $type) { $dumper->enterHash($cursor, Cursor::HASH_INDEXED, 0, false); $dumper->leaveHash($cursor, Cursor::HASH_INDEXED, 0, false, 0); } elseif ('string' === $type) { $dumper->dumpString($cursor, $item, false, 0); } else { $dumper->dumpScalar($cursor, $type, $item); } } /** * Dumps children of hash structures. * * @return int The final number of removed items */ private function dumpChildren(DumperInterface $dumper, Cursor $parentCursor, array &$refs, array $children, int $hashCut, int $hashType, bool $dumpKeys): int { $cursor = clone $parentCursor; ++$cursor->depth; $cursor->hashType = $hashType; $cursor->hashIndex = 0; $cursor->hashLength = \count($children); $cursor->hashCut = $hashCut; foreach ($children as $key => $child) { $cursor->hashKeyIsBinary = isset($key[0]) && !preg_match('//u', $key); $cursor->hashKey = $dumpKeys ? $key : null; $this->dumpItem($dumper, $cursor, $refs, $child); if (++$cursor->hashIndex === $this->maxItemsPerDepth || $cursor->stop) { $parentCursor->stop = true; return $hashCut >= 0 ? $hashCut + $cursor->hashLength - $cursor->hashIndex : $hashCut; } } return $hashCut; } private function getStub(mixed $item): mixed { if (!$item || !\is_array($item)) { return $item; } $stub = new Stub(); $stub->type = Stub::TYPE_ARRAY; foreach ($item as $stub->class => $stub->position) { } if (isset($item[0])) { $stub->cut = $item[0]; } $stub->value = $stub->cut + ($stub->position ? \count($this->data[$stub->position]) : 0); return $stub; } } ================================================ FILE: Cloner/DumperInterface.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Cloner; /** * DumperInterface used by Data objects. * * @author Nicolas Grekas */ interface DumperInterface { /** * Dumps a scalar value. */ public function dumpScalar(Cursor $cursor, string $type, string|int|float|bool|null $value): void; /** * Dumps a string. * * @param string $str The string being dumped * @param bool $bin Whether $str is UTF-8 or binary encoded * @param int $cut The number of characters $str has been cut by */ public function dumpString(Cursor $cursor, string $str, bool $bin, int $cut): void; /** * Dumps while entering an hash. * * @param int $type A Cursor::HASH_* const for the type of hash * @param string|int|null $class The object class, resource type or array count * @param bool $hasChild When the dump of the hash has child item */ public function enterHash(Cursor $cursor, int $type, string|int|null $class, bool $hasChild): void; /** * Dumps while leaving an hash. * * @param int $type A Cursor::HASH_* const for the type of hash * @param string|int|null $class The object class, resource type or array count * @param bool $hasChild When the dump of the hash has child item * @param int $cut The number of items the hash has been cut by */ public function leaveHash(Cursor $cursor, int $type, string|int|null $class, bool $hasChild, int $cut): void; } ================================================ FILE: Cloner/Stub.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Cloner; /** * Represents the main properties of a PHP variable. * * @author Nicolas Grekas */ class Stub { public const TYPE_REF = 1; public const TYPE_STRING = 2; public const TYPE_ARRAY = 3; public const TYPE_OBJECT = 4; public const TYPE_RESOURCE = 5; public const TYPE_SCALAR = 6; public const STRING_BINARY = 1; public const STRING_UTF8 = 2; public const ARRAY_ASSOC = 1; public const ARRAY_INDEXED = 2; public int $type = self::TYPE_REF; public string|int|null $class = ''; public mixed $value = null; public int $cut = 0; public int $handle = 0; public int $refCount = 0; public int $position = 0; public array $attr = []; /** * @internal */ protected static array $propertyDefaults = []; public function __serialize(): array { static $noDefault = new \stdClass(); if (self::class === static::class) { $data = []; foreach ($this as $k => $v) { $default = self::$propertyDefaults[$this::class][$k] ??= ($p = new \ReflectionProperty($this, $k))->hasDefaultValue() ? $p->getDefaultValue() : ($p->hasType() ? $noDefault : null); if ($noDefault === $default || $default !== $v) { $data[$k] = $v; } } return $data; } return \Closure::bind(function () use ($noDefault) { $data = []; foreach ($this as $k => $v) { $default = self::$propertyDefaults[$this::class][$k] ??= ($p = new \ReflectionProperty($this, $k))->hasDefaultValue() ? $p->getDefaultValue() : ($p->hasType() ? $noDefault : null); if ($noDefault === $default || $default !== $v) { $data[$k] = $v; } } return $data; }, $this, $this::class)(); } } ================================================ FILE: Cloner/VarCloner.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Cloner; /** * @author Nicolas Grekas */ class VarCloner extends AbstractCloner { private static array $arrayCache = []; protected function doClone(mixed $var): array { $len = 1; // Length of $queue $pos = 0; // Number of cloned items past the minimum depth $refsCounter = 0; // Hard references counter $queue = [[$var]]; // This breadth-first queue is the return value $hardRefs = []; // Map of original zval ids to stub objects $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 $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 $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 $arrayStub = new Stub(); $arrayStub->type = Stub::TYPE_ARRAY; for ($i = 0; $i < $len; ++$i) { // Detect when we move on to the next tree depth if ($i > $currentDepthFinalIndex) { ++$currentDepth; $currentDepthFinalIndex = $len - 1; if ($currentDepth >= $minDepth) { $minimumDepthReached = true; } } $vals = $queue[$i]; foreach ($vals as $k => $v) { // $v is the original value or a stub object in case of hard references $zvalRef = ($r = \ReflectionReference::fromArrayElement($vals, $k)) ? $r->getId() : null; if ($zvalRef) { $vals[$k] = &$stub; // Break hard references to make $queue completely unset($stub); // independent from the original structure if (null !== $vals[$k] = $hardRefs[$zvalRef] ?? null) { $v = $vals[$k]; if ($v->value instanceof Stub && (Stub::TYPE_OBJECT === $v->value->type || Stub::TYPE_RESOURCE === $v->value->type)) { ++$v->value->refCount; } ++$v->refCount; continue; } $vals[$k] = new Stub(); $vals[$k]->value = $v; $vals[$k]->handle = ++$refsCounter; $hardRefs[$zvalRef] = $vals[$k]; } // Create $stub when the original value $v cannot be used directly // If $v is a nested structure, put that structure in array $a switch (true) { case null === $v: case \is_bool($v): case \is_int($v): case \is_float($v): continue 2; case \is_string($v): if ('' === $v) { continue 2; } if (!preg_match('//u', $v)) { $stub = new Stub(); $stub->type = Stub::TYPE_STRING; $stub->class = Stub::STRING_BINARY; if (0 <= $maxString && 0 < $cut = \strlen($v) - $maxString) { $stub->cut = $cut; $stub->value = substr($v, 0, -$cut); } else { $stub->value = $v; } } elseif (0 <= $maxString && isset($v[1 + ($maxString >> 2)]) && 0 < $cut = mb_strlen($v, 'UTF-8') - $maxString) { $stub = new Stub(); $stub->type = Stub::TYPE_STRING; $stub->class = Stub::STRING_UTF8; $stub->cut = $cut; $stub->value = mb_substr($v, 0, $maxString, 'UTF-8'); } else { continue 2; } $a = null; break; case \is_array($v): if (!$v) { continue 2; } $stub = $arrayStub; $stub->class = array_is_list($v) ? Stub::ARRAY_INDEXED : Stub::ARRAY_ASSOC; $a = $v; break; case \is_object($v): if (empty($objRefs[$h = spl_object_id($v)])) { $stub = new Stub(); $stub->type = Stub::TYPE_OBJECT; $stub->class = $v::class; $stub->value = $v; $stub->handle = $h; $a = $this->castObject($stub, 0 < $i); if ($v !== $stub->value) { if (Stub::TYPE_OBJECT !== $stub->type || null === $stub->value) { break; } $stub->handle = $h = spl_object_id($stub->value); } $stub->value = null; if (0 <= $maxItems && $maxItems <= $pos && $minimumDepthReached) { $stub->cut = \count($a); $a = null; } } if (empty($objRefs[$h])) { $objRefs[$h] = $stub; $objects[] = $v; } else { $stub = $objRefs[$h]; ++$stub->refCount; $a = null; } break; default: // resource if (empty($resRefs[$h = (int) $v])) { $stub = new Stub(); $stub->type = Stub::TYPE_RESOURCE; if ('Unknown' === $stub->class = @get_resource_type($v)) { $stub->class = 'Closed'; } $stub->value = $v; $stub->handle = $h; $a = $this->castResource($stub, 0 < $i); $stub->value = null; if (0 <= $maxItems && $maxItems <= $pos && $minimumDepthReached) { $stub->cut = \count($a); $a = null; } } if (empty($resRefs[$h])) { $resRefs[$h] = $stub; } else { $stub = $resRefs[$h]; ++$stub->refCount; $a = null; } break; } if ($a) { if (!$minimumDepthReached || 0 > $maxItems) { $queue[$len] = $a; $stub->position = $len++; } elseif ($pos < $maxItems) { if ($maxItems < $pos += \count($a)) { $a = \array_slice($a, 0, $maxItems - $pos, true); if ($stub->cut >= 0) { $stub->cut += $pos - $maxItems; } } $queue[$len] = $a; $stub->position = $len++; } elseif ($stub->cut >= 0) { $stub->cut += \count($a); $stub->position = 0; } } if ($arrayStub === $stub) { if ($arrayStub->cut) { $stub = [$arrayStub->cut, $arrayStub->class => $arrayStub->position]; $arrayStub->cut = 0; } elseif (isset(self::$arrayCache[$arrayStub->class][$arrayStub->position])) { $stub = self::$arrayCache[$arrayStub->class][$arrayStub->position]; } else { self::$arrayCache[$arrayStub->class][$arrayStub->position] = $stub = [$arrayStub->class => $arrayStub->position]; } } if (!$zvalRef) { $vals[$k] = $stub; } else { $hardRefs[$zvalRef]->value = $stub; } } $queue[$i] = $vals; } return $queue; } } ================================================ FILE: Command/Descriptor/CliDescriptor.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Command\Descriptor; use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\VarDumper\Cloner\Data; use Symfony\Component\VarDumper\Dumper\CliDumper; /** * Describe collected data clones for cli output. * * @author Maxime Steinhausser * * @final */ class CliDescriptor implements DumpDescriptorInterface { private mixed $lastIdentifier = null; public function __construct( private CliDumper $dumper, ) { } public function describe(OutputInterface $output, Data $data, array $context, int $clientId): void { $io = $output instanceof SymfonyStyle ? $output : new SymfonyStyle(new ArrayInput([]), $output); $this->dumper->setColors($output->isDecorated()); $rows = [['date', date('r', (int) $context['timestamp'])]]; $lastIdentifier = $this->lastIdentifier; $this->lastIdentifier = $clientId; $section = "Received from client #$clientId"; if (isset($context['request'])) { $request = $context['request']; $this->lastIdentifier = $request['identifier']; $section = \sprintf('%s %s', $request['method'], $request['uri']); if ($controller = $request['controller']) { $rows[] = ['controller', rtrim($this->dumper->dump($controller, true), "\n")]; } } elseif (isset($context['cli'])) { $this->lastIdentifier = $context['cli']['identifier']; $section = '$ '.$context['cli']['command_line']; } if ($this->lastIdentifier !== $lastIdentifier) { $io->section($section); } if (isset($context['source'])) { $source = $context['source']; $sourceInfo = \sprintf('%s on line %d', $source['name'], $source['line']); if ($fileLink = $source['file_link'] ?? null) { $sourceInfo = \sprintf('%s', $fileLink, $sourceInfo); } $rows[] = ['source', $sourceInfo]; $file = $source['file_relative'] ?? $source['file']; $rows[] = ['file', $file]; } $io->table([], $rows); $this->dumper->dump($data); $io->newLine(); } } ================================================ FILE: Command/Descriptor/DumpDescriptorInterface.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Command\Descriptor; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\VarDumper\Cloner\Data; /** * @author Maxime Steinhausser */ interface DumpDescriptorInterface { public function describe(OutputInterface $output, Data $data, array $context, int $clientId): void; } ================================================ FILE: Command/Descriptor/HtmlDescriptor.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Command\Descriptor; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\VarDumper\Cloner\Data; use Symfony\Component\VarDumper\Dumper\HtmlDumper; /** * Describe collected data clones for html output. * * @author Maxime Steinhausser * * @final */ class HtmlDescriptor implements DumpDescriptorInterface { private bool $initialized = false; public function __construct( private HtmlDumper $dumper, ) { } public function describe(OutputInterface $output, Data $data, array $context, int $clientId): void { if (!$this->initialized) { $styles = file_get_contents(__DIR__.'/../../Resources/css/htmlDescriptor.css'); $scripts = file_get_contents(__DIR__.'/../../Resources/js/htmlDescriptor.js'); $output->writeln(""); $this->initialized = true; } $title = '-'; if (isset($context['request'])) { $request = $context['request']; $controller = "{$this->dumper->dump($request['controller'], true, ['maxDepth' => 0])}"; $title = \sprintf('%s %s', $request['method'], $uri = $request['uri'], $uri); $dedupIdentifier = $request['identifier']; } elseif (isset($context['cli'])) { $title = '$ '.$context['cli']['command_line']; $dedupIdentifier = $context['cli']['identifier']; } else { $dedupIdentifier = bin2hex(random_bytes(4)); } $sourceDescription = ''; if (isset($context['source'])) { $source = $context['source']; $projectDir = $source['project_dir'] ?? null; $sourceDescription = \sprintf('%s on line %d', $source['name'], $source['line']); if (isset($source['file_link'])) { $sourceDescription = \sprintf('%s', $source['file_link'], $sourceDescription); } } $isoDate = $this->extractDate($context, 'c'); $tags = array_filter([ 'controller' => $controller ?? null, 'project dir' => $projectDir ?? null, ]); $output->writeln(<<

$title

{$this->renderTags($tags)}

$sourceDescription

{$this->dumper->dump($data, true)}
HTML ); } private function extractDate(array $context, string $format = 'r'): string { return date($format, (int) $context['timestamp']); } private function renderTags(array $tags): string { if (!$tags) { return ''; } $renderedTags = ''; foreach ($tags as $key => $value) { $renderedTags .= \sprintf('
  • %s%s
  • ', $key, $value); } return <<
      $renderedTags
    HTML; } } ================================================ FILE: Command/ServerDumpCommand.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Command; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Completion\CompletionInput; use Symfony\Component\Console\Completion\CompletionSuggestions; use Symfony\Component\Console\Exception\InvalidArgumentException; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\VarDumper\Cloner\Data; use Symfony\Component\VarDumper\Command\Descriptor\CliDescriptor; use Symfony\Component\VarDumper\Command\Descriptor\DumpDescriptorInterface; use Symfony\Component\VarDumper\Command\Descriptor\HtmlDescriptor; use Symfony\Component\VarDumper\Dumper\CliDumper; use Symfony\Component\VarDumper\Dumper\HtmlDumper; use Symfony\Component\VarDumper\Server\DumpServer; /** * Starts a dump server to collect and output dumps on a single place with multiple formats support. * * @author Maxime Steinhausser * * @final */ #[AsCommand(name: 'server:dump', description: 'Start a dump server that collects and displays dumps in a single place')] class ServerDumpCommand extends Command { /** @var DumpDescriptorInterface[] */ private array $descriptors; public function __construct( private DumpServer $server, array $descriptors = [], ) { $this->descriptors = $descriptors + [ 'cli' => new CliDescriptor(new CliDumper()), 'html' => new HtmlDescriptor(new HtmlDumper()), ]; parent::__construct(); } protected function configure(): void { $this ->addOption('format', null, InputOption::VALUE_REQUIRED, \sprintf('The output format (%s)', implode(', ', $this->getAvailableFormats())), 'cli') ->setHelp(<<<'EOF' %command.name% starts a dump server that collects and displays dumps in a single place for debugging you application: php %command.full_name% You can consult dumped data in HTML format in your browser by providing the --format=html option and redirecting the output to a file: php %command.full_name% --format="html" > dump.html EOF ) ; } protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); $format = $input->getOption('format'); if (!$descriptor = $this->descriptors[$format] ?? null) { throw new InvalidArgumentException(\sprintf('Unsupported format "%s".', $format)); } $errorIo = $io->getErrorStyle(); $errorIo->title('Symfony Var Dumper Server'); $this->server->start(); $errorIo->success(\sprintf('Server listening on %s', $this->server->getHost())); $errorIo->comment('Quit the server with CONTROL-C.'); $this->server->listen(static function (Data $data, array $context, int $clientId) use ($descriptor, $io) { $descriptor->describe($io, $data, $context, $clientId); }); return 0; } public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void { if ($input->mustSuggestOptionValuesFor('format')) { $suggestions->suggestValues($this->getAvailableFormats()); } } private function getAvailableFormats(): array { return array_keys($this->descriptors); } } ================================================ FILE: Dumper/AbstractDumper.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Dumper; use Symfony\Component\VarDumper\Cloner\Data; use Symfony\Component\VarDumper\Cloner\DumperInterface; /** * Abstract mechanism for dumping a Data object. * * @author Nicolas Grekas */ abstract class AbstractDumper implements DataDumperInterface, DumperInterface { public const DUMP_LIGHT_ARRAY = 1; public const DUMP_STRING_LENGTH = 2; public const DUMP_COMMA_SEPARATOR = 4; public const DUMP_TRAILING_COMMA = 8; /** @var callable|resource|string|null */ public static $defaultOutput = 'php://output'; protected string $line = ''; /** @var callable|null */ protected $lineDumper; /** @var resource|null */ protected $outputStream; protected string $decimalPoint = '.'; protected string $indentPad = ' '; private string $charset = ''; /** * @param callable|resource|string|null $output A line dumper callable, an opened stream or an output path, defaults to static::$defaultOutput * @param string|null $charset The default character encoding to use for non-UTF8 strings * @param int $flags A bit field of static::DUMP_* constants to fine tune dumps representation */ public function __construct( $output = null, ?string $charset = null, protected int $flags = 0, ) { $this->setCharset($charset ?: \ini_get('php.output_encoding') ?: \ini_get('default_charset') ?: 'UTF-8'); $this->setOutput($output ?: static::$defaultOutput); if (!$output && \is_string(static::$defaultOutput)) { static::$defaultOutput = $this->outputStream; } } /** * Sets the output destination of the dumps. * * @param callable|resource|string|null $output A line dumper callable, an opened stream or an output path * * @return callable|resource|string|null The previous output destination */ public function setOutput($output) { $prev = $this->outputStream ?? $this->lineDumper; if (\is_callable($output)) { $this->outputStream = null; $this->lineDumper = $output; } else { if (\is_string($output)) { $output = fopen($output, 'w'); } $this->outputStream = $output; $this->lineDumper = $this->echoLine(...); } return $prev; } /** * Sets the default character encoding to use for non-UTF8 strings. * * @return string The previous charset */ public function setCharset(string $charset): string { $prev = $this->charset; $charset = strtoupper($charset); $charset = 'UTF-8' === $charset || 'UTF8' === $charset ? 'CP1252' : $charset; $this->charset = $charset; return $prev; } /** * Sets the indentation pad string. * * @param string $pad A string that will be prepended to dumped lines, repeated by nesting level * * @return string The previous indent pad */ public function setIndentPad(string $pad): string { $prev = $this->indentPad; $this->indentPad = $pad; return $prev; } /** * Dumps a Data object. * * @param callable|resource|string|true|null $output A line dumper callable, an opened stream, an output path or true to return the dump * * @return string|null The dump as string when $output is true */ public function dump(Data $data, $output = null): ?string { if ($locale = $this->flags & (self::DUMP_COMMA_SEPARATOR | self::DUMP_TRAILING_COMMA) ? setlocale(\LC_NUMERIC, 0) : null) { setlocale(\LC_NUMERIC, 'C'); } if ($returnDump = true === $output) { $output = fopen('php://memory', 'r+'); } if ($output) { $prevOutput = $this->setOutput($output); } try { $data->dump($this); $this->dumpLine(-1); if ($returnDump) { $result = stream_get_contents($output, -1, 0); fclose($output); return $result; } } finally { if ($output) { $this->setOutput($prevOutput); } if ($locale) { setlocale(\LC_NUMERIC, $locale); } } return null; } /** * Dumps the current line. * * @param int $depth The recursive depth in the dumped structure for the line being dumped, * or -1 to signal the end-of-dump to the line dumper callable */ protected function dumpLine(int $depth): void { ($this->lineDumper)($this->line, $depth, $this->indentPad); $this->line = ''; } /** * Generic line dumper callback. */ protected function echoLine(string $line, int $depth, string $indentPad): void { if (-1 !== $depth) { fwrite($this->outputStream, str_repeat($indentPad, $depth).$line."\n"); } } /** * Converts a non-UTF-8 string to UTF-8. */ protected function utf8Encode(?string $s): ?string { if (null === $s || preg_match('//u', $s)) { return $s; } if (\function_exists('iconv')) { if (false !== $c = @iconv($this->charset, 'UTF-8', $s)) { return $c; } if ('CP1252' !== $this->charset && false !== $c = @iconv('CP1252', 'UTF-8', $s)) { return $c; } } $s .= $s; $len = \strlen($s); $mapCp1252 = false; for ($i = $len >> 1, $j = 0; $i < $len; ++$i, ++$j) { if ($s[$i] < "\x80") { $s[$j] = $s[$i]; } elseif ($s[$i] < "\xC0") { $s[$j] = "\xC2"; $s[++$j] = $s[$i]; if ($s[$i] < "\xA0") { $mapCp1252 = true; } } else { $s[$j] = "\xC3"; $s[++$j] = \chr(\ord($s[$i]) - 64); } } $s = substr($s, 0, $j); if (!$mapCp1252) { return $s; } return strtr($s, [ "\xC2\x80" => '€', "\xC2\x82" => '‚', "\xC2\x83" => 'ƒ', "\xC2\x84" => '„', "\xC2\x85" => '…', "\xC2\x86" => '†', "\xC2\x87" => '‡', "\xC2\x88" => 'ˆ', "\xC2\x89" => '‰', "\xC2\x8A" => 'Š', "\xC2\x8B" => '‹', "\xC2\x8C" => 'Œ', "\xC2\x8D" => 'Ž', "\xC2\x91" => '‘', "\xC2\x92" => '’', "\xC2\x93" => '“', "\xC2\x94" => '”', "\xC2\x95" => '•', "\xC2\x96" => '–', "\xC2\x97" => '—', "\xC2\x98" => '˜', "\xC2\x99" => '™', "\xC2\x9A" => 'š', "\xC2\x9B" => '›', "\xC2\x9C" => 'œ', "\xC2\x9E" => 'ž', ]); } } ================================================ FILE: Dumper/CliDumper.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Dumper; use Symfony\Component\ErrorHandler\ErrorRenderer\FileLinkFormatter; use Symfony\Component\VarDumper\Cloner\Cursor; use Symfony\Component\VarDumper\Cloner\Stub; /** * CliDumper dumps variables for command line output. * * @author Nicolas Grekas */ class CliDumper extends AbstractDumper { public static bool $defaultColors; /** @var callable|resource|string|null */ public static $defaultOutput = 'php://stdout'; protected bool $colors; protected int $maxStringWidth = 0; protected array $styles = [ // See http://en.wikipedia.org/wiki/ANSI_escape_code#graphics 'default' => '0;38;5;208', 'num' => '1;38;5;38', 'const' => '1;38;5;208', 'virtual' => '3', 'str' => '1;38;5;113', 'note' => '38;5;38', 'ref' => '38;5;247', 'public' => '39', 'protected' => '39', 'private' => '39', 'meta' => '38;5;170', 'key' => '38;5;113', 'index' => '38;5;38', ]; protected static string $controlCharsRx = '/[\x00-\x1F\x7F]+/'; protected static array $controlCharsMap = [ "\t" => '\t', "\n" => '\n', "\v" => '\v', "\f" => '\f', "\r" => '\r', "\033" => '\e', ]; protected static string $unicodeCharsRx = "/[\u{00A0}\u{00AD}\u{034F}\u{061C}\u{115F}\u{1160}\u{17B4}\u{17B5}\u{180E}\u{2000}-\u{200F}\u{202F}\u{205F}\u{2060}-\u{2064}\u{206A}-\u{206F}\u{3000}\u{2800}\u{3164}\u{FEFF}\u{FFA0}\u{1D159}\u{1D173}-\u{1D17A}]/u"; protected bool $collapseNextHash = false; protected bool $expandNextHash = false; private array $displayOptions = [ 'fileLinkFormat' => null, ]; private bool $handlesHrefGracefully; public function __construct($output = null, ?string $charset = null, int $flags = 0) { parent::__construct($output, $charset, $flags); if ('\\' === \DIRECTORY_SEPARATOR && !$this->isWindowsTrueColor()) { // Use only the base 16 xterm colors when using ANSICON or standard Windows 10 CLI $this->setStyles([ 'default' => '31', 'num' => '1;34', 'const' => '1;31', 'str' => '1;32', 'note' => '34', 'ref' => '1;30', 'meta' => '35', 'key' => '32', 'index' => '34', ]); } $this->displayOptions['fileLinkFormat'] = class_exists(FileLinkFormatter::class) ? new FileLinkFormatter() : (\ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format') ?: 'file://%f#L%l'); } /** * Enables/disables colored output. */ public function setColors(bool $colors): void { $this->colors = $colors; } /** * Sets the maximum number of characters per line for dumped strings. */ public function setMaxStringWidth(int $maxStringWidth): void { $this->maxStringWidth = $maxStringWidth; } /** * Configures styles. * * @param array $styles A map of style names to style definitions */ public function setStyles(array $styles): void { $this->styles = $styles + $this->styles; } /** * Configures display options. * * @param array $displayOptions A map of display options to customize the behavior */ public function setDisplayOptions(array $displayOptions): void { $this->displayOptions = $displayOptions + $this->displayOptions; } public function dumpScalar(Cursor $cursor, string $type, string|int|float|bool|null $value): void { $this->dumpKey($cursor); $this->collapseNextHash = $this->expandNextHash = false; $style = 'const'; $attr = $cursor->attr; switch ($type) { case 'default': $style = 'default'; break; case 'label': $this->styles += ['label' => $this->styles['default']]; $style = 'label'; break; case 'integer': $style = 'num'; if (isset($this->styles['integer'])) { $style = 'integer'; } break; case 'double': $style = 'num'; if (isset($this->styles['float'])) { $style = 'float'; } $value = match (true) { \INF === $value => 'INF', -\INF === $value => '-INF', is_nan($value) => 'NAN', default => !str_contains($value = (string) $value, $this->decimalPoint) ? $value .= $this->decimalPoint.'0' : $value, }; break; case 'NULL': $value = 'null'; break; case 'boolean': $value = $value ? 'true' : 'false'; break; default: $attr += ['value' => $this->utf8Encode($value)]; $value = $this->utf8Encode($type); break; } $this->line .= $this->style($style, $value, $attr); $this->endValue($cursor); } public function dumpString(Cursor $cursor, string $str, bool $bin, int $cut): void { $this->dumpKey($cursor); $this->collapseNextHash = $this->expandNextHash = false; $attr = $cursor->attr; if ($bin) { $str = $this->utf8Encode($str); } if ('' === $str) { $this->line .= '""'; if ($cut) { $this->line .= '…'.$cut; } $this->endValue($cursor); } else { $attr += [ 'length' => 0 <= $cut ? mb_strlen($str, 'UTF-8') + $cut : 0, 'binary' => $bin, ]; $str = $bin && str_contains($str, "\0") ? [$str] : explode("\n", $str); if (isset($str[1]) && !isset($str[2]) && !isset($str[1][0])) { unset($str[1]); $str[0] .= "\n"; } $m = \count($str) - 1; $i = $lineCut = 0; if (self::DUMP_STRING_LENGTH & $this->flags) { $this->line .= '('.$attr['length'].') '; } if ($bin) { $this->line .= 'b'; } if ($m) { $this->line .= '"""'; $this->dumpLine($cursor->depth); } else { $this->line .= '"'; } foreach ($str as $str) { if ($i < $m) { $str .= "\n"; } if (0 < $this->maxStringWidth && $this->maxStringWidth < $len = mb_strlen($str, 'UTF-8')) { $str = mb_substr($str, 0, $this->maxStringWidth, 'UTF-8'); $lineCut = $len - $this->maxStringWidth; } if ($m && 0 < $cursor->depth) { $this->line .= $this->indentPad; } if ('' !== $str) { $this->line .= $this->style('str', $str, $attr); } if ($i++ == $m) { if ($m) { if ('' !== $str) { $this->dumpLine($cursor->depth); if (0 < $cursor->depth) { $this->line .= $this->indentPad; } } $this->line .= '"""'; } else { $this->line .= '"'; } if ($cut < 0) { $this->line .= '…'; $lineCut = 0; } elseif ($cut) { $lineCut += $cut; } } if ($lineCut) { $this->line .= '…'.$lineCut; $lineCut = 0; } if ($i > $m) { $this->endValue($cursor); } else { $this->dumpLine($cursor->depth); } } } } public function enterHash(Cursor $cursor, int $type, string|int|null $class, bool $hasChild): void { $this->colors ??= $this->supportsColors(); $this->dumpKey($cursor); $this->expandNextHash = false; $attr = $cursor->attr; if ($this->collapseNextHash) { $cursor->skipChildren = true; $this->collapseNextHash = $hasChild = false; } $class = $this->utf8Encode($class); if (Cursor::HASH_OBJECT === $type) { $prefix = $class && 'stdClass' !== $class ? $this->style('note', $class, $attr).(empty($attr['cut_hash']) ? ' {' : '') : '{'; } elseif (Cursor::HASH_RESOURCE === $type) { $prefix = $this->style('note', $class.' resource', $attr).($hasChild ? ' {' : ' '); } else { $prefix = $class && !(self::DUMP_LIGHT_ARRAY & $this->flags) ? $this->style('note', 'array:'.$class).' [' : '['; } if (($cursor->softRefCount || 0 < $cursor->softRefHandle) && empty($attr['cut_hash'])) { $prefix .= $this->style('ref', (Cursor::HASH_RESOURCE === $type ? '@' : '#').(0 < $cursor->softRefHandle ? $cursor->softRefHandle : $cursor->softRefTo), ['count' => $cursor->softRefCount]); } elseif ($cursor->hardRefTo && !$cursor->refIndex && $class) { $prefix .= $this->style('ref', '&'.$cursor->hardRefTo, ['count' => $cursor->hardRefCount]); } elseif (!$hasChild && Cursor::HASH_RESOURCE === $type) { $prefix = substr($prefix, 0, -1); } $this->line .= $prefix; if ($hasChild) { $this->dumpLine($cursor->depth); } } public function leaveHash(Cursor $cursor, int $type, string|int|null $class, bool $hasChild, int $cut): void { if (empty($cursor->attr['cut_hash'])) { $this->dumpEllipsis($cursor, $hasChild, $cut); $this->line .= Cursor::HASH_OBJECT === $type ? '}' : (Cursor::HASH_RESOURCE !== $type ? ']' : ($hasChild ? '}' : '')); } $this->endValue($cursor); } /** * Dumps an ellipsis for cut children. * * @param bool $hasChild When the dump of the hash has child item * @param int $cut The number of items the hash has been cut by */ protected function dumpEllipsis(Cursor $cursor, bool $hasChild, int $cut): void { if ($cut) { $this->line .= ' …'; if (0 < $cut) { $this->line .= $cut; } if ($hasChild) { $this->dumpLine($cursor->depth + 1); } } } /** * Dumps a key in a hash structure. */ protected function dumpKey(Cursor $cursor): void { if (null !== $key = $cursor->hashKey) { if ($cursor->hashKeyIsBinary) { $key = $this->utf8Encode($key); } $attr = [ 'binary' => $cursor->hashKeyIsBinary, 'virtual' => $cursor->attr['virtual'] ?? false, ]; $bin = $cursor->hashKeyIsBinary ? 'b' : ''; $style = 'key'; switch ($cursor->hashType) { default: case Cursor::HASH_INDEXED: if (self::DUMP_LIGHT_ARRAY & $this->flags) { break; } $style = 'index'; // no break case Cursor::HASH_ASSOC: if (\is_int($key)) { $this->line .= $this->style($style, $key).' => '; } else { $this->line .= $bin.'"'.$this->style($style, $key).'" => '; } break; case Cursor::HASH_RESOURCE: $key = "\0~\0".$key; // no break case Cursor::HASH_OBJECT: if (!isset($key[0]) || "\0" !== $key[0]) { $this->line .= '+'.$bin.$this->style('public', $key, $attr).': '; } elseif (0 < strpos($key, "\0", 1)) { $key = explode("\0", substr($key, 1), 2); switch ($key[0][0]) { case '+': // User inserted keys $attr['dynamic'] = true; $this->line .= '+'.$bin.'"'.$this->style('public', $key[1], $attr).'": '; break 2; case '~': $style = 'meta'; if (isset($key[0][1])) { parse_str(substr($key[0], 1), $attr); $attr += ['binary' => $cursor->hashKeyIsBinary]; } break; case '*': $style = 'protected'; $bin = '#'.$bin; break; default: $attr['class'] = $key[0]; $style = 'private'; $bin = '-'.$bin; break; } if (isset($attr['collapse'])) { if ($attr['collapse']) { $this->collapseNextHash = true; } else { $this->expandNextHash = true; } } $this->line .= $bin.$this->style($style, $key[1], $attr).($attr['separator'] ?? ': '); } else { // This case should not happen $this->line .= '-'.$bin.'"'.$this->style('private', $key, ['class' => '']).'": '; } break; } if ($cursor->hardRefTo) { $this->line .= $this->style('ref', '&'.($cursor->hardRefCount ? $cursor->hardRefTo : ''), ['count' => $cursor->hardRefCount]).' '; } } } /** * Decorates a value with some style. * * @param string $style The type of style being applied * @param string $value The value being styled * @param array $attr Optional context information */ protected function style(string $style, string $value, array $attr = []): string { $this->colors ??= $this->supportsColors(); $this->handlesHrefGracefully ??= 'JetBrains-JediTerm' !== getenv('TERMINAL_EMULATOR') && (!getenv('KONSOLE_VERSION') || (int) getenv('KONSOLE_VERSION') > 201100) && !isset($_SERVER['IDEA_INITIAL_DIRECTORY']); if (isset($attr['ellipsis'], $attr['ellipsis-type'])) { $prefix = substr($value, 0, -$attr['ellipsis']); if ('cli' === \PHP_SAPI && 'path' === $attr['ellipsis-type'] && isset($_SERVER[$pwd = '\\' === \DIRECTORY_SEPARATOR ? 'CD' : 'PWD']) && str_starts_with($prefix, $_SERVER[$pwd])) { $prefix = '.'.substr($prefix, \strlen($_SERVER[$pwd])); } if (!empty($attr['ellipsis-tail'])) { $prefix .= substr($value, -$attr['ellipsis'], $attr['ellipsis-tail']); $value = substr($value, -$attr['ellipsis'] + $attr['ellipsis-tail']); } else { $value = substr($value, -$attr['ellipsis']); } $value = $this->style('default', $prefix).$this->style($style, $value); goto href; } $map = static::$controlCharsMap; $startCchr = $this->colors ? "\033[m\033[{$this->styles['default']}m" : ''; $endCchr = $this->colors ? "\033[m\033[{$this->styles[$style]}m" : ''; $value = preg_replace_callback(static::$controlCharsRx, static function ($c) use ($map, $startCchr, $endCchr) { $s = $startCchr; $c = $c[$i = 0]; do { $s .= $map[$c[$i]] ?? \sprintf('\x%02X', \ord($c[$i])); } while (isset($c[++$i])); return $s.$endCchr; }, $value, -1, $cchrCount); if (!($attr['binary'] ?? false)) { $value = preg_replace_callback(static::$unicodeCharsRx, static function ($c) use (&$cchrCount, $startCchr, $endCchr) { ++$cchrCount; return $startCchr.'\u{'.strtoupper(dechex(mb_ord($c[0]))).'}'.$endCchr; }, $value); } if ($this->colors && '' !== $value) { if ($cchrCount && "\033" === $value[0]) { $value = substr($value, \strlen($startCchr)); } else { $value = "\033[{$this->styles[$style]}m".$value; } if ($cchrCount && str_ends_with($value, $endCchr)) { $value = substr($value, 0, -\strlen($endCchr)); } else { $value .= "\033[{$this->styles['default']}m"; } } href: if ($this->colors && $this->handlesHrefGracefully) { if (isset($attr['file']) && $href = $this->getSourceLink($attr['file'], $attr['line'] ?? 0)) { if ('note' === $style) { $value .= "\033]8;;{$href}\033\\^\033]8;;\033\\"; } else { $attr['href'] = $href; } } if (isset($attr['href'])) { if ('label' === $style) { $value .= '^'; } $value = "\033]8;;{$attr['href']}\033\\{$value}\033]8;;\033\\"; } } if ('label' === $style && '' !== $value) { $value .= ' '; } if ($this->colors && ($attr['virtual'] ?? false)) { $value = "\033[{$this->styles['virtual']}m".$value; } return $value; } protected function supportsColors(): bool { if ($this->outputStream !== static::$defaultOutput) { return $this->hasColorSupport($this->outputStream); } if (isset(static::$defaultColors)) { return static::$defaultColors; } if (isset($_SERVER['argv'][1])) { $colors = $_SERVER['argv']; $i = \count($colors); while (--$i > 0) { if (isset($colors[$i][5])) { switch ($colors[$i]) { case '--ansi': case '--color': case '--color=yes': case '--color=force': case '--color=always': case '--colors=always': return static::$defaultColors = true; case '--no-ansi': case '--color=no': case '--color=none': case '--color=never': case '--colors=never': return static::$defaultColors = false; } } } } $h = stream_get_meta_data($this->outputStream) + ['wrapper_type' => null]; $h = 'Output' === $h['stream_type'] && 'PHP' === $h['wrapper_type'] ? fopen('php://stdout', 'w') : $this->outputStream; return static::$defaultColors = $this->hasColorSupport($h); } protected function dumpLine(int $depth, bool $endOfValue = false): void { if ($this->colors ??= $this->supportsColors()) { $this->line = \sprintf("\033[%sm%s\033[m", $this->styles['default'], $this->line); } parent::dumpLine($depth); } protected function endValue(Cursor $cursor): void { if (-1 === $cursor->hashType) { return; } if (Stub::ARRAY_INDEXED === $cursor->hashType || Stub::ARRAY_ASSOC === $cursor->hashType) { if (self::DUMP_TRAILING_COMMA & $this->flags && 0 < $cursor->depth) { $this->line .= ','; } elseif (self::DUMP_COMMA_SEPARATOR & $this->flags && 1 < $cursor->hashLength - $cursor->hashIndex) { $this->line .= ','; } } $this->dumpLine($cursor->depth, true); } /** * Returns true if the stream supports colorization. * * Reference: Composer\XdebugHandler\Process::supportsColor * https://github.com/composer/xdebug-handler */ private function hasColorSupport(mixed $stream): bool { if (!\is_resource($stream) || 'stream' !== get_resource_type($stream)) { return false; } // Follow https://no-color.org/ if ('' !== (($_SERVER['NO_COLOR'] ?? getenv('NO_COLOR'))[0] ?? '')) { return false; } // Follow https://force-color.org/ if ('' !== (($_SERVER['FORCE_COLOR'] ?? getenv('FORCE_COLOR'))[0] ?? '')) { return true; } // Detect msysgit/mingw and assume this is a tty because detection // does not work correctly, see https://github.com/composer/composer/issues/9690 if (!@stream_isatty($stream) && !\in_array(strtoupper((string) getenv('MSYSTEM')), ['MINGW32', 'MINGW64'], true)) { return false; } if ('\\' === \DIRECTORY_SEPARATOR && @sapi_windows_vt100_support($stream)) { return true; } if ('Hyper' === getenv('TERM_PROGRAM') || false !== getenv('COLORTERM') || false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI') ) { return true; } if ('dumb' === $term = (string) getenv('TERM')) { return false; } // See https://github.com/chalk/supports-color/blob/d4f413efaf8da045c5ab440ed418ef02dbb28bf1/index.js#L157 return preg_match('/^((screen|xterm|vt100|vt220|putty|rxvt|ansi|cygwin|linux).*)|(.*-256(color)?(-bce)?)$/', $term); } /** * Returns true if the Windows terminal supports true color. * * Note that this does not check an output stream, but relies on environment * variables from known implementations, or a PHP and Windows version that * supports true color. */ private function isWindowsTrueColor(): bool { $result = 183 <= getenv('ANSICON_VER') || 'ON' === getenv('ConEmuANSI') || 'xterm' === getenv('TERM') || 'Hyper' === getenv('TERM_PROGRAM'); if (!$result) { $version = \sprintf( '%s.%s.%s', PHP_WINDOWS_VERSION_MAJOR, PHP_WINDOWS_VERSION_MINOR, PHP_WINDOWS_VERSION_BUILD ); $result = $version >= '10.0.15063'; } return $result; } private function getSourceLink(string $file, int $line): string|false { if ($fmt = $this->displayOptions['fileLinkFormat']) { return \is_string($fmt) ? strtr($fmt, ['%f' => $file, '%l' => $line]) : ($fmt->format($file, $line) ?: 'file://'.$file.'#L'.$line); } return false; } } ================================================ FILE: Dumper/ContextProvider/CliContextProvider.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Dumper\ContextProvider; /** * Tries to provide context on CLI. * * @author Maxime Steinhausser */ final class CliContextProvider implements ContextProviderInterface { public function getContext(): ?array { if ('cli' !== \PHP_SAPI) { return null; } return [ 'command_line' => $commandLine = implode(' ', $_SERVER['argv'] ?? []), 'identifier' => hash('xxh128', $commandLine.'@'.$_SERVER['REQUEST_TIME_FLOAT']), ]; } } ================================================ FILE: Dumper/ContextProvider/ContextProviderInterface.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Dumper\ContextProvider; /** * Interface to provide contextual data about dump data clones sent to a server. * * @author Maxime Steinhausser */ interface ContextProviderInterface { public function getContext(): ?array; } ================================================ FILE: Dumper/ContextProvider/RequestContextProvider.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Dumper\ContextProvider; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\VarDumper\Caster\ReflectionCaster; use Symfony\Component\VarDumper\Cloner\VarCloner; /** * Tries to provide context from a request. * * @author Maxime Steinhausser */ final class RequestContextProvider implements ContextProviderInterface { private VarCloner $cloner; public function __construct( private RequestStack $requestStack, ) { $this->cloner = new VarCloner(); $this->cloner->setMaxItems(0); $this->cloner->addCasters(ReflectionCaster::UNSET_CLOSURE_FILE_INFO); } public function getContext(): ?array { if (null === $request = $this->requestStack->getCurrentRequest()) { return null; } $controller = $request->attributes->get('_controller'); return [ 'uri' => $request->getUri(), 'method' => $request->getMethod(), 'controller' => $controller ? $this->cloner->cloneVar($controller) : $controller, 'identifier' => hash('xxh128', spl_object_id($request).'@'.$_SERVER['REQUEST_TIME_FLOAT']), ]; } } ================================================ FILE: Dumper/ContextProvider/SourceContextProvider.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Dumper\ContextProvider; use Symfony\Component\ErrorHandler\ErrorRenderer\FileLinkFormatter; use Symfony\Component\VarDumper\Cloner\VarCloner; use Symfony\Component\VarDumper\Dumper\HtmlDumper; use Symfony\Component\VarDumper\VarDumper; use Twig\Template; /** * Tries to provide context from sources (class name, file, line, code excerpt, ...). * * @author Nicolas Grekas * @author Maxime Steinhausser */ final class SourceContextProvider implements ContextProviderInterface { public function __construct( private ?string $charset = null, private ?string $projectDir = null, private ?FileLinkFormatter $fileLinkFormatter = null, private int $limit = 9, ) { } public function getContext(): ?array { $trace = debug_backtrace(\DEBUG_BACKTRACE_PROVIDE_OBJECT | \DEBUG_BACKTRACE_IGNORE_ARGS, $this->limit); $file = $trace[1]['file']; $line = $trace[1]['line']; $name = '-' === $file || 'Standard input code' === $file ? 'Standard input code' : false; $fileExcerpt = false; for ($i = 2; $i < $this->limit; ++$i) { if (isset($trace[$i]['class'], $trace[$i]['function']) && 'dump' === $trace[$i]['function'] && VarDumper::class === $trace[$i]['class'] ) { $file = $trace[$i]['file'] ?? $file; $line = $trace[$i]['line'] ?? $line; while (++$i < $this->limit) { if (isset($trace[$i]['function'], $trace[$i]['file']) && empty($trace[$i]['class']) && !str_starts_with($trace[$i]['function'], 'call_user_func')) { $file = $trace[$i]['file']; $line = $trace[$i]['line']; break; } elseif (isset($trace[$i]['object']) && $trace[$i]['object'] instanceof Template) { $template = $trace[$i]['object']; $name = $template->getTemplateName(); $src = method_exists($template, 'getSourceContext') ? $template->getSourceContext()->getCode() : (method_exists($template, 'getSource') ? $template->getSource() : false); $info = $template->getDebugInfo(); if (isset($info[$trace[$i - 1]['line']])) { $line = $info[$trace[$i - 1]['line']]; $file = method_exists($template, 'getSourceContext') ? $template->getSourceContext()->getPath() : null; if ($src) { $src = explode("\n", $src); $fileExcerpt = []; for ($i = max($line - 3, 1), $max = min($line + 3, \count($src)); $i <= $max; ++$i) { $fileExcerpt[] = ''.$this->htmlEncode($src[$i - 1]).''; } $fileExcerpt = '
      '.implode("\n", $fileExcerpt).'
    '; } } break; } } break; } } if (false === $name) { $name = str_replace('\\', '/', $file); $name = substr($name, strrpos($name, '/') + 1); } $context = ['name' => $name, 'file' => $file, 'line' => $line]; $context['file_excerpt'] = $fileExcerpt; 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); } } if ($this->fileLinkFormatter && $fileLink = $this->fileLinkFormatter->format($context['file'], $context['line'])) { $context['file_link'] = $fileLink; } return $context; } private function htmlEncode(string $s): string { $html = ''; $dumper = new HtmlDumper(static function ($line) use (&$html) { $html .= $line; }, $this->charset); $dumper->setDumpHeader(''); $dumper->setDumpBoundaries('', ''); $cloner = new VarCloner(); $dumper->dump($cloner->cloneVar($s)); return substr(strip_tags($html), 1, -1); } } ================================================ FILE: Dumper/ContextualizedDumper.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Dumper; use Symfony\Component\VarDumper\Cloner\Data; use Symfony\Component\VarDumper\Dumper\ContextProvider\ContextProviderInterface; /** * @author Kévin Thérage */ class ContextualizedDumper implements DataDumperInterface { /** * @param ContextProviderInterface[] $contextProviders */ public function __construct( private DataDumperInterface $wrappedDumper, private array $contextProviders, ) { } public function dump(Data $data): ?string { $context = $data->getContext(); foreach ($this->contextProviders as $contextProvider) { $context[$contextProvider::class] = $contextProvider->getContext(); } return $this->wrappedDumper->dump($data->withContext($context)); } } ================================================ FILE: Dumper/DataDumperInterface.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Dumper; use Symfony\Component\VarDumper\Cloner\Data; /** * DataDumperInterface for dumping Data objects. * * @author Nicolas Grekas */ interface DataDumperInterface { public function dump(Data $data): ?string; } ================================================ FILE: Dumper/HtmlDumper.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Dumper; use Symfony\Component\VarDumper\Cloner\Cursor; use Symfony\Component\VarDumper\Cloner\Data; /** * HtmlDumper dumps variables as HTML. * * @author Nicolas Grekas */ class HtmlDumper extends CliDumper { /** @var callable|resource|string|null */ public static $defaultOutput = 'php://output'; protected static $themes = [ 'dark' => [ 'default' => 'background-color:#18171B; color:#FF8400; line-height:1.2em; font:12px Menlo, Monaco, Consolas, monospace; word-wrap: break-word; white-space: pre-wrap; position:relative; z-index:99999; word-break: break-all', 'num' => 'font-weight:bold; color:#1299DA', 'const' => 'font-weight:bold', 'virtual' => 'font-style:italic', 'str' => 'font-weight:bold; color:#56DB3A', 'note' => 'color:#1299DA', 'ref' => 'color:#A0A0A0', 'public' => 'color:#FFFFFF', 'protected' => 'color:#FFFFFF', 'private' => 'color:#FFFFFF', 'meta' => 'color:#B729D9', 'key' => 'color:#56DB3A', 'index' => 'color:#1299DA', 'ellipsis' => 'color:#FF8400', 'ns' => 'user-select:none;', ], 'light' => [ 'default' => 'background:none; color:#CC7832; line-height:1.2em; font:12px Menlo, Monaco, Consolas, monospace; word-wrap: break-word; white-space: pre-wrap; position:relative; z-index:99999; word-break: break-all', 'num' => 'font-weight:bold; color:#1299DA', 'const' => 'font-weight:bold', 'virtual' => 'font-style:italic', 'str' => 'font-weight:bold; color:#629755;', 'note' => 'color:#6897BB', 'ref' => 'color:#6E6E6E', 'public' => 'color:#262626', 'protected' => 'color:#262626', 'private' => 'color:#262626', 'meta' => 'color:#B729D9', 'key' => 'color:#789339', 'index' => 'color:#1299DA', 'ellipsis' => 'color:#CC7832', 'ns' => 'user-select:none;', ], ]; protected ?string $dumpHeader = null; protected string $dumpPrefix = '
    ';
        protected string $dumpSuffix = '
    '; protected string $dumpId; protected bool $colors = true; protected $headerIsDumped = false; protected int $lastDepth = -1; private array $displayOptions = [ 'maxDepth' => 1, 'maxStringLength' => 160, 'fileLinkFormat' => null, ]; private array $extraDisplayOptions = []; public function __construct($output = null, ?string $charset = null, int $flags = 0) { AbstractDumper::__construct($output, $charset, $flags); $this->dumpId = 'sf-dump-'.mt_rand(); $this->displayOptions['fileLinkFormat'] = \ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format'); $this->styles = static::$themes['dark'] ?? self::$themes['dark']; } public function setStyles(array $styles): void { $this->headerIsDumped = false; $this->styles = $styles + $this->styles; } public function setTheme(string $themeName): void { if (!isset(static::$themes[$themeName])) { throw new \InvalidArgumentException(\sprintf('Theme "%s" does not exist in class "%s".', $themeName, static::class)); } $this->setStyles(static::$themes[$themeName]); } /** * Configures display options. * * @param array $displayOptions A map of display options to customize the behavior */ public function setDisplayOptions(array $displayOptions): void { $this->headerIsDumped = false; $this->displayOptions = $displayOptions + $this->displayOptions; } /** * Sets an HTML header that will be dumped once in the output stream. */ public function setDumpHeader(?string $header): void { $this->dumpHeader = $header; } /** * Sets an HTML prefix and suffix that will encapse every single dump. */ public function setDumpBoundaries(string $prefix, string $suffix): void { $this->dumpPrefix = $prefix; $this->dumpSuffix = $suffix; } public function dump(Data $data, $output = null, array $extraDisplayOptions = []): ?string { $this->extraDisplayOptions = $extraDisplayOptions; $result = parent::dump($data, $output); $this->dumpId = 'sf-dump-'.mt_rand(); return $result; } /** * Dumps the HTML header. */ protected function getDumpHeader(): string { $this->headerIsDumped = $this->outputStream ?? $this->lineDumper; if (null !== $this->dumpHeader) { return $this->dumpHeader; } $line = str_replace('{$options}', json_encode($this->displayOptions, \JSON_FORCE_OBJECT), <<<'EOHTML' '.$this->dumpHeader; } public function dumpString(Cursor $cursor, string $str, bool $bin, int $cut): void { if ('' === $str && isset($cursor->attr['img-data'], $cursor->attr['content-type'])) { $this->dumpKey($cursor); $this->line .= $this->style('default', $cursor->attr['img-size'] ?? '', []); $this->line .= $cursor->depth >= $this->displayOptions['maxDepth'] ? ' ' : ' '; $this->endValue($cursor); $this->line .= $this->indentPad; $this->line .= \sprintf('', $cursor->attr['content-type'], base64_encode($cursor->attr['img-data'])); $this->endValue($cursor); } else { parent::dumpString($cursor, $str, $bin, $cut); } } public function enterHash(Cursor $cursor, int $type, string|int|null $class, bool $hasChild): void { if (Cursor::HASH_OBJECT === $type) { $cursor->attr['depth'] = $cursor->depth; } parent::enterHash($cursor, $type, $class, false); if ($cursor->skipChildren || $cursor->depth >= $this->displayOptions['maxDepth']) { $cursor->skipChildren = false; $eol = ' class=sf-dump-compact>'; } else { $this->expandNextHash = false; $eol = ' class=sf-dump-expanded>'; } if ($hasChild) { $this->line .= 'dumpId, $r); } $this->line .= $eol; $this->dumpLine($cursor->depth); } } public function leaveHash(Cursor $cursor, int $type, string|int|null $class, bool $hasChild, int $cut): void { $this->dumpEllipsis($cursor, $hasChild, $cut); if ($hasChild) { $this->line .= ''; } parent::leaveHash($cursor, $type, $class, $hasChild, 0); } protected function style(string $style, string $value, array $attr = []): string { if ('' === $value && ('label' !== $style || !isset($attr['file']) && !isset($attr['href']))) { return ''; } $v = esc($value); if ('ref' === $style) { if (empty($attr['count'])) { return \sprintf('%s', $v); } $r = ('#' !== $v[0] ? 1 - ('@' !== $v[0]) : 2).substr($value, 1); return \sprintf('%s', $this->dumpId, $r, 1 + $attr['count'], $v); } $dumpClasses = ['sf-dump-'.$style]; $dumpTitle = ''; if ('const' === $style && isset($attr['value'])) { $dumpTitle = esc(\is_scalar($attr['value']) ? $attr['value'] : json_encode($attr['value'])); } elseif ('public' === $style) { $dumpTitle = empty($attr['dynamic']) ? 'Public property' : 'Runtime added dynamic property'; } elseif ('str' === $style && 1 < $attr['length']) { $dumpTitle = \sprintf('%d%s characters', $attr['length'], $attr['binary'] ? ' binary or non-UTF-8' : ''); } elseif ('note' === $style && 0 < ($attr['depth'] ?? 0) && false !== $c = strrpos($value, '\\')) { $attr += [ 'ellipsis' => \strlen($value) - $c, 'ellipsis-type' => 'note', 'ellipsis-tail' => 1, ]; } elseif ('protected' === $style) { $dumpTitle = 'Protected property'; } elseif ('meta' === $style && isset($attr['title'])) { $dumpTitle = esc($this->utf8Encode($attr['title'])); } elseif ('private' === $style) { $dumpTitle = \sprintf('Private property defined in class: `%s`', esc($this->utf8Encode($attr['class']))); } if (isset($attr['ellipsis'])) { $dumpClasses[] = 'sf-dump-ellipsization'; $ellipsisClass = 'sf-dump-ellipsis'; if (isset($attr['ellipsis-type'])) { $ellipsisClass .= ' sf-dump-ellipsis-'.$attr['ellipsis-type']; } $label = esc(substr($value, -$attr['ellipsis'])); $dumpTitle = $v."\n".$dumpTitle; $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)); } else { $v .= \sprintf('%s', $label); } } $map = static::$controlCharsMap; $v = \sprintf( '%s', 1 === \count($dumpClasses) ? '' : '"', implode(' ', $dumpClasses), $dumpTitle ? ' title="'.$dumpTitle.'"' : '', preg_replace_callback(static::$controlCharsRx, static function ($c) use ($map) { $s = $b = ''; }, $v) ); if (!($attr['binary'] ?? false)) { $v = preg_replace_callback(static::$unicodeCharsRx, static fn ($c) => '\u{'.strtoupper(dechex(mb_ord($c[0]))).'}', $v); } if (isset($attr['file']) && $href = $this->getSourceLink($attr['file'], $attr['line'] ?? 0)) { $attr['href'] = $href; } if (isset($attr['href'])) { if ('label' === $style) { $v .= '^'; } $target = isset($attr['file']) ? '' : ' target="_blank"'; $v = \sprintf('%s', esc($this->utf8Encode($attr['href'])), $target, $v); } if (isset($attr['lang'])) { $v = \sprintf('%s', esc($attr['lang']), $v); } if ('label' === $style) { $v .= ' '; } if ($attr['virtual'] ?? false) { $v = ''.$v.''; } return $v; } protected function dumpLine(int $depth, bool $endOfValue = false): void { if (-1 === $this->lastDepth) { $this->line = \sprintf($this->dumpPrefix, $this->dumpId, $this->indentPad).$this->line; } if ($this->headerIsDumped !== ($this->outputStream ?? $this->lineDumper)) { $this->line = $this->getDumpHeader().$this->line; } if (-1 === $depth) { $args = ['"'.$this->dumpId.'"']; if ($this->extraDisplayOptions) { $args[] = json_encode($this->extraDisplayOptions, \JSON_FORCE_OBJECT); } // Replace is for BC $this->line .= \sprintf(str_replace('"%s"', '%s', $this->dumpSuffix), implode(', ', $args)); } $this->lastDepth = $depth; $this->line = mb_encode_numericentity($this->line, [0x80, 0x10FFFF, 0, 0x1FFFFF], 'UTF-8'); if (-1 === $depth) { AbstractDumper::dumpLine(0); } AbstractDumper::dumpLine($depth); } private function getSourceLink(string $file, int $line): string|false { $options = $this->extraDisplayOptions + $this->displayOptions; if ($fmt = $options['fileLinkFormat']) { return \is_string($fmt) ? strtr($fmt, ['%f' => $file, '%l' => $line]) : $fmt->format($file, $line); } return false; } } function esc(string $str): string { return htmlspecialchars($str, \ENT_QUOTES, 'UTF-8'); } ================================================ FILE: Dumper/ServerDumper.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Dumper; use Symfony\Component\VarDumper\Cloner\Data; use Symfony\Component\VarDumper\Dumper\ContextProvider\ContextProviderInterface; use Symfony\Component\VarDumper\Server\Connection; /** * ServerDumper forwards serialized Data clones to a server. * * @author Maxime Steinhausser */ class ServerDumper implements DataDumperInterface { private Connection $connection; /** * @param string $host The server host * @param DataDumperInterface|null $wrappedDumper A wrapped instance used whenever we failed contacting the server * @param ContextProviderInterface[] $contextProviders Context providers indexed by context name */ public function __construct( string $host, private ?DataDumperInterface $wrappedDumper = null, array $contextProviders = [], ) { $this->connection = new Connection($host, $contextProviders); } public function getContextProviders(): array { return $this->connection->getContextProviders(); } public function dump(Data $data): ?string { if (!$this->connection->write($data) && $this->wrappedDumper) { return $this->wrappedDumper->dump($data); } return null; } } ================================================ FILE: Exception/ThrowingCasterException.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Exception; /** * @author Nicolas Grekas */ class ThrowingCasterException extends \Exception { /** * @param \Throwable $prev The exception thrown from the caster */ public function __construct(\Throwable $prev) { parent::__construct('Unexpected '.$prev::class.' thrown from a caster: '.$prev->getMessage(), 0, $prev); } } ================================================ FILE: LICENSE ================================================ Copyright (c) 2014-present Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ VarDumper Component =================== The VarDumper component provides mechanisms for walking through any arbitrary PHP variable. It provides a better `dump()` function that you can use instead of `var_dump()`. Resources --------- * [Documentation](https://symfony.com/doc/current/components/var_dumper/introduction.html) * [Contributing](https://symfony.com/doc/current/contributing/index.html) * [Report issues](https://github.com/symfony/symfony/issues) and [send Pull Requests](https://github.com/symfony/symfony/pulls) in the [main Symfony repository](https://github.com/symfony/symfony) ================================================ FILE: Resources/bin/var-dump-server ================================================ #!/usr/bin/env php * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ if ('cli' !== PHP_SAPI) { throw new Exception('This script must be run from the command line.'); } /** * Starts a dump server to collect and output dumps on a single place with multiple formats support. * * @author Maxime Steinhausser */ use Psr\Log\LoggerInterface; use Symfony\Component\Console\Application; use Symfony\Component\Console\Input\ArgvInput; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Logger\ConsoleLogger; use Symfony\Component\Console\Output\ConsoleOutput; use Symfony\Component\VarDumper\Command\ServerDumpCommand; use Symfony\Component\VarDumper\Server\DumpServer; function includeIfExists(string $file): bool { return file_exists($file) && include $file; } if ( !includeIfExists(__DIR__ . '/../../../../autoload.php') && !includeIfExists(__DIR__ . '/../../vendor/autoload.php') && !includeIfExists(__DIR__ . '/../../../../../../vendor/autoload.php') ) { fwrite(STDERR, 'Install dependencies using Composer.'.PHP_EOL); exit(1); } if (!class_exists(Application::class)) { fwrite(STDERR, 'You need the "symfony/console" component in order to run the VarDumper server.'.PHP_EOL); exit(1); } $input = new ArgvInput(); $output = new ConsoleOutput(); $defaultHost = '127.0.0.1:9912'; $host = $input->getParameterOption(['--host'], $_SERVER['VAR_DUMPER_SERVER'] ?? $defaultHost, true); $logger = interface_exists(LoggerInterface::class) ? new ConsoleLogger($output->getErrorOutput()) : null; $app = new Application(); $app->getDefinition()->addOption( new InputOption('--host', null, InputOption::VALUE_REQUIRED, 'The address the server should listen to', $defaultHost) ); $app->addCommand($command = new ServerDumpCommand(new DumpServer($host, $logger))) ->getApplication() ->setDefaultCommand($command->getName(), true) ->run($input, $output) ; ================================================ FILE: Resources/css/htmlDescriptor.css ================================================ body { display: flex; flex-direction: column-reverse; justify-content: flex-end; max-width: 1140px; margin: auto; padding: 15px; word-wrap: break-word; background-color: #F9F9F9; color: #222; font-family: Helvetica, Arial, sans-serif; font-size: 14px; line-height: 1.4; } p { margin: 0; } a { color: #218BC3; text-decoration: none; } a:hover { text-decoration: underline; } .text-small { font-size: 12px !important; } article { margin: 5px; margin-bottom: 10px; } article > header > .row { display: flex; flex-direction: row; align-items: baseline; margin-bottom: 10px; } article > header > .row > .col { flex: 1; display: flex; align-items: baseline; } article > header > .row > h2 { font-size: 14px; color: #222; font-weight: normal; font-family: "Lucida Console", monospace, sans-serif; word-break: break-all; margin: 20px 5px 0 0; user-select: all; } article > header > .row > h2 > code { white-space: nowrap; user-select: none; color: #cc2255; background-color: #f7f7f9; border: 1px solid #e1e1e8; border-radius: 3px; margin-right: 5px; padding: 0 3px; } article > header > .row > time.col { flex: 0; text-align: right; white-space: nowrap; color: #999; font-style: italic; } article > header ul.tags { list-style: none; padding: 0; margin: 0; font-size: 12px; } article > header ul.tags > li { user-select: all; margin-bottom: 2px; } article > header ul.tags > li > span.badge { display: inline-block; padding: .25em .4em; margin-right: 5px; border-radius: 4px; background-color: #6c757d3b; color: #524d4d; font-size: 12px; text-align: center; font-weight: 700; line-height: 1; white-space: nowrap; vertical-align: baseline; user-select: none; } article > section.body { border: 1px solid #d8d8d8; background: #FFF; padding: 10px; border-radius: 3px; } pre.sf-dump { border-radius: 3px; margin-bottom: 0; } .hidden { display: none !important; } .dumped-tag > .sf-dump { display: inline-block; margin: 0; padding: 1px 5px; line-height: 1.4; vertical-align: top; background-color: transparent; user-select: auto; } .dumped-tag > pre.sf-dump, .dumped-tag > .sf-dump-default { color: #CC7832; background: none; } .dumped-tag > .sf-dump .sf-dump-str { color: #629755; } .dumped-tag > .sf-dump .sf-dump-private, .dumped-tag > .sf-dump .sf-dump-protected, .dumped-tag > .sf-dump .sf-dump-public { color: #262626; } .dumped-tag > .sf-dump .sf-dump-note { color: #6897BB; } .dumped-tag > .sf-dump .sf-dump-key { color: #789339; } .dumped-tag > .sf-dump .sf-dump-ref { color: #6E6E6E; } .dumped-tag > .sf-dump .sf-dump-ellipsis { color: #CC7832; max-width: 100em; } .dumped-tag > .sf-dump .sf-dump-ellipsis-path { max-width: 5em; } .dumped-tag > .sf-dump .sf-dump-ns { user-select: none; } ================================================ FILE: Resources/functions/dump.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ use Symfony\Component\VarDumper\Caster\ScalarStub; use Symfony\Component\VarDumper\VarDumper; if (!function_exists('dump')) { /** * @author Nicolas Grekas * @author Alexandre Daubois */ function dump(mixed ...$vars): mixed { if (!$vars) { VarDumper::dump(new ScalarStub('🐛')); return null; } if (array_key_exists(0, $vars) && 1 === count($vars)) { VarDumper::dump($vars[0]); $k = 0; } else { foreach ($vars as $k => $v) { VarDumper::dump($v, is_int($k) ? 1 + $k : $k); } } if (1 < count($vars)) { return $vars; } return $vars[$k]; } } if (!function_exists('dd')) { function dd(mixed ...$vars): never { if (!in_array(\PHP_SAPI, ['cli', 'phpdbg', 'embed'], true) && !headers_sent()) { header('HTTP/1.1 500 Internal Server Error'); } if (!$vars) { VarDumper::dump(new ScalarStub('🐛')); exit(1); } if (array_key_exists(0, $vars) && 1 === count($vars)) { VarDumper::dump($vars[0]); } else { foreach ($vars as $k => $v) { VarDumper::dump($v, is_int($k) ? 1 + $k : $k); } } exit(1); } } ================================================ FILE: Resources/js/htmlDescriptor.js ================================================ document.addEventListener('DOMContentLoaded', function() { let prev = null; Array.from(document.getElementsByTagName('article')).reverse().forEach(function (article) { const dedupId = article.dataset.dedupId; if (dedupId === prev) { article.getElementsByTagName('header')[0].classList.add('hidden'); } prev = dedupId; }); }); ================================================ FILE: Server/Connection.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Server; use Symfony\Component\VarDumper\Cloner\Data; use Symfony\Component\VarDumper\Dumper\ContextProvider\ContextProviderInterface; /** * Forwards serialized Data clones to a server. * * @author Maxime Steinhausser */ class Connection { private string $host; /** * @var resource|null */ private $socket; /** * @param string $host The server host * @param ContextProviderInterface[] $contextProviders Context providers indexed by context name */ public function __construct( string $host, private array $contextProviders = [], ) { if (!str_contains($host, '://')) { $host = 'tcp://'.$host; } $this->host = $host; } public function getContextProviders(): array { return $this->contextProviders; } public function write(Data $data): bool { $socketIsFresh = !$this->socket; if (!$this->socket = $this->socket ?: $this->createSocket()) { 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(static fn () => null); try { if (-1 !== stream_socket_sendto($this->socket, $encodedPayload)) { return true; } if (!$socketIsFresh) { stream_socket_shutdown($this->socket, \STREAM_SHUT_RDWR); fclose($this->socket); $this->socket = $this->createSocket(); } if (-1 !== stream_socket_sendto($this->socket, $encodedPayload)) { return true; } } finally { restore_error_handler(); } return false; } /** * @return resource|null */ private function createSocket() { set_error_handler(static fn () => null); try { return stream_socket_client($this->host, $errno, $errstr, 3) ?: null; } finally { restore_error_handler(); } } } ================================================ FILE: Server/DumpServer.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Server; use Psr\Log\LoggerInterface; use Symfony\Component\VarDumper\Cloner\Data; use Symfony\Component\VarDumper\Cloner\Stub; /** * A server collecting Data clones sent by a ServerDumper. * * @author Maxime Steinhausser * * @final */ class DumpServer { private string $host; /** * @var resource|null */ private $socket; public function __construct( string $host, private ?LoggerInterface $logger = null, ) { if (!str_contains($host, '://')) { $host = 'tcp://'.$host; } $this->host = $host; } public function start(): void { if (!$this->socket = stream_socket_server($this->host, $errno, $errstr)) { throw new \RuntimeException(\sprintf('Server start failed on "%s": ', $this->host).$errstr.' '.$errno); } } public function listen(callable $callback): void { if (null === $this->socket) { $this->start(); } foreach ($this->getMessages() as $clientId => $message) { $this->logger?->info('Received a payload from client {clientId}', ['clientId' => $clientId]); $payload = @unserialize(base64_decode($message), ['allowed_classes' => [Data::class, Stub::class]]); // Impossible to decode the message, give up. if (false === $payload) { $this->logger?->warning('Unable to decode a message from {clientId} client.', ['clientId' => $clientId]); continue; } if (!\is_array($payload) || \count($payload) < 2 || !$payload[0] instanceof Data || !\is_array($payload[1])) { $this->logger?->warning('Invalid payload from {clientId} client. Expected an array of two elements (Data $data, array $context)', ['clientId' => $clientId]); continue; } [$data, $context] = $payload; $callback($data, $context, $clientId); } } public function getHost(): string { return $this->host; } private function getMessages(): iterable { $sockets = [(int) $this->socket => $this->socket]; $write = []; while (true) { $read = $sockets; stream_select($read, $write, $write, null); foreach ($read as $stream) { if ($this->socket === $stream) { $stream = stream_socket_accept($this->socket); $sockets[(int) $stream] = $stream; } elseif (feof($stream)) { unset($sockets[(int) $stream]); fclose($stream); } else { yield (int) $stream => fgets($stream); } } } } } ================================================ FILE: Test/VarDumperTestTrait.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Test; use PHPUnit\Framework\Attributes\After; use Symfony\Component\VarDumper\Cloner\VarCloner; use Symfony\Component\VarDumper\Dumper\CliDumper; /** * @author Nicolas Grekas */ trait VarDumperTestTrait { /** * @internal */ private array $varDumperConfig = [ 'casters' => [], 'flags' => null, ]; /** * @param array $casters */ protected function setUpVarDumper(array $casters, ?int $flags = null): void { $this->varDumperConfig['casters'] = $casters; $this->varDumperConfig['flags'] = $flags; } /** * @after */ #[After] protected function tearDownVarDumper(): void { $this->varDumperConfig['casters'] = []; $this->varDumperConfig['flags'] = null; } public function assertDumpEquals(mixed $expected, mixed $data, int $filter = 0, string $message = ''): void { $this->assertSame($this->prepareExpectation($expected, $filter), $this->getDump($data, null, $filter), $message); } public function assertDumpMatchesFormat(mixed $expected, mixed $data, int $filter = 0, string $message = ''): void { $this->assertStringMatchesFormat($this->prepareExpectation($expected, $filter), $this->getDump($data, null, $filter), $message); } protected function getDump(mixed $data, string|int|null $key = null, int $filter = 0): ?string { if (null === $flags = $this->varDumperConfig['flags']) { $flags = getenv('DUMP_LIGHT_ARRAY') ? CliDumper::DUMP_LIGHT_ARRAY : 0; $flags |= getenv('DUMP_STRING_LENGTH') ? CliDumper::DUMP_STRING_LENGTH : 0; $flags |= getenv('DUMP_COMMA_SEPARATOR') ? CliDumper::DUMP_COMMA_SEPARATOR : 0; } $cloner = new VarCloner(); $cloner->addCasters($this->varDumperConfig['casters']); $cloner->setMaxItems(-1); $dumper = new CliDumper(null, null, $flags); $dumper->setColors(false); $data = $cloner->cloneVar($data, $filter)->withRefHandles(false); if (null !== $key && null === $data = $data->seek($key)) { return null; } return rtrim($dumper->dump($data, true)); } private function prepareExpectation(mixed $expected, int $filter): string { if (!\is_string($expected)) { $expected = $this->getDump($expected, null, $filter); } return rtrim($expected); } } ================================================ FILE: Tests/Caster/AddressInfoCasterTest.php ================================================ * * 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\Attributes\RequiresPhpExtension; use PHPUnit\Framework\TestCase; use Symfony\Component\VarDumper\Test\VarDumperTestTrait; #[RequiresPhpExtension('sockets')] class AddressInfoCasterTest extends TestCase { use VarDumperTestTrait; public function testCaster() { $xDump = <<assertDumpMatchesFormat($xDump, socket_addrinfo_lookup('localhost')[0]); } } ================================================ FILE: Tests/Caster/CasterTest.php ================================================ * * 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\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\VarDumper\Caster\Caster; use Symfony\Component\VarDumper\Test\VarDumperTestTrait; /** * @author Nicolas Grekas */ class CasterTest extends TestCase { use VarDumperTestTrait; private static array $referenceArray = [ 'null' => null, 'empty' => false, 'public' => 'pub', "\0~\0virtual" => 'virt', "\0+\0dynamic" => 'dyn', "\0*\0protected" => 'prot', "\0Foo\0private" => 'priv', ]; #[DataProvider('provideFilter')] public function testFilter($filter, $expectedDiff, $listedProperties = null) { if (null === $listedProperties) { $filteredArray = Caster::filter(self::$referenceArray, $filter); } else { $filteredArray = Caster::filter(self::$referenceArray, $filter, $listedProperties); } $this->assertSame($expectedDiff, array_diff_assoc(self::$referenceArray, $filteredArray)); } public static function provideFilter() { return [ [ 0, [], ], [ Caster::EXCLUDE_PUBLIC, [ 'null' => null, 'empty' => false, 'public' => 'pub', ], ], [ Caster::EXCLUDE_NULL, [ 'null' => null, ], ], [ Caster::EXCLUDE_EMPTY, [ 'null' => null, 'empty' => false, ], ], [ Caster::EXCLUDE_VIRTUAL, [ "\0~\0virtual" => 'virt', ], ], [ Caster::EXCLUDE_DYNAMIC, [ "\0+\0dynamic" => 'dyn', ], ], [ Caster::EXCLUDE_PROTECTED, [ "\0*\0protected" => 'prot', ], ], [ Caster::EXCLUDE_PRIVATE, [ "\0Foo\0private" => 'priv', ], ], [ Caster::EXCLUDE_VERBOSE, [ 'public' => 'pub', "\0*\0protected" => 'prot', ], ['public', "\0*\0protected"], ], [ Caster::EXCLUDE_NOT_IMPORTANT, [ 'null' => null, 'empty' => false, "\0~\0virtual" => 'virt', "\0+\0dynamic" => 'dyn', "\0Foo\0private" => 'priv', ], ['public', "\0*\0protected"], ], [ Caster::EXCLUDE_VIRTUAL | Caster::EXCLUDE_DYNAMIC, [ "\0~\0virtual" => 'virt', "\0+\0dynamic" => 'dyn', ], ], [ Caster::EXCLUDE_NOT_IMPORTANT | Caster::EXCLUDE_VERBOSE, self::$referenceArray, ['public', "\0*\0protected"], ], [ Caster::EXCLUDE_NOT_IMPORTANT | Caster::EXCLUDE_EMPTY, [ 'null' => null, 'empty' => false, "\0~\0virtual" => 'virt', "\0+\0dynamic" => 'dyn', "\0*\0protected" => 'prot', "\0Foo\0private" => 'priv', ], ['public', 'empty'], ], [ Caster::EXCLUDE_VERBOSE | Caster::EXCLUDE_EMPTY | Caster::EXCLUDE_STRICT, [ 'empty' => false, ], ['public', 'empty'], ], ]; } public function testAnonymousClass() { $c = eval('return new class extends stdClass { private $foo = "foo"; };'); $this->assertDumpMatchesFormat( <<<'EOTXT' stdClass@anonymous { -foo: "foo" } EOTXT, $c ); $c = eval('return new class implements \Countable { private $foo = "foo"; public function count(): int { return 0; } };'); $this->assertDumpMatchesFormat( <<<'EOTXT' Countable@anonymous { -foo: "foo" } EOTXT, $c ); } public function testTypeErrorInDebugInfo() { $this->assertDumpMatchesFormat('class@anonymous {}', new class { public function __debugInfo(): array { return ['class' => \get_class(null)]; } }); } public function testClassHierarchy() { $this->assertDumpMatchesFormat(<<<'DUMP' Symfony\Component\VarDumper\Tests\Caster\B { +a: "a" #b: "b" -c: "c" +d: "d" #e: "e" -f: "f" } DUMP, new B() ); } } class A { public $a = 'a'; protected $b = 'b'; private $c = 'c'; } class B extends A { public $d = 'd'; protected $e = 'e'; private $f = 'f'; } ================================================ FILE: Tests/Caster/CurlCasterTest.php ================================================ * * 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\Attributes\RequiresPhpExtension; use PHPUnit\Framework\TestCase; use Symfony\Component\VarDumper\Test\VarDumperTestTrait; #[RequiresPhpExtension('curl')] class CurlCasterTest extends TestCase { use VarDumperTestTrait; public function testCastCurl() { $ch = curl_init('http://example.com'); curl_setopt($ch, \CURLOPT_RETURNTRANSFER, true); curl_exec($ch); $this->assertDumpMatchesFormat( <<<'EODUMP' CurlHandle { url: "http://example.com/" content_type: "text/html%S" http_code: %d %A } EODUMP, $ch ); } } ================================================ FILE: Tests/Caster/DOMCasterTest.php ================================================ * * 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; class DOMCasterTest extends TestCase { use VarDumperTestTrait; public function testCastImplementation() { $implementation = new \DOMImplementation(); $this->assertDumpEquals(<<<'EODUMP' DOMImplementation { Core: "1.0" XML: "2.0" } EODUMP, $implementation ); } public function testCastModernImplementation() { $implementation = new \Dom\Implementation(); $this->assertDumpEquals(<<<'EODUMP' Dom\Implementation { Core: "1.0" XML: "2.0" } EODUMP, $implementation ); } public function testCastNode() { $doc = new \DOMDocument(); $doc->loadXML(''); $node = $doc->documentElement->firstChild; $this->assertDumpMatchesFormat(<<<'EODUMP' DOMElement {%A +parentNode: DOMElement {%a…} %A} EODUMP, $node ); } public function testCastModernNode() { $doc = \Dom\XMLDocument::createFromString(''); $node = $doc->documentElement->firstChild; $this->assertDumpMatchesFormat(<<<'EODUMP' Dom\Element {%A +parentElement: Dom\Element {#1 …} %A} EODUMP, $node ); } public function testCastDocument() { $doc = new \DOMDocument(); $doc->loadXML(''); $this->assertDumpMatchesFormat(<<<'EODUMP' DOMDocument {%A xml: """ \n \n \n \n """ } EODUMP, $doc ); } public function testCastXMLDocument() { $doc = \Dom\XMLDocument::createFromString(''); $this->assertDumpMatchesFormat(<<<'EODUMP' Dom\XMLDocument {%A xml: """ \n \n \n """ } EODUMP, $doc ); } public function testCastHTMLDocument() { $doc = \Dom\HTMLDocument::createFromString('

    foo

    '); $this->assertDumpMatchesFormat(<<<'EODUMP' Dom\HTMLDocument {%A html: "

    foo

    " } EODUMP, $doc ); } public function testCastText() { $doc = new \DOMText('foo'); $this->assertDumpMatchesFormat(<<<'EODUMP' DOMText {%A +nodeName: "#text" %A} EODUMP, $doc ); } public function testCastModernText() { $text = \Dom\HTMLDocument::createEmpty()->createTextNode('foo'); $this->assertDumpMatchesFormat(<<<'EODUMP' Dom\Text {%A +nodeName: "#text" %A} EODUMP, $text ); } public function testCastAttr() { $attr = new \DOMAttr('attr', 'value'); $this->assertDumpMatchesFormat(<<<'EODUMP' DOMAttr {%A +nodeName: "attr" %A} EODUMP, $attr ); } public function testCastModernAttr() { $attr = \Dom\HTMLDocument::createEmpty()->createAttribute('attr'); $this->assertDumpMatchesFormat(<<<'EODUMP' Dom\Attr {%A +nodeName: "attr" %A} EODUMP, $attr ); } public function testCastElement() { $attr = new \DOMElement('foo'); $this->assertDumpMatchesFormat(<<<'EODUMP' DOMElement {%A +tagName: "foo" %A} EODUMP, $attr ); } public function testCastModernElement() { $attr = \Dom\HTMLDocument::createEmpty()->createElement('foo'); $this->assertDumpMatchesFormat(<<<'EODUMP' Dom\HTMLElement {%A +tagName: "FOO" %A} EODUMP, $attr ); } public function testCastDocumentType() { $implementation = new \DOMImplementation(); $type = $implementation->createDocumentType('html', 'publicId', 'systemId'); $this->assertDumpMatchesFormat(<<<'EODUMP' DOMDocumentType { +nodeName: "html" +nodeValue: null +nodeType: XML_DOCUMENT_TYPE_NODE %A} EODUMP, $type ); } public function testCastModernDocumentType() { $implementation = new \Dom\Implementation(); $type = $implementation->createDocumentType('html', 'publicId', 'systemId'); $this->assertDumpMatchesFormat(<<<'EODUMP' Dom\DocumentType { +nodeType: XML_DOCUMENT_TYPE_NODE %A} EODUMP, $type ); } public function testCastProcessingInstruction() { $entity = new \DOMProcessingInstruction('target', 'data'); $this->assertDumpMatchesFormat(<<<'EODUMP' DOMProcessingInstruction {%A +data: "data" } EODUMP, $entity ); } public function testCastModernProcessingInstruction() { $entity = \Dom\HTMLDocument::createEmpty()->createProcessingInstruction('target', 'data'); $this->assertDumpMatchesFormat(<<<'EODUMP' Dom\ProcessingInstruction {%A +target: "target" } EODUMP, $entity ); } public function testCastXPath() { $xpath = new \DOMXPath(new \DOMDocument()); $this->assertDumpEquals(<<<'EODUMP' DOMXPath { +document: DOMDocument { …} +registerNodeNamespaces: true } EODUMP, $xpath ); } public function testCastModernXPath() { $entity = new \Dom\XPath(\Dom\HTMLDocument::createEmpty()); $this->assertDumpEquals(<<<'EODUMP' Dom\XPath { +document: Dom\HTMLDocument { …} +registerNodeNamespaces: true } EODUMP, $entity ); } } ================================================ FILE: Tests/Caster/DateCasterTest.php ================================================ * * 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\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\VarDumper\Caster\Caster; use Symfony\Component\VarDumper\Caster\DateCaster; use Symfony\Component\VarDumper\Cloner\Stub; use Symfony\Component\VarDumper\Test\VarDumperTestTrait; use Symfony\Component\VarDumper\Tests\Fixtures\DateTimeChild; /** * @author Dany Maillard */ class DateCasterTest extends TestCase { use VarDumperTestTrait; private string $previousTimezone; protected function setUp(): void { $this->previousTimezone = date_default_timezone_get(); } protected function tearDown(): void { date_default_timezone_set($this->previousTimezone); } #[DataProvider('provideDateTimes')] public function testDumpDateTime($time, $timezone, $xDate, $xTimestamp, $xInfos) { $date = new \DateTime($time, new \DateTimeZone($timezone)); $xDump = <<assertDumpEquals($xDump, $date); } #[DataProvider('provideDateTimes')] public function testDumpDateTimeImmutable($time, $timezone, $xDate, $xTimestamp, $xInfos) { $date = new \DateTimeImmutable($time, new \DateTimeZone($timezone)); $xDump = <<assertDumpEquals($xDump, $date); } #[DataProvider('provideDateTimes')] public function testCastDateTime($time, $timezone, $xDate, $xTimestamp, $xInfos) { $stub = new Stub(); $date = new \DateTimeImmutable($time, new \DateTimeZone($timezone)); $cast = DateCaster::castDateTime($date, Caster::castObject($date, \DateTimeImmutable::class), $stub, false, 0); $xDump = << $xDate ] EODUMP; $this->assertDumpEquals($xDump, $cast); $xDump = <<assertDumpMatchesFormat($xDump, $cast["\0~\0date"]); } public static function provideDateTimes() { return [ ['2017-04-30 00:00:00.000000', 'Europe/Zurich', '2017-04-30 00:00:00.0 Europe/Zurich (+02:00)', 1493503200, 'Sunday, April 30, 2017%Afrom now%ADST On'], ['2017-12-31 00:00:00.000000', 'Europe/Zurich', '2017-12-31 00:00:00.0 Europe/Zurich (+01:00)', 1514674800, 'Sunday, December 31, 2017%Afrom now%ADST Off'], ['2017-04-30 00:00:00.000000', '+02:00', '2017-04-30 00:00:00.0 +02:00', 1493503200, 'Sunday, April 30, 2017%Afrom now'], ['2017-04-30 00:00:00.100000', '+00:00', '2017-04-30 00:00:00.100 +00:00', 1493510400, 'Sunday, April 30, 2017%Afrom now'], ['2017-04-30 00:00:00.120000', '+00:00', '2017-04-30 00:00:00.120 +00:00', 1493510400, 'Sunday, April 30, 2017%Afrom now'], ['2017-04-30 00:00:00.123000', '+00:00', '2017-04-30 00:00:00.123 +00:00', 1493510400, 'Sunday, April 30, 2017%Afrom now'], ['2017-04-30 00:00:00.123400', '+00:00', '2017-04-30 00:00:00.123400 +00:00', 1493510400, 'Sunday, April 30, 2017%Afrom now'], ['2017-04-30 00:00:00.123450', '+00:00', '2017-04-30 00:00:00.123450 +00:00', 1493510400, 'Sunday, April 30, 2017%Afrom now'], ['2017-04-30 00:00:00.123456', '+00:00', '2017-04-30 00:00:00.123456 +00:00', 1493510400, 'Sunday, April 30, 2017%Afrom now'], ]; } #[DataProvider('provideNoTimezoneDateTimes')] public function testCastDateTimeNoTimezone($time, $xDate, $xInfos) { date_default_timezone_set('UTC'); $stub = new Stub(); $date = new NoTimezoneDate($time); $cast = DateCaster::castDateTime($date, Caster::castObject($date, \DateTime::class), $stub, false, 0); $xDump = << $xDate ] EODUMP; $this->assertDumpEquals($xDump, $cast); $xDump = <<assertDumpMatchesFormat($xDump, $cast["\0~\0date"]); } public static function provideNoTimezoneDateTimes() { return [ ['2017-04-30 00:00:00.000000', '2017-04-30 00:00:00.0 +00:00', 'Sunday, April 30, 2017'], ['2017-04-30 00:00:00.100000', '2017-04-30 00:00:00.100 +00:00', 'Sunday, April 30, 2017'], ['2017-04-30 00:00:00.120000', '2017-04-30 00:00:00.120 +00:00', 'Sunday, April 30, 2017'], ['2017-04-30 00:00:00.123000', '2017-04-30 00:00:00.123 +00:00', 'Sunday, April 30, 2017'], ['2017-04-30 00:00:00.123400', '2017-04-30 00:00:00.123400 +00:00', 'Sunday, April 30, 2017'], ['2017-04-30 00:00:00.123450', '2017-04-30 00:00:00.123450 +00:00', 'Sunday, April 30, 2017'], ['2017-04-30 00:00:00.123456', '2017-04-30 00:00:00.123456 +00:00', 'Sunday, April 30, 2017'], ]; } public function testCastDateTimeWithAdditionalChildProperty() { $stub = new Stub(); $date = new DateTimeChild('2020-02-13 00:00:00.123456', new \DateTimeZone('Europe/Paris')); $objectCast = Caster::castObject($date, DateTimeChild::class); $dateCast = DateCaster::castDateTime($date, $objectCast, $stub, false, 0); $xDate = '2020-02-13 00:00:00.123456 Europe/Paris (+01:00)'; $xInfo = 'Thursday, February 13, 2020%Afrom now'; $xDump = << "foo" "\\x00~\\x00date" => $xDate ] EODUMP; $this->assertDumpEquals($xDump, $dateCast); $xDump = <<assertDumpMatchesFormat($xDump, $dateCast["\0~\0date"]); } #[DataProvider('provideIntervals')] public function testDumpInterval($intervalSpec, $ms, $invert, $expected, $xSeconds) { $interval = $this->createInterval($intervalSpec, $ms, $invert); $xDump = <<assertDumpMatchesFormat($xDump, $interval); } #[DataProvider('provideIntervals')] public function testDumpIntervalExcludingVerbosity($intervalSpec, $ms, $invert, $expected, $xSeconds) { $interval = $this->createInterval($intervalSpec, $ms, $invert); $xDump = <<assertDumpEquals($xDump, $interval, Caster::EXCLUDE_VERBOSE); } #[DataProvider('provideIntervals')] public function testCastInterval($intervalSpec, $ms, $invert, $xInterval, $xSeconds) { $interval = $this->createInterval($intervalSpec, $ms, $invert); $stub = new Stub(); $cast = DateCaster::castInterval($interval, ['foo' => 'bar'], $stub, false, Caster::EXCLUDE_VERBOSE); $xDump = << $xInterval ] EODUMP; $this->assertDumpEquals($xDump, $cast); if (null === $xSeconds) { return; } $xDump = <<assertDumpMatchesFormat($xDump, $cast["\0~\0interval"]); } public static function provideIntervals() { return [ ['PT0S', 0, 0, '0s', '0s'], ['PT0S', 0.1, 0, '+ 00:00:00.100', '%is'], ['PT1S', 0, 0, '+ 00:00:01.0', '%is'], ['PT2M', 0, 0, '+ 00:02:00.0', '%is'], ['PT3H', 0, 0, '+ 03:00:00.0', '%ss'], ['P4D', 0, 0, '+ 4d', '%ss'], ['P5M', 0, 0, '+ 5m', null], ['P6Y', 0, 0, '+ 6y', null], ['P1Y2M3DT4H5M6S', 0, 0, '+ 1y 2m 3d 04:05:06.0', null], ['PT1M60S', 0, 0, '+ 00:02:00.0', null], ['PT1H60M', 0, 0, '+ 02:00:00.0', null], ['P1DT24H', 0, 0, '+ 2d', null], ['P1M32D', 0, 0, '+ 1m 32d', null], ['PT0S', 0, 1, '0s', '0s'], ['PT0S', 0.1, 1, '- 00:00:00.100', '%is'], ['PT1S', 0, 1, '- 00:00:01.0', '%is'], ['PT2M', 0, 1, '- 00:02:00.0', '%is'], ['PT3H', 0, 1, '- 03:00:00.0', '%ss'], ['P4D', 0, 1, '- 4d', '%ss'], ['P5M', 0, 1, '- 5m', null], ['P6Y', 0, 1, '- 6y', null], ['P1Y2M3DT4H5M6S', 0, 1, '- 1y 2m 3d 04:05:06.0', null], ['PT1M60S', 0, 1, '- 00:02:00.0', null], ['PT1H60M', 0, 1, '- 02:00:00.0', null], ['P1DT24H', 0, 1, '- 2d', null], ['P1M32D', 0, 1, '- 1m 32d', null], ]; } #[DataProvider('provideTimeZones')] public function testDumpTimeZone($timezone, $expected, $xRegion) { $timezone = new \DateTimeZone($timezone); $xDump = <<assertDumpMatchesFormat($xDump, $timezone); } #[DataProvider('provideTimeZones')] public function testDumpTimeZoneExcludingVerbosity($timezone, $expected, $xRegion) { $timezone = new \DateTimeZone($timezone); $xDump = <<assertDumpMatchesFormat($xDump, $timezone, Caster::EXCLUDE_VERBOSE); } #[DataProvider('provideTimeZones')] public function testCastTimeZone($timezone, $xTimezone, $xRegion) { $timezone = new \DateTimeZone($timezone); $stub = new Stub(); $cast = DateCaster::castTimeZone($timezone, ['foo' => 'bar'], $stub, false, Caster::EXCLUDE_VERBOSE); $xDump = << $xTimezone ] EODUMP; $this->assertDumpMatchesFormat($xDump, $cast); $xDump = <<assertDumpMatchesFormat($xDump, $cast["\0~\0timezone"]); } public static function provideTimeZones() { $xRegion = \extension_loaded('intl') ? '%s' : ''; return [ // type 1 (UTC offset) ['-12:00', '-12:00', ''], ['+00:00', '+00:00', ''], ['+14:00', '+14:00', ''], // type 2 (timezone abbreviation) ['GMT', '+00:00', ''], ['a', '+01:00', ''], ['b', '+02:00', ''], ['z', '+00:00', ''], // type 3 (timezone identifier) ['Africa/Tunis', 'Africa/Tunis (%s:00)', $xRegion], ['America/Panama', 'America/Panama (%s:00)', $xRegion], ['Asia/Jerusalem', 'Asia/Jerusalem (%s:00)', $xRegion], ['Atlantic/Canary', 'Atlantic/Canary (%s:00)', $xRegion], ['Australia/Perth', 'Australia/Perth (%s:00)', $xRegion], ['Europe/Zurich', 'Europe/Zurich (%s:00)', $xRegion], ['Pacific/Tahiti', 'Pacific/Tahiti (%s:00)', $xRegion], ]; } #[DataProvider('providePeriods')] public function testDumpPeriod($start, $interval, $end, $options, $expected, $xDates) { $p = new \DatePeriod(new \DateTimeImmutable($start), new \DateInterval($interval), \is_int($end) ? $end : new \DateTime($end), $options); $xDump = <<assertDumpMatchesFormat($xDump, $p); } #[DataProvider('providePeriods')] public function testCastPeriod($start, $interval, $end, $options, $xPeriod, $xDates) { $p = new \DatePeriod(new \DateTimeImmutable($start, new \DateTimeZone('UTC')), new \DateInterval($interval), \is_int($end) ? $end : new \DateTimeImmutable($end, new \DateTimeZone('UTC')), $options); $stub = new Stub(); $cast = DateCaster::castPeriod($p, [], $stub, false, 0); $xDump = << $xPeriod ] EODUMP; $this->assertDumpEquals($xDump, $cast); $xDump = <<assertDumpMatchesFormat($xDump, $cast["\0~\0period"]); } public static function providePeriods() { $periods = [ ['2017-01-01', 'P1D', '2017-01-03', 0, 'every + 1d, from [2017-01-01 00:00:00.0 to 2017-01-03 00:00:00.0[', '1) 2017-01-01%a2) 2017-01-02'], ['2017-01-01', 'P1D', 1, 0, 'every + 1d, from [2017-01-01 00:00:00.0 recurring 2 time/s', '1) 2017-01-01%a2) 2017-01-02'], ['2017-01-01', 'P1D', '2017-01-04', 0, 'every + 1d, from [2017-01-01 00:00:00.0 to 2017-01-04 00:00:00.0[', '1) 2017-01-01%a2) 2017-01-02%a3) 2017-01-03'], ['2017-01-01', 'P1D', 2, 0, 'every + 1d, from [2017-01-01 00:00:00.0 recurring 3 time/s', '1) 2017-01-01%a2) 2017-01-02%a3) 2017-01-03'], ['2017-01-01', 'P1D', '2017-01-05', 0, 'every + 1d, from [2017-01-01 00:00:00.0 to 2017-01-05 00:00:00.0[', '1) 2017-01-01%a2) 2017-01-02%a1 more'], ['2017-01-01', 'P1D', 3, 0, 'every + 1d, from [2017-01-01 00:00:00.0 recurring 4 time/s', '1) 2017-01-01%a2) 2017-01-02%a3) 2017-01-03%a1 more'], ['2017-01-01', 'P1D', '2017-01-21', 0, 'every + 1d, from [2017-01-01 00:00:00.0 to 2017-01-21 00:00:00.0[', '1) 2017-01-01%a17 more'], ['2017-01-01', 'P1D', 19, 0, 'every + 1d, from [2017-01-01 00:00:00.0 recurring 20 time/s', '1) 2017-01-01%a17 more'], ['2017-01-01 01:00:00', 'P1D', '2017-01-03 01:00:00', 0, 'every + 1d, from [2017-01-01 01:00:00.0 to 2017-01-03 01:00:00.0[', '1) 2017-01-01 01:00:00.0%a2) 2017-01-02 01:00:00.0'], ['2017-01-01 01:00:00', 'P1D', 1, 0, 'every + 1d, from [2017-01-01 01:00:00.0 recurring 2 time/s', '1) 2017-01-01 01:00:00.0%a2) 2017-01-02 01:00:00.0'], ['2017-01-01', 'P1DT1H', '2017-01-03', 0, 'every + 1d 01:00:00.0, from [2017-01-01 00:00:00.0 to 2017-01-03 00:00:00.0[', '1) 2017-01-01 00:00:00.0%a2) 2017-01-02 01:00:00.0'], ['2017-01-01', 'P1DT1H', 1, 0, 'every + 1d 01:00:00.0, from [2017-01-01 00:00:00.0 recurring 2 time/s', '1) 2017-01-01 00:00:00.0%a2) 2017-01-02 01:00:00.0'], ['2017-01-01', 'P1D', '2017-01-04', \DatePeriod::EXCLUDE_START_DATE, 'every + 1d, from ]2017-01-01 00:00:00.0 to 2017-01-04 00:00:00.0[', '1) 2017-01-02%a2) 2017-01-03'], ['2017-01-01', 'P1D', 2, \DatePeriod::EXCLUDE_START_DATE, 'every + 1d, from ]2017-01-01 00:00:00.0 recurring 2 time/s', '1) 2017-01-02%a2) 2017-01-03'], ]; return $periods; } private function createInterval($intervalSpec, $ms, $invert) { $interval = new \DateInterval($intervalSpec); $interval->f = $ms; $interval->invert = $invert; return $interval; } } class NoTimezoneDate extends \DateTime { public function getTimezone(): \DateTimeZone|false { return false; } } ================================================ FILE: Tests/Caster/DoctrineCasterTest.php ================================================ * * 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 Doctrine\Common\Collections\ArrayCollection; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\PersistentCollection; use PHPUnit\Framework\Attributes\RequiresMethod; use PHPUnit\Framework\TestCase; use Symfony\Component\VarDumper\Test\VarDumperTestTrait; #[RequiresMethod(ArrayCollection::class, '__construct')] class DoctrineCasterTest extends TestCase { use VarDumperTestTrait; public function testCastPersistentCollection() { $classMetadata = new ClassMetadata(__CLASS__); $entityManager = $this->createStub(EntityManagerInterface::class); $entityManagerClass = $entityManager::class; $collection = new PersistentCollection($entityManager, $classMetadata, new ArrayCollection(['test'])); if (property_exists(PersistentCollection::class, 'isDirty')) { // Collections >= 2 $expected = <<assertDumpMatchesFormat($expected, $collection); } } ================================================ FILE: Tests/Caster/ExceptionCasterTest.php ================================================ * * 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\Attributes\RequiresMethod; use PHPUnit\Framework\TestCase; use Symfony\Component\ErrorHandler\Exception\FlattenException; use Symfony\Component\ErrorHandler\Exception\SilencedErrorContext; use Symfony\Component\VarDumper\Caster\Caster; use Symfony\Component\VarDumper\Caster\ExceptionCaster; use Symfony\Component\VarDumper\Caster\FrameStub; use Symfony\Component\VarDumper\Caster\TraceStub; use Symfony\Component\VarDumper\Cloner\VarCloner; use Symfony\Component\VarDumper\Dumper\HtmlDumper; use Symfony\Component\VarDumper\Test\VarDumperTestTrait; class ExceptionCasterTest extends TestCase { use VarDumperTestTrait; private function getTestException($msg, &$ref = null) { return new \Exception(''.$msg); } private function getTestError($msg): \Error { return new \Error(''.$msg); } private function getTestErrorException($msg): \ErrorException { return new \ErrorException(''.$msg); } private function getTestSilencedErrorContext(): SilencedErrorContext { return new SilencedErrorContext(\E_ERROR, __FILE__, __LINE__); } protected function tearDown(): void { ExceptionCaster::$srcContext = 1; ExceptionCaster::$traceArgs = true; } public function testDefaultSettings() { $ref = ['foo']; $e = $this->getTestException('foo', $ref); $expectedDump = <<<'EODUMP' Exception { #message: "foo" #code: 0 #file: "%sExceptionCasterTest.php" #line: %d trace: { %s%eTests%eCaster%eExceptionCasterTest.php:%d { Symfony\Component\VarDumper\Tests\Caster\ExceptionCasterTest->getTestException($msg, &$ref = null) › { › return new \Exception(''.$msg); › } } %s%eTests%eCaster%eExceptionCasterTest.php:%d { …} %A EODUMP; $this->assertDumpMatchesFormat($expectedDump, $e); $this->assertSame(['foo'], $ref); } public function testDefaultSettingsOnError() { $e = $this->getTestError('foo'); $expectedDump = <<<'EODUMP' Error { #message: "foo" #code: 0 #file: "%sExceptionCasterTest.php" #line: %d trace: { %s%eTests%eCaster%eExceptionCasterTest.php:%d { Symfony\Component\VarDumper\Tests\Caster\ExceptionCasterTest->getTestError($msg): Error › { › return new \Error(''.$msg); › } } %s%eTests%eCaster%eExceptionCasterTest.php:%d { …} %A EODUMP; $this->assertDumpMatchesFormat($expectedDump, $e); } public function testDefaultSettingsOnErrorException() { $e = $this->getTestErrorException('foo'); $expectedDump = <<<'EODUMP' ErrorException { #message: "foo" #code: 0 #file: "%sExceptionCasterTest.php" #line: %d #severity: E_ERROR trace: { %s%eTests%eCaster%eExceptionCasterTest.php:%d { Symfony\Component\VarDumper\Tests\Caster\ExceptionCasterTest->getTestErrorException($msg): ErrorException › { › return new \ErrorException(''.$msg); › } } %s%eTests%eCaster%eExceptionCasterTest.php:%d { …} %A EODUMP; $this->assertDumpMatchesFormat($expectedDump, $e); } #[RequiresMethod(SilencedErrorContext::class, '__construct')] public function testCastSilencedErrorContext() { $e = $this->getTestSilencedErrorContext(); $expectedDump = <<<'EODUMP' Symfony\Component\ErrorHandler\Exception\SilencedErrorContext { +count: 1 -severity: E_ERROR trace: { %s%eTests%eCaster%eExceptionCasterTest.php:%d { › { › return new SilencedErrorContext(\E_ERROR, __FILE__, __LINE__); › } } } } EODUMP; $this->assertDumpMatchesFormat($expectedDump, $e); } public function testSeek() { $e = $this->getTestException(2); $expectedDump = <<<'EODUMP' { %s%eTests%eCaster%eExceptionCasterTest.php:%d { Symfony\Component\VarDumper\Tests\Caster\ExceptionCasterTest->getTestException($msg, &$ref = null) › { › return new \Exception(''.$msg); › } } %s%eTests%eCaster%eExceptionCasterTest.php:%d { …} %A EODUMP; $this->assertStringMatchesFormat($expectedDump, $this->getDump($e, 'trace')); } public function testNoArgs() { $e = $this->getTestException(1); ExceptionCaster::$traceArgs = false; $expectedDump = <<<'EODUMP' Exception { #message: "1" #code: 0 #file: "%sExceptionCasterTest.php" #line: %d trace: { %sExceptionCasterTest.php:%d { Symfony\Component\VarDumper\Tests\Caster\ExceptionCasterTest->getTestException($msg, &$ref = null) › { › return new \Exception(''.$msg); › } } %s%eTests%eCaster%eExceptionCasterTest.php:%d { …} %A EODUMP; $this->assertDumpMatchesFormat($expectedDump, $e); } public function testNoSrcContext() { $e = $this->getTestException(1); ExceptionCaster::$srcContext = -1; $expectedDump = <<<'EODUMP' Exception { #message: "1" #code: 0 #file: "%sExceptionCasterTest.php" #line: %d trace: { %s%eTests%eCaster%eExceptionCasterTest.php:%d %s%eTests%eCaster%eExceptionCasterTest.php:%d %A EODUMP; $this->assertDumpMatchesFormat($expectedDump, $e); } public function testShouldReturnTraceForConcreteTwigWithError() { require_once \dirname(__DIR__).'/Fixtures/Twig.php'; $innerExc = (new \__TwigTemplate_VarDumperFixture_u75a09(null, __FILE__))->provideError(); $nestingWrapper = new \stdClass(); $nestingWrapper->trace = new TraceStub($innerExc->getTrace()); $expectedDump = <<<'EODUMP' { +"trace": { %sTwig.php:%d { AbstractTwigTemplate->provideError() › { › return $this->createError(); › } } %sExceptionCasterTest.php:%d { …} %A EODUMP; $this->assertDumpMatchesFormat($expectedDump, $nestingWrapper); } public function testHtmlDump() { if (\ini_get('xdebug.file_link_format') || get_cfg_var('xdebug.file_link_format')) { $this->markTestSkipped('A custom file_link_format is defined.'); } $e = $this->getTestException(1); ExceptionCaster::$srcContext = -1; $cloner = new VarCloner(); $cloner->setMaxItems(1); $dumper = new HtmlDumper(); $dumper->setDumpHeader(''); $dumper->setDumpBoundaries('', ''); $dump = $dumper->dump($cloner->cloneVar($e)->withRefHandles(false), true); $expectedDump = <<<'EODUMP' Exception { #message: "1" #code: 0 #file: "%s%eVarDumper%eTests%eCaster%eExceptionCasterTest.php" #line: %d trace: { %s%eVarDumper%eTests%eCaster%eExceptionCasterTest.php:%d …%d } } EODUMP; $this->assertStringMatchesFormat($expectedDump, $dump); } public function testFrameWithTwig() { require_once \dirname(__DIR__).'/Fixtures/Twig.php'; $f = [ new FrameStub([ 'file' => \dirname(__DIR__).'/Fixtures/Twig.php', 'line' => 33, 'class' => '__TwigTemplate_VarDumperFixture_u75a09', ]), new FrameStub([ 'file' => \dirname(__DIR__).'/Fixtures/Twig.php', 'line' => 34, 'class' => '__TwigTemplate_VarDumperFixture_u75a09', 'object' => new \__TwigTemplate_VarDumperFixture_u75a09(null, __FILE__), ]), ]; $expectedDump = <<<'EODUMP' array:2 [ 0 => { class: "__TwigTemplate_VarDumperFixture_u75a09" src: { %sTwig.php:1 { ›%s › foo bar › twig source } } } 1 => { class: "__TwigTemplate_VarDumperFixture_u75a09" object: __TwigTemplate_VarDumperFixture_u75a09 { %A } src: { %sExceptionCasterTest.php:2 { › foo bar › twig source ›%s } } } ] EODUMP; $this->assertDumpMatchesFormat($expectedDump, $f); } public function testExcludeVerbosity() { $e = $this->getTestException('foo'); $expectedDump = <<<'EODUMP' Exception { #message: "foo" #code: 0 #file: "%sExceptionCasterTest.php" #line: %d } EODUMP; $this->assertDumpMatchesFormat($expectedDump, $e, Caster::EXCLUDE_VERBOSE); } public function testAnonymous() { $e = new \Exception(\sprintf('Boo "%s" ba.', (new class('Foo') extends \Exception { })::class)); $expectedDump = <<<'EODUMP' Exception { #message: "Boo "Exception@anonymous" ba." #code: 0 #file: "%sExceptionCasterTest.php" #line: %d } EODUMP; $this->assertDumpMatchesFormat($expectedDump, $e, Caster::EXCLUDE_VERBOSE); } #[RequiresMethod(FlattenException::class, 'create')] public function testFlattenException() { $f = FlattenException::createFromThrowable(new \Exception('Hello')); $expectedDump = <<<'EODUMP' array:1 [ 0 => Symfony\Component\ErrorHandler\Exception\FlattenException { -message: "Hello" -code: 0 -previous: null -trace: array:%d %a -traceAsString: ""…%d -class: "Exception" -statusCode: 500 -statusText: "Internal Server Error" -headers: [] -file: "%sExceptionCasterTest.php" -line: %d -asString: null -dataRepresentation: ? Symfony\Component\VarDumper\Cloner\Data } ] EODUMP; $this->assertDumpMatchesFormat($expectedDump, [$f], Caster::EXCLUDE_VERBOSE); } } ================================================ FILE: Tests/Caster/FFICasterTest.php ================================================ * * 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\Attributes\DataProvider; use PHPUnit\Framework\Attributes\RequiresPhpExtension; use PHPUnit\Framework\TestCase; use Symfony\Component\VarDumper\Caster\FFICaster; use Symfony\Component\VarDumper\Test\VarDumperTestTrait; /** * @author Kirill Nesmeyanov */ #[RequiresPhpExtension('ffi')] class FFICasterTest extends TestCase { use VarDumperTestTrait; /** * @see FFICaster::MAX_STRING_LENGTH */ private const MAX_STRING_LENGTH = 255; protected function setUp(): void { if (\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) && 'preload' === \ini_get('ffi.enable')) { return; } if (!filter_var(\ini_get('ffi.enable'), \FILTER_VALIDATE_BOOL)) { $this->markTestSkipped('FFI not enabled for CLI SAPI'); } } public function testCastAnonymousStruct() { $this->assertDumpEquals(<<<'PHP' FFI\CData> size 4 align 4 { uint32_t x: 0 } PHP, \FFI::cdef()->new('struct { uint32_t x; }') ); } public function testCastNamedStruct() { $this->assertDumpEquals(<<<'PHP' FFI\CData size 4 align 4 { uint32_t x: 0 } PHP, \FFI::cdef()->new('struct Example { uint32_t x; }') ); } public function testCastAnonymousUnion() { $this->assertDumpEquals(<<<'PHP' FFI\CData> size 4 align 4 { uint32_t x: 0 uint32_t y: 0 } PHP, \FFI::cdef()->new('union { uint32_t x; uint32_t y; }') ); } public function testCastNamedUnion() { $this->assertDumpEquals(<<<'PHP' FFI\CData size 4 align 4 { uint32_t x: 0 uint32_t y: 0 } PHP, \FFI::cdef()->new('union Example { uint32_t x; uint32_t y; }') ); } public function testCastAnonymousEnum() { $this->assertDumpEquals(<<<'PHP' FFI\CData> size 4 align 4 { cdata: 0 } PHP, \FFI::cdef()->new('enum { a, b }') ); } public function testCastNamedEnum() { $this->assertDumpEquals(<<<'PHP' FFI\CData size 4 align 4 { cdata: 0 } PHP, \FFI::cdef()->new('enum Example { a, b }') ); } public static function scalarsDataProvider(): array { return [ 'int8_t' => ['int8_t', '0', 1, 1], 'uint8_t' => ['uint8_t', '0', 1, 1], 'int16_t' => ['int16_t', '0', 2, 2], 'uint16_t' => ['uint16_t', '0', 2, 2], 'int32_t' => ['int32_t', '0', 4, 4], 'uint32_t' => ['uint32_t', '0', 4, 4], 'int64_t' => ['int64_t', '0', 8, 8], 'uint64_t' => ['uint64_t', '0', 8, 8], 'bool' => ['bool', 'false', 1, 1], 'char' => ['char', '"\x00"', 1, 1], 'float' => ['float', '0.0', 4, 4], 'double' => ['double', '0.0', 8, 8], ]; } #[DataProvider('scalarsDataProvider')] public function testCastScalar(string $type, string $value, int $size, int $align) { $this->assertDumpEquals(<< size $size align $align { cdata: $value } PHP, \FFI::cdef()->new($type) ); } public function testCastVoidFunction() { $abi = \PHP_OS_FAMILY === 'Windows' ? '[cdecl]' : '[fastcall]'; $this->assertDumpEquals(<< size 1 align 1 {} } PHP, \FFI::cdef()->new('void (*)(void)') ); } public function testCastIntFunction() { $abi = \PHP_OS_FAMILY === 'Windows' ? '[cdecl]' : '[fastcall]'; $this->assertDumpEquals(<< size 8 align 8 {} } PHP, \FFI::cdef()->new('unsigned long long (*)(void)') ); } public function testCastFunctionWithArguments() { $abi = \PHP_OS_FAMILY === 'Windows' ? '[cdecl]' : '[fastcall]'; $this->assertDumpEquals(<< size 1 align 1 {} } PHP, \FFI::cdef()->new('void (*)(int a, const char* b)') ); } public function testCastNonCuttedPointerToChar() { $actualMessage = "Hello World!\0"; $string = \FFI::cdef()->new('char[100]'); $pointer = \FFI::addr($string[0]); \FFI::memcpy($pointer, $actualMessage, \strlen($actualMessage)); $this->assertDumpEquals(<<<'PHP' FFI\CData size 8 align 8 { cdata: "Hello World!\x00" } PHP, $pointer ); } public function testCastCuttedPointerToChar() { $actualMessage = str_repeat('Hello World!', 30)."\0"; $actualLength = \strlen($actualMessage); $expectedMessage = substr($actualMessage, 0, self::MAX_STRING_LENGTH); $string = \FFI::cdef()->new('char['.$actualLength.']'); $pointer = \FFI::addr($string[0]); \FFI::memcpy($pointer, $actualMessage, $actualLength); // the max length is platform-dependent and can be less than 255, // so we need to cut the expected message to the maximum length // allowed by pages size of the current system $ffi = \FFI::cdef(<<zend_get_page_size(); $start = $ffi->cast('uintptr_t', $ffi->cast('char*', $pointer))->cdata; $max = min(self::MAX_STRING_LENGTH, ($start | ($pageSize - 1)) - $start); $expectedMessage = substr($expectedMessage, 0, $max); $this->assertDumpEquals(<< size 8 align 8 { cdata: "$expectedMessage"… } PHP, $pointer ); } public function testCastNonTrailingCharPointer() { $actualMessage = 'Hello World!'; $actualLength = \strlen($actualMessage); $string = \FFI::cdef()->new('char['.($actualLength + 1).']'); $pointer = \FFI::addr($string[0]); \FFI::memcpy($pointer, $actualMessage, $actualLength); $pointer = \FFI::cdef()->cast('char*', \FFI::cdef()->cast('void*', $pointer)); $pointer[$actualLength] = "\x01"; $this->assertDumpMatchesFormat(<< size 8 align 8 { cdata: %A"$actualMessage%s" } PHP, $pointer ); } public function testCastUnionWithDirectReferencedFields() { $ffi = \FFI::cdef(<<<'CPP' typedef union Event { int32_t x; float y; } Event; CPP ); $this->assertDumpEquals(<<<'OUTPUT' FFI\CData size 4 align 4 { int32_t x: 0 float y: 0.0 } OUTPUT, $ffi->new('Event') ); } public function testCastUnionWithPointerReferencedFields() { $ffi = \FFI::cdef(<<<'CPP' typedef union Event { void* something; char* string; } Event; CPP ); $this->assertDumpEquals(<<<'OUTPUT' FFI\CData size 8 align 8 { something?: FFI\CType size 8 align 8 { 0: FFI\CType size 1 align 1 {} } string?: FFI\CType size 8 align 8 { 0: FFI\CType size 1 align 1 {} } } OUTPUT, $ffi->new('Event') ); } public function testCastUnionWithMixedFields() { $ffi = \FFI::cdef(<<<'CPP' typedef union Event { void* a; int32_t b; char* c; ptrdiff_t d; } Event; CPP ); $this->assertDumpEquals(<<<'OUTPUT' FFI\CData size 8 align 8 { a?: FFI\CType size 8 align 8 { 0: FFI\CType size 1 align 1 {} } int32_t b: 0 c?: FFI\CType size 8 align 8 { 0: FFI\CType size 1 align 1 {} } int64_t d: 0 } OUTPUT, $ffi->new('Event') ); } public function testCastPointerToEmptyScalars() { $ffi = \FFI::cdef(<<<'CPP' typedef struct { int8_t *a; uint8_t *b; int64_t *c; uint64_t *d; float *e; double *f; bool *g; } Example; CPP ); $this->assertDumpEquals(<<<'OUTPUT' FFI\CData> size 56 align 8 { int8_t* a: null uint8_t* b: null int64_t* c: null uint64_t* d: null float* e: null double* f: null bool* g: null } OUTPUT, $ffi->new('Example') ); } public function testCastPointerToNonEmptyScalars() { $ffi = \FFI::cdef(<<<'CPP' typedef struct { int8_t *a; uint8_t *b; int64_t *c; uint64_t *d; float *e; double *f; bool *g; } Example; CPP ); // Create values $int = \FFI::cdef()->new('int64_t'); $int->cdata = 42; $float = \FFI::cdef()->new('float'); $float->cdata = 42.0; $double = \FFI::cdef()->new('double'); $double->cdata = 42.2; $bool = \FFI::cdef()->new('bool'); $bool->cdata = true; // Fill struct $struct = $ffi->new('Example'); $struct->a = \FFI::addr(\FFI::cdef()->cast('int8_t', $int)); $struct->b = \FFI::addr(\FFI::cdef()->cast('uint8_t', $int)); $struct->c = \FFI::addr(\FFI::cdef()->cast('int64_t', $int)); $struct->d = \FFI::addr(\FFI::cdef()->cast('uint64_t', $int)); $struct->e = \FFI::addr(\FFI::cdef()->cast('float', $float)); $struct->f = \FFI::addr(\FFI::cdef()->cast('double', $double)); $struct->g = \FFI::addr(\FFI::cdef()->cast('bool', $bool)); $this->assertDumpEquals(<<<'OUTPUT' FFI\CData> size 56 align 8 { a: FFI\CData size 8 align 8 { cdata: 42 } b: FFI\CData size 8 align 8 { cdata: 42 } c: FFI\CData size 8 align 8 { cdata: 42 } d: FFI\CData size 8 align 8 { cdata: 42 } e: FFI\CData size 8 align 8 { cdata: 42.0 } f: FFI\CData size 8 align 8 { cdata: 42.2 } g: FFI\CData size 8 align 8 { cdata: true } } OUTPUT, $struct ); } public function testCastPointerToStruct() { $ffi = \FFI::cdef(<<<'CPP' typedef struct { int8_t a; } Example; CPP ); $struct = $ffi->new('Example', false); $this->assertDumpEquals(<<<'OUTPUT' FFI\CData*> size 8 align 8 { cdata: FFI\CData> size 1 align 1 { int8_t a: 0 } } OUTPUT, \FFI::addr($struct) ); // Save the pointer as variable so that // it is not cleaned up by the GC $pointer = \FFI::addr($struct); $this->assertDumpEquals(<<<'OUTPUT' FFI\CData**> size 8 align 8 { cdata: FFI\CData*> size 8 align 8 { cdata: FFI\CData> size 1 align 1 { int8_t a: 0 } } } OUTPUT, \FFI::addr($pointer) ); \FFI::free($struct); } public function testCastComplexType() { $ffi = \FFI::cdef(<<<'CPP' typedef struct { int x; int y; } Point; typedef struct Example { uint8_t a[32]; long b; __extension__ union { __extension__ struct { short c; long d; }; struct { Point point; float e; }; }; short f; bool g; int (*func)( struct __sub *h ); } Example; CPP ); $var = $ffi->new('Example'); $var->func = (static fn (object $p) => 42); $abi = \PHP_OS_FAMILY === 'Windows' ? '[cdecl]' : '[fastcall]'; $longSize = \FFI::cdef()->type('long')->getSize(); $longType = 8 === $longSize ? 'int64_t' : 'int32_t'; $structSize = 56 + $longSize * 2; $this->assertDumpEquals(<< size $structSize align 8 { a: FFI\CData size 32 align 1 {} $longType b: 0 int16_t c: 0 $longType d: 0 point: FFI\CData> size 8 align 4 { int32_t x: 0 int32_t y: 0 } float e: 0.0 int16_t f: 0 bool g: false func: $abi callable(struct __sub*): int32_t { returnType: FFI\CType size 4 align 4 {} } } OUTPUT, $var ); } } ================================================ FILE: Tests/Caster/FiberCasterTest.php ================================================ * * 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; class FiberCasterTest extends TestCase { use VarDumperTestTrait; public function testCastFiberNotStarted() { $fiber = new \Fiber(static fn () => true); $expected = <<assertDumpEquals($expected, $fiber); } public function testCastFiberTerminated() { $fiber = new \Fiber(static fn () => true); $fiber->start(); $expected = <<assertDumpEquals($expected, $fiber); } public function testCastFiberSuspended() { $fiber = new \Fiber(\Fiber::suspend(...)); $fiber->start(); $expected = <<assertDumpEquals($expected, $fiber); } public function testCastFiberRunning() { $fiber = new \Fiber(function () { $expected = <<assertDumpEquals($expected, \Fiber::getCurrent()); }); $fiber->start(); } } ================================================ FILE: Tests/Caster/GmpCasterTest.php ================================================ * * 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\Attributes\RequiresPhpExtension; use PHPUnit\Framework\TestCase; use Symfony\Component\VarDumper\Caster\GmpCaster; use Symfony\Component\VarDumper\Cloner\Stub; use Symfony\Component\VarDumper\Test\VarDumperTestTrait; class GmpCasterTest extends TestCase { use VarDumperTestTrait; #[RequiresPhpExtension('gmp')] public function testCastGmp() { $gmpString = gmp_init('1234'); $gmpOctal = gmp_init(0o10); $gmp = gmp_init('01101'); $gmpDump = << %s ] EODUMP; $this->assertDumpEquals(\sprintf($gmpDump, $gmpString), GmpCaster::castGmp($gmpString, [], new Stub(), false, 0)); $this->assertDumpEquals(\sprintf($gmpDump, $gmpOctal), GmpCaster::castGmp($gmpOctal, [], new Stub(), false, 0)); $this->assertDumpEquals(\sprintf($gmpDump, $gmp), GmpCaster::castGmp($gmp, [], new Stub(), false, 0)); $dump = <<assertDumpEquals($dump, $gmp); } } ================================================ FILE: Tests/Caster/IntlCasterTest.php ================================================ * * 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\Attributes\RequiresPhpExtension; use PHPUnit\Framework\TestCase; use Symfony\Component\VarDumper\Test\VarDumperTestTrait; #[RequiresPhpExtension('intl')] class IntlCasterTest extends TestCase { use VarDumperTestTrait; public function testMessageFormatter() { $var = new \MessageFormatter('en', 'Hello {name}'); $expected = <<assertDumpEquals($expected, $var); } public function testCastNumberFormatter() { $var = new \NumberFormatter('en', \NumberFormatter::DECIMAL); $expectedLocale = $var->getLocale(); $expectedPattern = $var->getPattern(); $expectedAttribute1 = $var->getAttribute(\NumberFormatter::PARSE_INT_ONLY); $expectedAttribute2 = $var->getAttribute(\NumberFormatter::GROUPING_USED); $expectedAttribute3 = $var->getAttribute(\NumberFormatter::DECIMAL_ALWAYS_SHOWN); $expectedAttribute4 = $var->getAttribute(\NumberFormatter::MAX_INTEGER_DIGITS); $expectedAttribute5 = $var->getAttribute(\NumberFormatter::MIN_INTEGER_DIGITS); $expectedAttribute6 = $var->getAttribute(\NumberFormatter::INTEGER_DIGITS); $expectedAttribute7 = $var->getAttribute(\NumberFormatter::MAX_FRACTION_DIGITS); $expectedAttribute8 = $var->getAttribute(\NumberFormatter::MIN_FRACTION_DIGITS); $expectedAttribute9 = $var->getAttribute(\NumberFormatter::FRACTION_DIGITS); $expectedAttribute10 = $var->getAttribute(\NumberFormatter::MULTIPLIER); $expectedAttribute11 = $var->getAttribute(\NumberFormatter::GROUPING_SIZE); $expectedAttribute12 = $var->getAttribute(\NumberFormatter::ROUNDING_MODE); $expectedAttribute13 = number_format($var->getAttribute(\NumberFormatter::ROUNDING_INCREMENT), 1); $expectedAttribute14 = $this->getDump($var->getAttribute(\NumberFormatter::FORMAT_WIDTH)); $expectedAttribute15 = $var->getAttribute(\NumberFormatter::PADDING_POSITION); $expectedAttribute16 = $var->getAttribute(\NumberFormatter::SECONDARY_GROUPING_SIZE); $expectedAttribute17 = $var->getAttribute(\NumberFormatter::SIGNIFICANT_DIGITS_USED); $expectedAttribute18 = $this->getDump($var->getAttribute(\NumberFormatter::MIN_SIGNIFICANT_DIGITS)); $expectedAttribute19 = $this->getDump($var->getAttribute(\NumberFormatter::MAX_SIGNIFICANT_DIGITS)); $expectedAttribute20 = $var->getAttribute(\NumberFormatter::LENIENT_PARSE); $expectedTextAttribute1 = $var->getTextAttribute(\NumberFormatter::POSITIVE_PREFIX); $expectedTextAttribute2 = $var->getTextAttribute(\NumberFormatter::POSITIVE_SUFFIX); $expectedTextAttribute3 = $var->getTextAttribute(\NumberFormatter::NEGATIVE_PREFIX); $expectedTextAttribute4 = $var->getTextAttribute(\NumberFormatter::NEGATIVE_SUFFIX); $expectedTextAttribute5 = $var->getTextAttribute(\NumberFormatter::PADDING_CHARACTER); $expectedTextAttribute6 = $var->getTextAttribute(\NumberFormatter::CURRENCY_CODE); $expectedTextAttribute7 = $var->getTextAttribute(\NumberFormatter::DEFAULT_RULESET) ? 'true' : 'false'; $expectedTextAttribute8 = $var->getTextAttribute(\NumberFormatter::PUBLIC_RULESETS) ? 'true' : 'false'; $expectedSymbol1 = $var->getSymbol(\NumberFormatter::DECIMAL_SEPARATOR_SYMBOL); $expectedSymbol2 = $var->getSymbol(\NumberFormatter::GROUPING_SEPARATOR_SYMBOL); $expectedSymbol3 = $var->getSymbol(\NumberFormatter::PATTERN_SEPARATOR_SYMBOL); $expectedSymbol4 = $var->getSymbol(\NumberFormatter::PERCENT_SYMBOL); $expectedSymbol5 = $var->getSymbol(\NumberFormatter::ZERO_DIGIT_SYMBOL); $expectedSymbol6 = $var->getSymbol(\NumberFormatter::DIGIT_SYMBOL); $expectedSymbol7 = $var->getSymbol(\NumberFormatter::MINUS_SIGN_SYMBOL); $expectedSymbol8 = $var->getSymbol(\NumberFormatter::PLUS_SIGN_SYMBOL); $expectedSymbol9 = $var->getSymbol(\NumberFormatter::CURRENCY_SYMBOL); $expectedSymbol10 = $var->getSymbol(\NumberFormatter::INTL_CURRENCY_SYMBOL); $expectedSymbol11 = $var->getSymbol(\NumberFormatter::MONETARY_SEPARATOR_SYMBOL); $expectedSymbol12 = $var->getSymbol(\NumberFormatter::EXPONENTIAL_SYMBOL); $expectedSymbol13 = $var->getSymbol(\NumberFormatter::PERMILL_SYMBOL); $expectedSymbol14 = $var->getSymbol(\NumberFormatter::PAD_ESCAPE_SYMBOL); $expectedSymbol15 = $var->getSymbol(\NumberFormatter::INFINITY_SYMBOL); $expectedSymbol16 = $var->getSymbol(\NumberFormatter::NAN_SYMBOL); $expectedSymbol17 = $var->getSymbol(\NumberFormatter::SIGNIFICANT_DIGIT_SYMBOL); $expectedSymbol18 = $var->getSymbol(\NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL); $expected = <<assertDumpEquals($expected, $var); } public function testCastIntlTimeZoneWithDST() { $var = \IntlTimeZone::createTimeZone('America/Los_Angeles'); $expectedDisplayName = $var->getDisplayName(); $expectedDSTSavings = $var->getDSTSavings(); $expectedID = $var->getID(); $expectedRawOffset = $var->getRawOffset(); $expected = <<assertDumpEquals($expected, $var); } public function testCastIntlTimeZoneWithoutDST() { $var = \IntlTimeZone::createTimeZone('Asia/Bangkok'); $expectedDisplayName = $var->getDisplayName(); $expectedID = $var->getID(); $expectedRawOffset = $var->getRawOffset(); $expected = <<assertDumpEquals($expected, $var); } public function testCastIntlCalendar() { $var = \IntlCalendar::createInstance('America/Los_Angeles', 'en'); $expectedType = $var->getType(); $expectedFirstDayOfWeek = $var->getFirstDayOfWeek(); $expectedMinimalDaysInFirstWeek = $var->getMinimalDaysInFirstWeek(); $expectedRepeatedWallTimeOption = $var->getRepeatedWallTimeOption(); $expectedSkippedWallTimeOption = $var->getSkippedWallTimeOption(); $expectedTime = $var->getTime().'.0'; $expectedInDaylightTime = $var->inDaylightTime() ? 'true' : 'false'; $expectedIsLenient = $var->isLenient() ? 'true' : 'false'; $expectedTimeZone = $var->getTimeZone(); $expectedTimeZoneDisplayName = $expectedTimeZone->getDisplayName(); $expectedTimeZoneID = $expectedTimeZone->getID(); $expectedTimeZoneRawOffset = $expectedTimeZone->getRawOffset(); $expectedTimeZoneDSTSavings = $expectedTimeZone->getDSTSavings(); $expected = <<assertDumpEquals($expected, $var); } public function testCastDateFormatter() { $var = new \IntlDateFormatter('en', \IntlDateFormatter::TRADITIONAL, \IntlDateFormatter::TRADITIONAL); $expectedLocale = $var->getLocale(); $expectedPattern = $this->normalizeNarrowNoBreakSpaceCharacter($var->getPattern()); $expectedCalendar = $var->getCalendar(); $expectedTimeZoneId = $var->getTimeZoneId(); $expectedTimeType = $var->getTimeType(); $expectedDateType = $var->getDateType(); $expectedTimeZone = $var->getTimeZone(); $expectedTimeZoneDisplayName = $expectedTimeZone->getDisplayName(); $expectedTimeZoneID = $expectedTimeZone->getID(); $expectedTimeZoneRawOffset = $expectedTimeZone->getRawOffset(); $expectedTimeZoneDSTSavings = $expectedTimeZone->useDaylightTime() ? "\n dst_savings: ".$expectedTimeZone->getDSTSavings() : ''; $expectedCalendarObject = $var->getCalendarObject(); $expectedCalendarObjectType = $expectedCalendarObject->getType(); $expectedCalendarObjectFirstDayOfWeek = $expectedCalendarObject->getFirstDayOfWeek(); $expectedCalendarObjectMinimalDaysInFirstWeek = $expectedCalendarObject->getMinimalDaysInFirstWeek(); $expectedCalendarObjectRepeatedWallTimeOption = $expectedCalendarObject->getRepeatedWallTimeOption(); $expectedCalendarObjectSkippedWallTimeOption = $expectedCalendarObject->getSkippedWallTimeOption(); $expectedCalendarObjectTime = $expectedCalendarObject->getTime().'.0'; $expectedCalendarObjectInDaylightTime = $expectedCalendarObject->inDaylightTime() ? 'true' : 'false'; $expectedCalendarObjectIsLenient = $expectedCalendarObject->isLenient() ? 'true' : 'false'; $expectedCalendarObjectTimeZone = $expectedCalendarObject->getTimeZone(); $expectedCalendarObjectTimeZoneDisplayName = $expectedCalendarObjectTimeZone->getDisplayName(); $expectedCalendarObjectTimeZoneID = $expectedCalendarObjectTimeZone->getID(); $expectedCalendarObjectTimeZoneRawOffset = $expectedCalendarObjectTimeZone->getRawOffset(); $expectedCalendarObjectTimeZoneDSTSavings = $expectedTimeZone->useDaylightTime() ? "\n dst_savings: ".$expectedCalendarObjectTimeZone->getDSTSavings() : ''; $expected = <<assertDumpEquals($expected, $var); } private function normalizeNarrowNoBreakSpaceCharacter(string $input): string { return str_replace("\u{202F}", '\\u{202F}', $input); } } ================================================ FILE: Tests/Caster/MemcachedCasterTest.php ================================================ * * 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; /** * @author Jan Schädlich */ class MemcachedCasterTest extends TestCase { use VarDumperTestTrait; public function testCastMemcachedWithDefaultOptions() { if (!class_exists(\Memcached::class)) { $this->markTestSkipped('Memcached not available'); } $var = new \Memcached(); $var->addServer('127.0.0.1', 11211); $var->addServer('127.0.0.2', 11212); $expected = << array:3 [ "host" => "127.0.0.1" "port" => 11211 "type" => "TCP" ] 1 => array:3 [ "host" => "127.0.0.2" "port" => 11212 "type" => "TCP" ] ] options: {} } EOTXT; $this->assertDumpEquals($expected, $var); } public function testCastMemcachedWithCustomOptions() { if (!class_exists(\Memcached::class)) { $this->markTestSkipped('Memcached not available'); } $var = new \Memcached(); $var->addServer('127.0.0.1', 11211); $var->addServer('127.0.0.2', 11212); // set a subset of non default options to test boolean, string and integer output $var->setOption(\Memcached::OPT_COMPRESSION, false); $var->setOption(\Memcached::OPT_PREFIX_KEY, 'pre'); $var->setOption(\Memcached::OPT_DISTRIBUTION, \Memcached::DISTRIBUTION_CONSISTENT); $expected = <<<'EOTXT' Memcached { servers: array:2 [ 0 => array:3 [ "host" => "127.0.0.1" "port" => 11211 "type" => "TCP" ] 1 => array:3 [ "host" => "127.0.0.2" "port" => 11212 "type" => "TCP" ] ] options: { OPT_COMPRESSION: false OPT_PREFIX_KEY: "pre" OPT_DISTRIBUTION: 1 } } EOTXT; $this->assertDumpEquals($expected, $var); } } ================================================ FILE: Tests/Caster/MysqliCasterTest.php ================================================ * * 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\Attributes\Group; use PHPUnit\Framework\Attributes\RequiresPhpExtension; use PHPUnit\Framework\TestCase; use Symfony\Component\VarDumper\Test\VarDumperTestTrait; #[RequiresPhpExtension('mysqli')] #[Group('integration')] class MysqliCasterTest extends TestCase { use VarDumperTestTrait; public function testNotConnected() { $driver = new \mysqli_driver(); $driver->report_mode = 3; $xCast = <<assertDumpMatchesFormat($xCast, $driver); } } ================================================ FILE: Tests/Caster/OpenSSLCasterTest.php ================================================ * * 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\Attributes\RequiresPhpExtension; use PHPUnit\Framework\TestCase; use Symfony\Component\VarDumper\Test\VarDumperTestTrait; #[RequiresPhpExtension('openssl')] class OpenSSLCasterTest extends TestCase { use VarDumperTestTrait; public function testAsymmetricKey() { $key = openssl_pkey_new([ 'private_key_bits' => 1024, 'private_key_type' => \OPENSSL_KEYTYPE_RSA, ]); if (false === $key) { $this->markTestSkipped('Unable to generate a key pair'); } $this->assertDumpMatchesFormat( <<<'EODUMP' OpenSSLAsymmetricKey { bits: 1024 key: """ -----BEGIN PUBLIC KEY-----\n %A %A %A %A -----END PUBLIC KEY-----\n """ type: 0 } EODUMP, $key ); } public function testOpensslCsr() { $dn = [ 'countryName' => 'FR', 'stateOrProvinceName' => 'Ile-de-France', 'localityName' => 'Paris', 'organizationName' => 'Symfony', 'organizationalUnitName' => 'Security', 'commonName' => 'symfony.com', 'emailAddress' => 'test@symfony.com', ]; $privkey = openssl_pkey_new(); $csr = openssl_csr_new($dn, $privkey); if (false === $csr) { $this->markTestSkipped('Unable to generate a CSR'); } $this->assertDumpMatchesFormat( <<<'EODUMP' OpenSSLCertificateSigningRequest { countryName: "FR" stateOrProvinceName: "Ile-de-France" localityName: "Paris" organizationName: "Symfony" organizationalUnitName: "Security" commonName: "symfony.com" emailAddress: "test@symfony.com" } EODUMP, $csr ); } } ================================================ FILE: Tests/Caster/PdoCasterTest.php ================================================ * * 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\Attributes\RequiresPhpExtension; use PHPUnit\Framework\TestCase; use Symfony\Component\VarDumper\Caster\ConstStub; use Symfony\Component\VarDumper\Caster\EnumStub; use Symfony\Component\VarDumper\Caster\PdoCaster; use Symfony\Component\VarDumper\Cloner\Stub; use Symfony\Component\VarDumper\Test\VarDumperTestTrait; /** * @author Nicolas Grekas */ class PdoCasterTest extends TestCase { use VarDumperTestTrait; #[RequiresPhpExtension('pdo_sqlite')] public function testCastPdo() { $pdo = new \PDO('sqlite::memory:'); $pdo->setAttribute(\PDO::ATTR_STATEMENT_CLASS, ['PDOStatement', [$pdo]]); $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); $cast = PdoCaster::castPdo($pdo, [], new Stub(), false); $this->assertInstanceOf(EnumStub::class, $cast["\0~\0attributes"]); $attr = $cast["\0~\0attributes"] = $cast["\0~\0attributes"]->value; $this->assertInstanceOf(ConstStub::class, $attr['CASE']); $this->assertSame('NATURAL', $attr['CASE']->class); $this->assertSame('BOTH', $attr['DEFAULT_FETCH_MODE']->class); $xDump = <<<'EODUMP' array:2 [ "\x00~\x00inTransaction" => false "\x00~\x00attributes" => array:10 [ "CASE" => NATURAL "ERRMODE" => EXCEPTION "PERSISTENT" => false "DRIVER_NAME" => "sqlite" "ORACLE_NULLS" => NATURAL "CLIENT_VERSION" => "%s" "SERVER_VERSION" => "%s" "STATEMENT_CLASS" => array:%d [ 0 => "PDOStatement"%A ] "STRINGIFY_FETCHES" => false "DEFAULT_FETCH_MODE" => BOTH ] ] EODUMP; $this->assertDumpMatchesFormat($xDump, $cast); } } ================================================ FILE: Tests/Caster/RdKafkaCasterTest.php ================================================ * * 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\Attributes\Group; use PHPUnit\Framework\Attributes\RequiresPhpExtension; use PHPUnit\Framework\TestCase; use RdKafka\Conf; use RdKafka\KafkaConsumer; use RdKafka\Producer; use RdKafka\TopicConf; use Symfony\Component\VarDumper\Test\VarDumperTestTrait; #[RequiresPhpExtension('rdkafka')] #[Group('integration')] class RdKafkaCasterTest extends TestCase { use VarDumperTestTrait; private const TOPIC = 'test-topic'; private const GROUP_ID = 'test-group-id'; private bool $hasBroker = false; private string $broker; protected function setUp(): void { if (!$this->hasBroker && getenv('KAFKA_BROKER')) { $this->broker = getenv('KAFKA_BROKER'); $this->hasBroker = true; } } public function testDumpConf() { $conf = new Conf(); $conf->setErrorCb(static function ($kafka, $err, $reason) {}); $conf->setDrMsgCb(static function () {}); $conf->setRebalanceCb(static function () {}); // BC with earlier version of extension rdkafka foreach (['setLogCb', 'setOffsetCommitCb', 'setStatsCb', 'setConsumeCb'] as $method) { if (method_exists($conf, $method)) { $conf->{$method}(static function () {}); } } $expectedDump = <<assertDumpMatchesFormat($expectedDump, $conf); } public function testDumpProducer() { if (!$this->hasBroker) { $this->markTestSkipped('Test requires an active broker'); } $producer = new Producer(new Conf()); $producer->addBrokers($this->broker); $expectedDump = <<broker/1001" brokers: RdKafka\Metadata\Collection { +0: RdKafka\Metadata\Broker { id: 1001 host: "%s" port: %d } } topics: RdKafka\Metadata\Collection { +0: RdKafka\Metadata\Topic { name: "%s" partitions: RdKafka\Metadata\Collection { +0: RdKafka\Metadata\Partition { id: 0 err: 0 leader: 1001 }%A } }%A } } EODUMP; $this->assertDumpMatchesFormat($expectedDump, $producer); } public function testDumpTopicConf() { $topicConf = new TopicConf(); $topicConf->set('auto.offset.reset', 'smallest'); $expectedDump = <<assertDumpMatchesFormat($expectedDump, $topicConf); } public function testDumpKafkaConsumer() { if (!$this->hasBroker) { $this->markTestSkipped('Test requires an active broker'); } $conf = new Conf(); $conf->set('metadata.broker.list', $this->broker); $conf->set('group.id', self::GROUP_ID); $consumer = new KafkaConsumer($conf); $consumer->subscribe([self::TOPIC]); $expectedDump = << "test-topic" ] assignment: [] orig_broker_id: %i orig_broker_name: "$this->broker/%s" brokers: RdKafka\Metadata\Collection { +0: RdKafka\Metadata\Broker { id: 1001 host: "%s" port: %d } } topics: RdKafka\Metadata\Collection { +0: RdKafka\Metadata\Topic { name: "%s" partitions: RdKafka\Metadata\Collection { +0: RdKafka\Metadata\Partition { id: 0 err: 0 leader: 1001 }%A } }%A } } EODUMP; $this->assertDumpMatchesFormat($expectedDump, $consumer); } public function testDumpProducerTopic() { $producer = new Producer(new Conf()); $producer->addBrokers($this->broker); $topic = $producer->newTopic('test'); $topic->produce(\RD_KAFKA_PARTITION_UA, 0, '{}'); $expectedDump = <<assertDumpMatchesFormat($expectedDump, $topic); } public function testDumpMessage() { $conf = new Conf(); $conf->set('metadata.broker.list', $this->broker); $conf->set('group.id', self::GROUP_ID); $consumer = new KafkaConsumer($conf); $consumer->subscribe([self::TOPIC]); // Force timeout $message = $consumer->consume(0); $expectedDump = <<assertDumpMatchesFormat($expectedDump, $message); } } ================================================ FILE: Tests/Caster/RedisCasterTest.php ================================================ * * 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\Attributes\Group; use PHPUnit\Framework\Attributes\RequiresPhpExtension; use PHPUnit\Framework\Attributes\TestWith; use PHPUnit\Framework\TestCase; use Relay\Relay; use Symfony\Component\VarDumper\Test\VarDumperTestTrait; /** * @author Nicolas Grekas */ #[Group('integration')] class RedisCasterTest extends TestCase { use VarDumperTestTrait; #[RequiresPhpExtension('redis')] public function testNotConnected() { $redis = new \Redis(); $xCast = <<<'EODUMP' Redis { isConnected: false } EODUMP; $this->assertDumpMatchesFormat($xCast, $redis); } #[TestWith([\Redis::class])] #[TestWith([Relay::class])] public function testConnected(string $class) { if (!class_exists($class)) { self::markTestSkipped(\sprintf('"%s" class required', $class)); } $redisHost = explode(':', getenv('REDIS_HOST')) + [1 => 6379]; $redis = new $class(); try { $redis->connect(...$redisHost); } catch (\Exception $e) { self::markTestSkipped($e->getMessage()); } $xCast = <<assertDumpMatchesFormat($xCast, $redis); } } ================================================ FILE: Tests/Caster/ReflectionCasterTest.php ================================================ * * 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\Caster\Caster; use Symfony\Component\VarDumper\Test\VarDumperTestTrait; use Symfony\Component\VarDumper\Tests\Fixtures\ExtendsReflectionTypeFixture; use Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo; use Symfony\Component\VarDumper\Tests\Fixtures\LotsOfAttributes; use Symfony\Component\VarDumper\Tests\Fixtures\NotLoadableClass; use Symfony\Component\VarDumper\Tests\Fixtures\Php82NullStandaloneReturnType; use Symfony\Component\VarDumper\Tests\Fixtures\ReflectionIntersectionTypeFixture; use Symfony\Component\VarDumper\Tests\Fixtures\ReflectionNamedTypeFixture; use Symfony\Component\VarDumper\Tests\Fixtures\ReflectionUnionTypeFixture; use Symfony\Component\VarDumper\Tests\Fixtures\ReflectionUnionTypeWithIntersectionFixture; /** * @author Nicolas Grekas */ class ReflectionCasterTest extends TestCase { use VarDumperTestTrait; public function testReflectionCaster() { $var = new \ReflectionClass(\ReflectionClass::class); $this->assertDumpMatchesFormat( <<<'EOTXT' ReflectionClass { +name: "ReflectionClass" %Aimplements: array:%d [ %A] constants: array:%d [ 0 => ReflectionClassConstant { +name: "IS_IMPLICIT_ABSTRACT" +class: "ReflectionClass" modifiers: "public" value: 16 } 1 => ReflectionClassConstant { +name: "IS_EXPLICIT_ABSTRACT" +class: "ReflectionClass" modifiers: "public" value: %d } 2 => ReflectionClassConstant { +name: "IS_FINAL" +class: "ReflectionClass" modifiers: "public" value: %d } %A] properties: array:%d [ "name" => ReflectionProperty { %A +name: "name" +class: "ReflectionClass" %A modifiers: "public" } %A] methods: array:%d [ %A "__construct" => ReflectionMethod { +name: "__construct" +class: "ReflectionClass" %A parameters: { $%s: ReflectionParameter { %A position: 0 %A } EOTXT, $var ); } public function testClosureCaster() { $a = $b = 123; $var = static function ($x) use ($a, &$b) { var_dump($a, $b); }; $this->assertDumpMatchesFormat( <<<'EOTXT' Closure($x) { %Ause: { $a: 123 $b: & 123 } file: "%sReflectionCasterTest.php" line: "%s" } EOTXT, $var ); } public function testFromCallableClosureCaster() { $var = [ (new \ReflectionMethod($this, __FUNCTION__))->getClosure($this), (new \ReflectionMethod(__CLASS__, 'stub'))->getClosure(), ]; $this->assertDumpMatchesFormat( << Symfony\Component\VarDumper\Tests\Caster\ReflectionCasterTest::testFromCallableClosureCaster() { this: Symfony\Component\VarDumper\Tests\Caster\ReflectionCasterTest { …} file: "%sReflectionCasterTest.php" line: "%d to %d" } 1 => Symfony\Component\VarDumper\Tests\Caster\ReflectionCasterTest::stub(): void { returnType: "void" file: "%sReflectionCasterTest.php" line: "%d to %d" } ] EOTXT, $var ); } public function testClosureCasterExcludingVerbosity() { $var = function &($a = 123) { \assert(null !== $this); }; $this->assertDumpEquals('Closure&($a = 123) { …5}', $var, Caster::EXCLUDE_VERBOSE); } public function testReflectionParameter() { $var = new \ReflectionParameter(reflectionParameterFixture::class, 0); $this->assertDumpMatchesFormat( <<<'EOTXT' ReflectionParameter { +name: "arg1" position: 0 allowsNull: true typeHint: "Symfony\Component\VarDumper\Tests\Fixtures\NotLoadableClass" } EOTXT, $var ); } public function testReflectionParameterScalar() { $f = static function (int $a) {}; $var = new \ReflectionParameter($f, 0); $this->assertDumpMatchesFormat( <<<'EOTXT' ReflectionParameter { +name: "a" position: 0 typeHint: "int" } EOTXT, $var ); } public function testReflectionParameterMixed() { $f = static function (mixed $a) {}; $var = new \ReflectionParameter($f, 0); $this->assertDumpMatchesFormat( <<<'EOTXT' ReflectionParameter { +name: "a" position: 0 allowsNull: true typeHint: "mixed" } EOTXT, $var ); } public function testReflectionParameterUnion() { $f = static function (int|float $a) {}; $var = new \ReflectionParameter($f, 0); $this->assertDumpMatchesFormat( <<<'EOTXT' ReflectionParameter { +name: "a" position: 0 typeHint: "int|float" } EOTXT, $var ); } public function testReflectionParameterNullableUnion() { $f = static function (int|float|null $a) {}; $var = new \ReflectionParameter($f, 0); $this->assertDumpMatchesFormat( <<<'EOTXT' ReflectionParameter { +name: "a" position: 0 allowsNull: true typeHint: "int|float|null" } EOTXT, $var ); } public function testReflectionParameterIntersection() { $f = static function (\Traversable&\Countable $a) {}; $var = new \ReflectionParameter($f, 0); $this->assertDumpMatchesFormat( <<<'EOTXT' ReflectionParameter { +name: "a" position: 0 typeHint: "Traversable&Countable" } EOTXT, $var ); } public function testReflectionPropertyScalar() { $var = new \ReflectionProperty(ReflectionNamedTypeFixture::class, 'a'); $this->assertDumpMatchesFormat( <<<'EOTXT' ReflectionProperty { +name: "a" +class: "Symfony\Component\VarDumper\Tests\Fixtures\ReflectionNamedTypeFixture" modifiers: "public" } EOTXT, $var ); } public function testReflectionNamedType() { $var = (new \ReflectionProperty(ReflectionNamedTypeFixture::class, 'a'))->getType(); $this->assertDumpMatchesFormat( <<<'EOTXT' ReflectionNamedType { name: "int" allowsNull: false isBuiltin: true } EOTXT, $var ); } public function testReflectionUnionType() { $var = (new \ReflectionProperty(ReflectionUnionTypeFixture::class, 'a'))->getType(); $this->assertDumpMatchesFormat( <<<'EOTXT' ReflectionUnionType { allowsNull: false types: array:2 [ 0 => ReflectionNamedType { name: "string" allowsNull: false isBuiltin: true } 1 => ReflectionNamedType { name: "int" allowsNull: false isBuiltin: true } ] } EOTXT, $var ); } public function testReflectionIntersectionType() { $var = (new \ReflectionProperty(ReflectionIntersectionTypeFixture::class, 'a'))->getType(); $this->assertDumpMatchesFormat( <<<'EOTXT' ReflectionIntersectionType { allowsNull: false types: array:2 [ 0 => ReflectionNamedType { name: "Traversable" allowsNull: false isBuiltin: false } 1 => ReflectionNamedType { name: "Countable" allowsNull: false isBuiltin: false } ] } EOTXT, $var ); } public function testReflectionUnionTypeWithIntersection() { $var = (new \ReflectionProperty(ReflectionUnionTypeWithIntersectionFixture::class, 'a'))->getType(); $this->assertDumpMatchesFormat( <<<'EOTXT' ReflectionUnionType { allowsNull: true types: array:2 [ 0 => ReflectionIntersectionType { allowsNull: false types: array:2 [ 0 => ReflectionNamedType { name: "Traversable" allowsNull: false isBuiltin: false } 1 => ReflectionNamedType { name: "Countable" allowsNull: false isBuiltin: false } ] } 1 => ReflectionNamedType { name: "null" allowsNull: true isBuiltin: true } ] } EOTXT, $var ); } public function testExtendsReflectionType() { $var = new ExtendsReflectionTypeFixture(); $this->assertDumpMatchesFormat( <<<'EOTXT' Symfony\Component\VarDumper\Tests\Fixtures\ExtendsReflectionTypeFixture { allowsNull: false } EOTXT, $var ); } public function testReturnType() { $f = function (): int { \assert(null !== $this); }; $this->assertDumpMatchesFormat( <<assertDumpMatchesFormat( <<assertDumpMatchesFormat( <<foo(...) ); } public function testUnionReturnType() { $f = function (): int|float { \assert(null !== $this); }; $this->assertDumpMatchesFormat( <<assertDumpMatchesFormat( <<markTestSkipped('xdebug is active'); } $generator = new GeneratorDemo(); $generator = $generator->baz(); $expectedDump = <<<'EODUMP' Generator { function: "Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo::baz" this: Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo { …} %s: { %sGeneratorDemo.php:12 { Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo->baz() › › public function baz() › { } Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo->baz() {} %A} closed: false } EODUMP; $this->assertDumpMatchesFormat($expectedDump, $generator); foreach ($generator as $v) { break; } $expectedDump = <<<'EODUMP' array:2 [ 0 => ReflectionGenerator { this: Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo { …} %s: { %s%eTests%eFixtures%eGeneratorDemo.php:%d { Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo::foo() › { › yield 1; › } %A } %s%eTests%eFixtures%eGeneratorDemo.php:20 { …} %s%eTests%eFixtures%eGeneratorDemo.php:14 { …} %A } closed: false } 1 => Generator { function: "Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo::foo" %s: { %s%eTests%eFixtures%eGeneratorDemo.php:%d { Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo::foo() › { › yield 1; › } } %A } closed: false } ] EODUMP; $r = new \ReflectionGenerator($generator); $this->assertDumpMatchesFormat($expectedDump, [$r, $r->getExecutingGenerator()]); foreach ($generator as $v) { } $expectedDump = <<<'EODUMP' Generator { function: "Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo::baz" closed: true } EODUMP; $this->assertDumpMatchesFormat($expectedDump, $generator); } public function testNewInInitializer() { $f = function ($a = new \stdClass()) { \assert(null !== $this); }; $this->assertDumpMatchesFormat( <<assertDumpMatchesFormat(<< ReflectionAttribute { +name: "Symfony\Component\VarDumper\Tests\Fixtures\MyAttribute" arguments: [] } ] %A } EOTXT, $var ); } public function testReflectionMethodWithAttribute() { $var = new \ReflectionMethod(LotsOfAttributes::class, 'someMethod'); $this->assertDumpMatchesFormat(<< ReflectionAttribute { +name: "Symfony\Component\VarDumper\Tests\Fixtures\MyAttribute" arguments: array:1 [ 0 => "two" ] } ] %A } EOTXT, $var ); } public function testReflectionPropertyWithAttribute() { $var = new \ReflectionProperty(LotsOfAttributes::class, 'someProperty'); $this->assertDumpMatchesFormat(<< ReflectionAttribute { +name: "Symfony\Component\VarDumper\Tests\Fixtures\MyAttribute" arguments: array:2 [ 0 => "one" "extra" => "hello" ] } ] } EOTXT, $var ); } public function testReflectionClassConstantWithAttribute() { $var = new \ReflectionClassConstant(LotsOfAttributes::class, 'SOME_CONSTANT'); $this->assertDumpMatchesFormat(<< ReflectionAttribute { +name: "Symfony\Component\VarDumper\Tests\Fixtures\RepeatableAttribute" arguments: array:1 [ 0 => "one" ] } 1 => ReflectionAttribute { +name: "Symfony\Component\VarDumper\Tests\Fixtures\RepeatableAttribute" arguments: array:1 [ 0 => "two" ] } ] } EOTXT, $var ); } public function testReflectionParameterWithAttribute() { $var = new \ReflectionParameter([LotsOfAttributes::class, 'someMethod'], 'someParameter'); $this->assertDumpMatchesFormat(<< ReflectionAttribute { +name: "Symfony\Component\VarDumper\Tests\Fixtures\MyAttribute" arguments: array:1 [ 0 => "three" ] } ] %A } EOTXT, $var ); } public static function stub(): void { } } function reflectionParameterFixture(?NotLoadableClass $arg1, $arg2) { } ================================================ FILE: Tests/Caster/ResourceCasterTest.php ================================================ * * 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\Attributes\RequiresPhpExtension; use PHPUnit\Framework\TestCase; use Symfony\Component\VarDumper\Test\VarDumperTestTrait; class ResourceCasterTest extends TestCase { use VarDumperTestTrait; #[RequiresPhpExtension('dba')] public function testCastDba() { $dba = dba_open(sys_get_temp_dir().'/test.db', 'c'); $this->assertDumpMatchesFormat( <<<'EODUMP' Dba\Connection { +file: %s } EODUMP, $dba ); } #[RequiresPhpExtension('dba')] public function testCastDbaOnBuggyPhp84() { if (\PHP_VERSION_ID >= 80402) { $this->markTestSkipped('The test can only be run on PHP 8.4.0 and 8.4.1, see https://github.com/php/php-src/issues/16990'); } $dba = dba_open(sys_get_temp_dir().'/test.db', 'c'); $this->assertDumpMatchesFormat( <<<'EODUMP' Dba\Connection { } EODUMP, $dba ); } } ================================================ FILE: Tests/Caster/SocketCasterTest.php ================================================ * * 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\Attributes\RequiresPhpExtension; use PHPUnit\Framework\TestCase; use Symfony\Component\VarDumper\Test\VarDumperTestTrait; #[RequiresPhpExtension('sockets')] class SocketCasterTest extends TestCase { use VarDumperTestTrait; public function testCastSocket() { $socket = socket_create(\AF_INET, \SOCK_DGRAM, \SOL_UDP); @socket_connect($socket, '127.0.0.1', 80); $this->assertDumpMatchesFormat( <<<'EODUMP' Socket { uri: "udp://127.0.0.1:%d" timed_out: false blocked: true%A } EODUMP, $socket ); } public function testCastSocketIpV6() { $socket = socket_create(\AF_INET6, \SOCK_STREAM, \SOL_TCP); @socket_connect($socket, '::1', 80); $this->assertDumpMatchesFormat( <<<'EODUMP' Socket { uri: "tcp://[%A]:%d" timed_out: false blocked: true last_error: SOCKET_ECONNREFUSED } EODUMP, $socket ); } public function testCastUnixSocket() { $socket = socket_create(\AF_UNIX, \SOCK_STREAM, 0); @socket_connect($socket, '/tmp/socket.sock'); $this->assertDumpMatchesFormat( <<<'EODUMP' Socket { uri: "unix://" timed_out: false blocked: true last_error: SOCKET_ENOENT } EODUMP, $socket ); } } ================================================ FILE: Tests/Caster/SplCasterTest.php ================================================ * * 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\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\VarDumper\Test\VarDumperTestTrait; /** * @author Grégoire Pineau */ class SplCasterTest extends TestCase { use VarDumperTestTrait; public static function getCastFileInfoTests() { return [ [__FILE__, <<<'EOTXT' SplFileInfo { %Apath: "%sCaster" filename: "SplCasterTest.php" basename: "SplCasterTest.php" pathname: "%sSplCasterTest.php" extension: "php" realPath: "%sSplCasterTest.php" aTime: %s-%s-%d %d:%d:%d mTime: %s-%s-%d %d:%d:%d cTime: %s-%s-%d %d:%d:%d inode: %i size: %d perms: 0%d owner: %d group: %d type: "file" writable: true readable: true executable: false file: true dir: false link: false %A} EOTXT ], ['http://example.com/about', <<<'EOTXT' SplFileInfo { %Apath: "http://example.com" filename: "about" basename: "about" pathname: "http://example.com/about" extension: "" realPath: false %A} EOTXT ], ]; } #[DataProvider('getCastFileInfoTests')] public function testCastFileInfo($file, $dump) { $this->assertDumpMatchesFormat($dump, new \SplFileInfo($file)); } public function testCastFileObject() { $var = new \SplFileObject(__FILE__); $var->setFlags(\SplFileObject::DROP_NEW_LINE | \SplFileObject::SKIP_EMPTY); $dump = <<<'EOTXT' SplFileObject { %Apath: "%sCaster" filename: "SplCasterTest.php" basename: "SplCasterTest.php" pathname: "%sSplCasterTest.php" extension: "php" realPath: "%sSplCasterTest.php" aTime: %s-%s-%d %d:%d:%d mTime: %s-%s-%d %d:%d:%d cTime: %s-%s-%d %d:%d:%d inode: %i size: %d perms: 0%d owner: %d group: %d type: "file" writable: true readable: true executable: false file: true dir: false link: false %AcsvControl: array:%d [ 0 => "," 1 => """ %A] flags: DROP_NEW_LINE|SKIP_EMPTY maxLineLen: 0 fstat: array:26 [ "dev" => %i "ino" => %i "nlink" => %d "rdev" => 0 "blksize" => %i "blocks" => %i …20 ] eof: false key: 0 } EOTXT; $this->assertDumpMatchesFormat($dump, $var); } #[DataProvider('provideCastSplDoublyLinkedList')] public function testCastSplDoublyLinkedList($modeValue, $modeDump) { $var = new \SplDoublyLinkedList(); $var->setIteratorMode($modeValue); $dump = <<assertDumpMatchesFormat($dump, $var); } public static function provideCastSplDoublyLinkedList() { return [ [\SplDoublyLinkedList::IT_MODE_FIFO, 'IT_MODE_FIFO | IT_MODE_KEEP'], [\SplDoublyLinkedList::IT_MODE_LIFO, 'IT_MODE_LIFO | IT_MODE_KEEP'], [\SplDoublyLinkedList::IT_MODE_FIFO | \SplDoublyLinkedList::IT_MODE_DELETE, 'IT_MODE_FIFO | IT_MODE_DELETE'], [\SplDoublyLinkedList::IT_MODE_LIFO | \SplDoublyLinkedList::IT_MODE_DELETE, 'IT_MODE_LIFO | IT_MODE_DELETE'], ]; } public function testCastObjectStorageIsntModified() { $var = new \SplObjectStorage(); $var[new \stdClass()] = null; $var->rewind(); $current = $var->current(); $this->assertDumpMatchesFormat('%A', $var); $this->assertSame($current, $var->current()); } public function testCastObjectStorageDumpsInfo() { $var = new \SplObjectStorage(); $var[new \stdClass()] = new \DateTimeImmutable(); $this->assertDumpMatchesFormat('%ADateTimeImmutable%A', $var); } public function testCastArrayObject() { $var = new #[\AllowDynamicProperties] class([123]) extends \ArrayObject {}; $var->foo = 234; $expected = << 123 ] flag::STD_PROP_LIST: false flag::ARRAY_AS_PROPS: false iteratorClass: "ArrayIterator" } EOTXT; $this->assertDumpEquals($expected, $var); } public function testArrayIterator() { $var = new MyArrayIterator([234]); $expected = << 234 ] flag::STD_PROP_LIST: false flag::ARRAY_AS_PROPS: false } EOTXT; $this->assertDumpEquals($expected, $var); } public function testBadSplFileInfo() { $var = new BadSplFileInfo(); $expected = <<assertDumpEquals($expected, $var); } public function testWeakMap() { $var = new \WeakMap(); $obj = new \stdClass(); $var[$obj] = 123; $expected = << { object: {} data: 123 } ] } EOTXT; $this->assertDumpEquals($expected, $var); } } class MyArrayIterator extends \ArrayIterator { private $foo = 123; } class BadSplFileInfo extends \SplFileInfo { public function __construct() { } } ================================================ FILE: Tests/Caster/SqliteCasterTest.php ================================================ * * 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\Attributes\RequiresPhpExtension; use PHPUnit\Framework\TestCase; use Symfony\Component\VarDumper\Test\VarDumperTestTrait; #[RequiresPhpExtension('sqlite3')] class SqliteCasterTest extends TestCase { use VarDumperTestTrait; public function testSqlite3Result() { $db = new \SQLite3(':memory:'); $db->exec('CREATE TABLE foo (id INTEGER PRIMARY KEY, bar TEXT)'); $db->exec('INSERT INTO foo (bar) VALUES ("baz")'); $stmt = $db->prepare('SELECT id, bar FROM foo'); $result = $stmt->execute(); $this->assertDumpMatchesFormat( <<<'EODUMP' SQLite3Result { columnNames: array:2 [ 0 => "id" 1 => "bar" ] } EODUMP, $result ); } } ================================================ FILE: Tests/Caster/StubCasterTest.php ================================================ * * 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\Caster\ArgsStub; use Symfony\Component\VarDumper\Caster\ClassStub; use Symfony\Component\VarDumper\Caster\LinkStub; use Symfony\Component\VarDumper\Caster\ScalarStub; use Symfony\Component\VarDumper\Caster\VirtualStub; use Symfony\Component\VarDumper\Cloner\VarCloner; use Symfony\Component\VarDumper\Dumper\HtmlDumper; use Symfony\Component\VarDumper\Test\VarDumperTestTrait; use Symfony\Component\VarDumper\Tests\Fixtures\FooInterface; use Symfony\Component\VarDumper\Tests\Fixtures\VirtualProperty; class StubCasterTest extends TestCase { use VarDumperTestTrait; public function testArgsStubWithDefaults($foo = 234, $bar = 456) { $args = [new ArgsStub([123], __FUNCTION__, __CLASS__)]; $expectedDump = <<<'EODUMP' array:1 [ 0 => { $foo: 123 } ] EODUMP; $this->assertDumpMatchesFormat($expectedDump, $args); } public function testArgsStubWithExtraArgs($foo = 234) { $args = [new ArgsStub([123, 456], __FUNCTION__, __CLASS__)]; $expectedDump = <<<'EODUMP' array:1 [ 0 => { $foo: 123 ...: { 456 } } ] EODUMP; $this->assertDumpMatchesFormat($expectedDump, $args); } public function testArgsStubNoParamWithExtraArgs() { $args = [new ArgsStub([123], __FUNCTION__, __CLASS__)]; $expectedDump = <<<'EODUMP' array:1 [ 0 => { 123 } ] EODUMP; $this->assertDumpMatchesFormat($expectedDump, $args); } public function testArgsStubWithClosure() { $args = [new ArgsStub([123], '{closure}', null)]; $expectedDump = <<<'EODUMP' array:1 [ 0 => { 123 } ] EODUMP; $this->assertDumpMatchesFormat($expectedDump, $args); } public function testEmptyStub() { $args = [new ScalarStub('🐛')]; $expectedDump = <<<'EODUMP' array:1 [ 0 => 🐛 ] EODUMP; $this->assertDumpMatchesFormat($expectedDump, $args); } public function testVirtualPropertyStub() { $class = new \ReflectionClass(VirtualProperty::class); $args = [new VirtualStub($class->getProperty('fullName'))]; $expectedDump = <<<'EODUMP' array:1 [ 0 => ~ string ] EODUMP; $this->assertDumpMatchesFormat($expectedDump, $args); } public function testVirtualPropertyWithoutTypeStub() { $class = new \ReflectionClass(VirtualProperty::class); $args = [new VirtualStub($class->getProperty('noType'))]; $expectedDump = <<<'EODUMP' array:1 [ 0 => ~ ] EODUMP; $this->assertDumpMatchesFormat($expectedDump, $args); } public function testLinkStub() { $var = [new LinkStub(__CLASS__, 0, __FILE__)]; $cloner = new VarCloner(); $dumper = new HtmlDumper(); $dumper->setDumpHeader(''); $dumper->setDumpBoundaries('', ''); $dumper->setDisplayOptions(['fileLinkFormat' => '%f:%l']); $dump = $dumper->dump($cloner->cloneVar($var), true); $expectedDump = <<<'EODUMP' array:1 [ 0 => "Symfony\Component\VarDumper\Tests\Caster\StubCasterTest" ] EODUMP; $this->assertStringMatchesFormat($expectedDump, $dump); } public function testLinkStubWithNoFileLink() { $var = [new LinkStub('example.com', 0, 'http://example.com')]; $cloner = new VarCloner(); $dumper = new HtmlDumper(); $dumper->setDumpHeader(''); $dumper->setDumpBoundaries('', ''); $dumper->setDisplayOptions(['fileLinkFormat' => '%f:%l']); $dump = $dumper->dump($cloner->cloneVar($var), true); $expectedDump = <<<'EODUMP' array:1 [ 0 => "example.com" ] EODUMP; $this->assertStringMatchesFormat($expectedDump, $dump); } public function testClassStub() { $var = [new ClassStub('hello', [FooInterface::class, 'foo'])]; $cloner = new VarCloner(); $dumper = new HtmlDumper(); $dumper->setDumpHeader(''); $dumper->setDumpBoundaries('', ''); $dump = $dumper->dump($cloner->cloneVar($var), true, ['fileLinkFormat' => '%f:%l']); $expectedDump = <<<'EODUMP' array:1 [ 0 => "hello(?stdClass $a, ?stdClass $b = null)" ] EODUMP; $this->assertStringMatchesFormat($expectedDump, $dump); } public function testClassStubWithNotExistingClass() { $var = [new ClassStub(NotExisting::class)]; $cloner = new VarCloner(); $dumper = new HtmlDumper(); $dumper->setDumpHeader(''); $dumper->setDumpBoundaries('', ''); $dump = $dumper->dump($cloner->cloneVar($var), true); $expectedDump = <<<'EODUMP' array:1 [ 0 => "Symfony\Component\VarDumper\Tests\Caster\NotExisting" ] EODUMP; $this->assertStringMatchesFormat($expectedDump, $dump); } public function testClassStubWithNotExistingMethod() { $var = [new ClassStub('hello', [FooInterface::class, 'missing'])]; $cloner = new VarCloner(); $dumper = new HtmlDumper(); $dumper->setDumpHeader(''); $dumper->setDumpBoundaries('', ''); $dump = $dumper->dump($cloner->cloneVar($var), true, ['fileLinkFormat' => '%f:%l']); $expectedDump = <<<'EODUMP' array:1 [ 0 => "hello" ] EODUMP; $this->assertStringMatchesFormat($expectedDump, $dump); } public function testClassStubWithAnonymousClass() { $var = [new ClassStub((new class extends \Exception { })::class)]; $cloner = new VarCloner(); $dumper = new HtmlDumper(); $dumper->setDumpHeader(''); $dumper->setDumpBoundaries('', ''); $dump = $dumper->dump($cloner->cloneVar($var), true, ['fileLinkFormat' => '%f:%l']); $expectedDump = <<<'EODUMP' array:1 [ 0 => "Exception@anonymous" ] EODUMP; $this->assertStringMatchesFormat($expectedDump, $dump); } } ================================================ FILE: Tests/Caster/SymfonyCasterTest.php ================================================ * * 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\Uid\Ulid; use Symfony\Component\Uid\UuidV4; use Symfony\Component\Uid\UuidV6; use Symfony\Component\VarDumper\Test\VarDumperTestTrait; final class SymfonyCasterTest extends TestCase { use VarDumperTestTrait; public function testCastUuid() { $uuid = new UuidV4('83a9db35-3c8c-4040-b3c1-02eccc00b419'); $expectedDump = <<assertDumpEquals($expectedDump, $uuid); $uuid = new UuidV6('1ebc50e9-8a23-6704-ad6f-59afd5cda7e5'); $expectedDump = <<assertDumpEquals($expectedDump, $uuid); } public function testCastUlid() { $ulid = new Ulid('01F7B252SZQGTSQGYSGACASAW6'); $expectedDump = <<assertDumpEquals($expectedDump, $ulid); } } ================================================ FILE: Tests/Caster/XmlReaderCasterTest.php ================================================ * * 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; /** * @author Baptiste Clavié */ class XmlReaderCasterTest extends TestCase { use VarDumperTestTrait; private \XMLReader $reader; protected function setUp(): void { $this->reader = new \XMLReader(); $this->reader->open(__DIR__.'/../Fixtures/xml_reader.xml'); } protected function tearDown(): void { $this->reader->close(); } public function testParserProperty() { $this->reader->setParserProperty(\XMLReader::SUBST_ENTITIES, true); $expectedDump = <<<'EODUMP' XMLReader {%A +nodeType: ~ int %A parserProperties: { SUBST_ENTITIES: true …3 } …12 } EODUMP; $this->assertDumpMatchesFormat($expectedDump, $this->reader); } public static function provideNodes() { return [ [0, <<<'EODUMP' XMLReader { +nodeType: NONE …13 } EODUMP ], [1, <<<'EODUMP' XMLReader { +localName: "foo" +nodeType: ELEMENT +baseURI: "%sxml_reader.xml" …11 } EODUMP ], [2, <<<'EODUMP' XMLReader { +localName: "#text" +nodeType: SIGNIFICANT_WHITESPACE +depth: 1 +value: """ \n """ +baseURI: "%sxml_reader.xml" …9 } EODUMP ], [3, <<<'EODUMP' XMLReader { +localName: "bar" +nodeType: ELEMENT +depth: 1 +baseURI: "%sxml_reader.xml" …10 } EODUMP ], [4, <<<'EODUMP' XMLReader { +localName: "bar" +nodeType: END_ELEMENT +depth: 1 +baseURI: "%sxml_reader.xml" …10 } EODUMP ], [6, <<<'EODUMP' XMLReader { +localName: "bar" +nodeType: ELEMENT +depth: 1 +isEmptyElement: true +baseURI: "%sxml_reader.xml" …9 } EODUMP ], [9, <<<'EODUMP' XMLReader { +localName: "#text" +nodeType: TEXT +depth: 2 +value: "With text" +baseURI: "%sxml_reader.xml" …9 } EODUMP ], [12, <<<'EODUMP' XMLReader { +localName: "bar" +nodeType: ELEMENT +depth: 1 +attributeCount: 2 +baseURI: "%sxml_reader.xml" …9 } EODUMP ], [13, <<<'EODUMP' XMLReader { +localName: "bar" +nodeType: END_ELEMENT +depth: 1 +baseURI: "%sxml_reader.xml" …10 } EODUMP ], [15, <<<'EODUMP' XMLReader { +localName: "bar" +nodeType: ELEMENT +depth: 1 +attributeCount: 1 +baseURI: "%sxml_reader.xml" …9 } EODUMP ], [16, <<<'EODUMP' XMLReader { +localName: "#text" +nodeType: SIGNIFICANT_WHITESPACE +depth: 2 +value: """ \n """ +baseURI: "%sxml_reader.xml" …9 } EODUMP ], [17, <<<'EODUMP' XMLReader { +localName: "baz" +prefix: "baz" +nodeType: ELEMENT +depth: 2 +namespaceURI: "http://symfony.com" +baseURI: "%sxml_reader.xml" …8 } EODUMP ], [18, <<<'EODUMP' XMLReader { +localName: "baz" +prefix: "baz" +nodeType: END_ELEMENT +depth: 2 +namespaceURI: "http://symfony.com" +baseURI: "%sxml_reader.xml" …8 } EODUMP ], [19, <<<'EODUMP' XMLReader { +localName: "#text" +nodeType: SIGNIFICANT_WHITESPACE +depth: 2 +value: """ \n """ +baseURI: "%sxml_reader.xml" …9 } EODUMP ], [21, <<<'EODUMP' XMLReader { +localName: "#text" +nodeType: SIGNIFICANT_WHITESPACE +depth: 1 +value: "\n" +baseURI: "%sxml_reader.xml" …9 } EODUMP ], [22, <<<'EODUMP' XMLReader { +localName: "foo" +nodeType: END_ELEMENT +baseURI: "%sxml_reader.xml" …11 } EODUMP ], ]; } public function testWithUninitializedXMLReader() { $this->reader = new \XMLReader(); $expectedDump = <<<'EODUMP' XMLReader {%A +nodeType: ~ int %A …13 } EODUMP; $this->assertDumpMatchesFormat($expectedDump, $this->reader); } } ================================================ FILE: Tests/Cloner/DataTest.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Tests\Cloner; use PHPUnit\Framework\TestCase; use Symfony\Component\VarDumper\Caster\Caster; use Symfony\Component\VarDumper\Caster\ClassStub; use Symfony\Component\VarDumper\Cloner\Data; use Symfony\Component\VarDumper\Cloner\VarCloner; class DataTest extends TestCase { public function testBasicData() { $values = [1 => 123, 4.5, 'abc', null, false]; $data = $this->cloneVar($values); $clonedValues = []; $this->assertInstanceOf(Data::class, $data); $this->assertCount(\count($values), $data); $this->assertFalse(isset($data->{0})); $this->assertFalse(isset($data[0])); foreach ($data as $k => $v) { $this->assertTrue(isset($data->{$k})); $this->assertTrue(isset($data[$k])); $this->assertSame(\gettype($values[$k]), $data->seek($k)->getType()); $this->assertSame($values[$k], $data->seek($k)->getValue()); $this->assertSame($values[$k], $data->{$k}); $this->assertSame($values[$k], $data[$k]); $this->assertSame((string) $values[$k], (string) $data->seek($k)); $clonedValues[$k] = $v->getValue(); } $this->assertSame($values, $clonedValues); } public function testObject() { $data = $this->cloneVar(new \Exception('foo')); $this->assertSame('Exception', $data->getType()); $this->assertSame('foo', $data->message); $this->assertSame('foo', $data->{Caster::PREFIX_PROTECTED.'message'}); $this->assertSame('foo', $data['message']); $this->assertSame('foo', $data[Caster::PREFIX_PROTECTED.'message']); $this->assertStringMatchesFormat('Exception (count=%d)', (string) $data); } public function testArray() { $values = [[], [123]]; $data = $this->cloneVar($values); $this->assertSame($values, $data->getValue(true)); $children = $data->getValue(); $this->assertIsArray($children); $this->assertInstanceOf(Data::class, $children[0]); $this->assertInstanceOf(Data::class, $children[1]); $this->assertEquals($children[0], $data[0]); $this->assertEquals($children[1], $data[1]); $this->assertSame($values[0], $children[0]->getValue(true)); $this->assertSame($values[1], $children[1]->getValue(true)); } public function testStub() { $data = $this->cloneVar([new ClassStub('stdClass')]); $data = $data[0]; $this->assertSame('string', $data->getType()); $this->assertSame('stdClass', $data->getValue()); $this->assertSame('stdClass', (string) $data); } public function testHardRefs() { $values = [[]]; $values[1] = &$values[0]; $values[2][0] = &$values[2]; $data = $this->cloneVar($values); $this->assertSame([], $data[0]->getValue()); $this->assertSame([], $data[1]->getValue()); $this->assertEquals([$data[2]->getValue()], $data[2]->getValue(true)); $this->assertSame('array (count=3)', (string) $data); } private function cloneVar($value) { $cloner = new VarCloner(); return $cloner->cloneVar($value); } } ================================================ FILE: Tests/Cloner/StubTest.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Tests\Cloner; use PHPUnit\Framework\TestCase; use Symfony\Component\VarDumper\Cloner\Stub; final class StubTest extends TestCase { public function testUnserializeNullValue() { $stub = new Stub(); $stub->value = null; $stub = unserialize(serialize($stub)); self::assertNull($stub->value); } public function testUnserializeNullInTypedProperty() { $stub = new MyStub(); $stub->myProp = null; $stub = unserialize(serialize($stub)); self::assertNull($stub->myProp); } public function testUninitializedStubPropertiesAreLeftUninitialized() { $stub = new MyStub(); $stub = unserialize(serialize($stub)); $r = new \ReflectionProperty(MyStub::class, 'myProp'); self::assertFalse($r->isInitialized($stub)); } } final class MyStub extends Stub { public mixed $myProp; } ================================================ FILE: Tests/Cloner/VarClonerTest.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Tests\Cloner; use PHPUnit\Framework\TestCase; use Symfony\Component\VarDumper\Caster\DateCaster; use Symfony\Component\VarDumper\Cloner\AbstractCloner; use Symfony\Component\VarDumper\Cloner\Stub; use Symfony\Component\VarDumper\Cloner\VarCloner; use Symfony\Component\VarDumper\Dumper\CliDumper; use Symfony\Component\VarDumper\Tests\Fixtures\Php74; use Symfony\Component\VarDumper\Tests\Fixtures\Php81Enums; /** * @author Nicolas Grekas */ class VarClonerTest extends TestCase { public function testAddCaster() { $o1 = new class { public string $p1 = 'p1'; }; $o2 = new class { public string $p2 = 'p2'; }; AbstractCloner::addDefaultCasters([ $o1::class => static function ($obj, $array) { $array['p1'] = 123; return $array; }, // Test we can override the default casters \DateTimeInterface::class => static function (\DateTimeInterface $obj, $array, Stub $stub, bool $isNested, int $filter) { $array = DateCaster::castDateTime($obj, $array, $stub, $isNested, $filter); $array['foo'] = 'bar'; return $array; }, ]); $cloner = new VarCloner(); $cloner->addCasters([ $o2::class => static function ($obj, $array) { $array['p2'] = 456; return $array; }, ]); $dumper = new CliDumper('php://output'); $dumper->setColors(false); ob_start(); $dumper->dump($cloner->cloneVar([$o1, $o2, new \DateTime('Mon Jan 4 15:26:20 2010 +0100')])); $out = ob_get_clean(); $out = preg_replace('/[ \t]+$/m', '', $out); $this->assertStringMatchesFormat( << class@anonymous {#%d +p1: 123 } 1 => class@anonymous {#%d +p2: 456 } 2 => DateTime @1262615180 {#%d date: 2010-01-04 15:26:20.0 +01:00 +foo: "bar" } ] EOTXT, $out ); } public function testMaxIntBoundary() { $data = [\PHP_INT_MAX => 123]; $cloner = new VarCloner(); $clone = $cloner->cloneVar($data); $expected = << Array ( [0] => Array ( [0] => Array ( [1] => 1 ) ) [1] => Array ( [%s] => 123 ) ) [position:Symfony\Component\VarDumper\Cloner\Data:private] => 0 [key:Symfony\Component\VarDumper\Cloner\Data:private] => 0 [maxDepth:Symfony\Component\VarDumper\Cloner\Data:private] => 20 [maxItemsPerDepth:Symfony\Component\VarDumper\Cloner\Data:private] => -1 [useRefHandles:Symfony\Component\VarDumper\Cloner\Data:private] => -1 [context:Symfony\Component\VarDumper\Cloner\Data:private] => Array ( ) ) EOTXT; $this->assertSame(\sprintf($expected, \PHP_INT_MAX), print_r($clone, true)); } public function testClone() { $json = json_decode('{"1":{"var":"val"},"2":{"var":"val"}}'); $cloner = new VarCloner(); $clone = $cloner->cloneVar($json); $expected = << Array ( [0] => Array ( [0] => Symfony\Component\VarDumper\Cloner\Stub Object ( [type] => 4 [class] => stdClass [value] => [cut] => 0 [handle] => %i [refCount] => 0 [position] => 1 [attr] => Array ( ) ) ) [1] => Array ( [\000+\0001] => Symfony\Component\VarDumper\Cloner\Stub Object ( [type] => 4 [class] => stdClass [value] => [cut] => 0 [handle] => %i [refCount] => 0 [position] => 2 [attr] => Array ( ) ) [\000+\0002] => Symfony\Component\VarDumper\Cloner\Stub Object ( [type] => 4 [class] => stdClass [value] => [cut] => 0 [handle] => %i [refCount] => 0 [position] => 3 [attr] => Array ( ) ) ) [2] => Array ( [\000+\000var] => val ) [3] => Array ( [\000+\000var] => val ) ) [position:Symfony\Component\VarDumper\Cloner\Data:private] => 0 [key:Symfony\Component\VarDumper\Cloner\Data:private] => 0 [maxDepth:Symfony\Component\VarDumper\Cloner\Data:private] => 20 [maxItemsPerDepth:Symfony\Component\VarDumper\Cloner\Data:private] => -1 [useRefHandles:Symfony\Component\VarDumper\Cloner\Data:private] => -1 [context:Symfony\Component\VarDumper\Cloner\Data:private] => Array ( ) ) EOTXT; $this->assertStringMatchesFormat($expected, print_r($clone, true)); } public function testLimits() { // Level 0: $data = [ // Level 1: [ // Level 2: [ // Level 3: 'Level 3 Item 0', 'Level 3 Item 1', 'Level 3 Item 2', 'Level 3 Item 3', ], [ 999 => 'Level 3 Item 4', 'Level 3 Item 5', 'Level 3 Item 6', ], [ 'Level 3 Item 7', ], ], [ [ 'Level 3 Item 8', ], 'Level 2 Item 0', ], [ 'Level 2 Item 1', ], 'Level 1 Item 0', [ // Test setMaxString: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'SHORT', ], ]; $cloner = new VarCloner(); $cloner->setMinDepth(2); $cloner->setMaxItems(5); $cloner->setMaxString(20); $clone = $cloner->cloneVar($data); $expected = << Array ( [0] => Array ( [0] => Array ( [2] => 1 ) ) [1] => Array ( [0] => Array ( [2] => 2 ) [1] => Array ( [2] => 3 ) [2] => Array ( [2] => 4 ) [3] => Level 1 Item 0 [4] => Array ( [2] => 5 ) ) [2] => Array ( [0] => Array ( [2] => 6 ) [1] => Array ( [0] => 2 [1] => 7 ) [2] => Array ( [0] => 1 [2] => 0 ) ) [3] => Array ( [0] => Array ( [0] => 1 [2] => 0 ) [1] => Level 2 Item 0 ) [4] => Array ( [0] => Level 2 Item 1 ) [5] => Array ( [0] => Symfony\Component\VarDumper\Cloner\Stub Object ( [type] => 2 [class] => 2 [value] => ABCDEFGHIJKLMNOPQRST [cut] => 6 [handle] => 0 [refCount] => 0 [position] => 0 [attr] => Array ( ) ) [1] => SHORT ) [6] => Array ( [0] => Level 3 Item 0 [1] => Level 3 Item 1 [2] => Level 3 Item 2 [3] => Level 3 Item 3 ) [7] => Array ( [999] => Level 3 Item 4 ) ) [position:Symfony\Component\VarDumper\Cloner\Data:private] => 0 [key:Symfony\Component\VarDumper\Cloner\Data:private] => 0 [maxDepth:Symfony\Component\VarDumper\Cloner\Data:private] => 20 [maxItemsPerDepth:Symfony\Component\VarDumper\Cloner\Data:private] => -1 [useRefHandles:Symfony\Component\VarDumper\Cloner\Data:private] => -1 [context:Symfony\Component\VarDumper\Cloner\Data:private] => Array ( ) ) EOTXT; $this->assertStringMatchesFormat($expected, print_r($clone, true)); } public function testJsonCast() { if (2 == \ini_get('xdebug.overload_var_dump')) { $this->markTestSkipped('xdebug is active'); } $data = (array) json_decode('{"1":{}}'); $cloner = new VarCloner(); $clone = $cloner->cloneVar($data); $expected = <<<'EOTXT' object(Symfony\Component\VarDumper\Cloner\Data)#%d (7) { ["data":"Symfony\Component\VarDumper\Cloner\Data":private]=> array(2) { [0]=> array(1) { [0]=> array(1) { [1]=> int(1) } } [1]=> array(1) { ["1"]=> object(Symfony\Component\VarDumper\Cloner\Stub)#%i (8) { ["type"]=> int(4) ["class"]=> string(8) "stdClass" ["value"]=> NULL ["cut"]=> int(0) ["handle"]=> int(%i) ["refCount"]=> int(0) ["position"]=> int(0) ["attr"]=> array(0) { } } } } ["position":"Symfony\Component\VarDumper\Cloner\Data":private]=> int(0) ["key":"Symfony\Component\VarDumper\Cloner\Data":private]=> int(0) ["maxDepth":"Symfony\Component\VarDumper\Cloner\Data":private]=> int(20) ["maxItemsPerDepth":"Symfony\Component\VarDumper\Cloner\Data":private]=> int(-1) ["useRefHandles":"Symfony\Component\VarDumper\Cloner\Data":private]=> int(-1) ["context":"Symfony\Component\VarDumper\Cloner\Data":private]=> array(0) { } } EOTXT; ob_start(); var_dump($clone); $this->assertStringMatchesFormat(str_replace('"1"', '1', $expected), ob_get_clean()); } public function testCaster() { $cloner = new VarCloner([ '*' => static fn ($obj, $array) => ['foo' => 123], __CLASS__ => static function ($obj, $array) { ++$array['foo']; return $array; }, ]); $clone = $cloner->cloneVar($this); $expected = << Array ( [0] => Array ( [0] => Symfony\Component\VarDumper\Cloner\Stub Object ( [type] => 4 [class] => %s [value] => [cut] => 0 [handle] => %i [refCount] => 0 [position] => 1 [attr] => Array ( [file] => %a%eVarClonerTest.php [line] => 26 ) ) ) [1] => Array ( [foo] => 124 ) ) [position:Symfony\Component\VarDumper\Cloner\Data:private] => 0 [key:Symfony\Component\VarDumper\Cloner\Data:private] => 0 [maxDepth:Symfony\Component\VarDumper\Cloner\Data:private] => 20 [maxItemsPerDepth:Symfony\Component\VarDumper\Cloner\Data:private] => -1 [useRefHandles:Symfony\Component\VarDumper\Cloner\Data:private] => -1 [context:Symfony\Component\VarDumper\Cloner\Data:private] => Array ( ) ) EOTXT; $this->assertStringMatchesFormat($expected, print_r($clone, true)); } public function testPhp74() { $data = new Php74(); $cloner = new VarCloner(); $clone = $cloner->cloneVar($data); $expected = <<<'EOTXT' Symfony\Component\VarDumper\Cloner\Data Object ( [data:Symfony\Component\VarDumper\Cloner\Data:private] => Array ( [0] => Array ( [0] => Symfony\Component\VarDumper\Cloner\Stub Object ( [type] => 4 [class] => Symfony\Component\VarDumper\Tests\Fixtures\Php74 [value] => [cut] => 0 [handle] => %i [refCount] => 0 [position] => 1 [attr] => Array ( [file] => %s [line] => 5 ) ) ) [1] => Array ( [p1] => 123 [p2] => Symfony\Component\VarDumper\Cloner\Stub Object ( [type] => 1 [class] => [value] => Symfony\Component\VarDumper\Cloner\Stub Object ( [type] => 4 [class] => stdClass [value] => [cut] => 0 [handle] => %i [refCount] => 1 [position] => 0 [attr] => Array ( ) ) [cut] => 0 [handle] => 1 [refCount] => 1 [position] => 0 [attr] => Array ( ) ) [p3] => Symfony\Component\VarDumper\Cloner\Stub Object ( [type] => 1 [class] => [value] => Symfony\Component\VarDumper\Cloner\Stub Object ( [type] => 4 [class] => stdClass [value] => [cut] => 0 [handle] => %i [refCount] => 1 [position] => 0 [attr] => Array ( ) ) [cut] => 0 [handle] => 1 [refCount] => 1 [position] => 0 [attr] => Array ( ) ) ) ) [position:Symfony\Component\VarDumper\Cloner\Data:private] => 0 [key:Symfony\Component\VarDumper\Cloner\Data:private] => 0 [maxDepth:Symfony\Component\VarDumper\Cloner\Data:private] => 20 [maxItemsPerDepth:Symfony\Component\VarDumper\Cloner\Data:private] => -1 [useRefHandles:Symfony\Component\VarDumper\Cloner\Data:private] => -1 [context:Symfony\Component\VarDumper\Cloner\Data:private] => Array ( ) ) EOTXT; $this->assertStringMatchesFormat($expected, print_r($clone, true)); } public function testPhp81Enums() { $data = new Php81Enums(); $cloner = new VarCloner(); $clone = $cloner->cloneVar($data); $expected = <<<'EOTXT' Symfony\Component\VarDumper\Cloner\Data Object ( [data:Symfony\Component\VarDumper\Cloner\Data:private] => Array ( [0] => Array ( [0] => Symfony\Component\VarDumper\Cloner\Stub Object ( [type] => 4 [class] => Symfony\Component\VarDumper\Tests\Fixtures\Php81Enums [value] => [cut] => 0 [handle] => %i [refCount] => 0 [position] => 1 [attr] => Array ( [file] => %s [line] => 5 ) ) ) [1] => Array ( [e1] => Symfony\Component\VarDumper\Cloner\Stub Object ( [type] => 4 [class] => Symfony\Component\VarDumper\Tests\Fixtures\UnitEnumFixture [value] => [cut] => 0 [handle] => %i [refCount] => 0 [position] => 2 [attr] => Array ( [file] => %s [line] => 5 ) ) [e2] => Symfony\Component\VarDumper\Cloner\Stub Object ( [type] => 4 [class] => Symfony\Component\VarDumper\Tests\Fixtures\BackedEnumFixture [value] => [cut] => 0 [handle] => %i [refCount] => 0 [position] => 3 [attr] => Array ( [file] => %s [line] => 5 ) ) ) [2] => Array ( [name] => Hearts ) [3] => Array ( [name] => Diamonds [value] => D ) ) [position:Symfony\Component\VarDumper\Cloner\Data:private] => 0 [key:Symfony\Component\VarDumper\Cloner\Data:private] => 0 [maxDepth:Symfony\Component\VarDumper\Cloner\Data:private] => 20 [maxItemsPerDepth:Symfony\Component\VarDumper\Cloner\Data:private] => -1 [useRefHandles:Symfony\Component\VarDumper\Cloner\Data:private] => -1 [context:Symfony\Component\VarDumper\Cloner\Data:private] => Array ( ) ) EOTXT; $this->assertStringMatchesFormat($expected, print_r($clone, true)); } } ================================================ FILE: Tests/Command/Descriptor/CliDescriptorTest.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Tests\Command\Descriptor; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\Console\Output\BufferedOutput; use Symfony\Component\VarDumper\Cloner\Data; use Symfony\Component\VarDumper\Command\Descriptor\CliDescriptor; use Symfony\Component\VarDumper\Dumper\CliDumper; class CliDescriptorTest extends TestCase { private static string $timezone; private static string|false $prevTerminalEmulator; public static function setUpBeforeClass(): void { self::$timezone = date_default_timezone_get(); date_default_timezone_set('UTC'); self::$prevTerminalEmulator = getenv('TERMINAL_EMULATOR'); putenv('TERMINAL_EMULATOR'); } public static function tearDownAfterClass(): void { date_default_timezone_set(self::$timezone); putenv('TERMINAL_EMULATOR'.(self::$prevTerminalEmulator ? '='.self::$prevTerminalEmulator : '')); } #[DataProvider('provideContext')] public function testDescribe(array $context, string $expectedOutput, bool $decorated = false) { $output = new BufferedOutput(); $output->setDecorated($decorated); $descriptor = new CliDescriptor(new CliDumper(static fn ($s) => $s)); $descriptor->describe($output, new Data([[123]]), $context + ['timestamp' => 1544804268.3668], 1); $this->assertStringMatchesFormat(trim($expectedOutput), str_replace(\PHP_EOL, "\n", trim($output->fetch()))); } public static function provideContext() { yield 'source' => [ [ 'source' => [ 'name' => 'CliDescriptorTest.php', 'line' => 30, 'file' => '/Users/ogi/symfony/src/Symfony/Component/VarDumper/Tests/Command/Descriptor/CliDescriptorTest.php', ], ], << [ [ 'source' => [ 'name' => 'CliDescriptorTest.php', 'line' => 30, 'file_relative' => 'src/Symfony/Component/VarDumper/Tests/Command/Descriptor/CliDescriptorTest.php', 'file' => '/Users/ogi/symfony/src/Symfony/Component/VarDumper/Tests/Command/Descriptor/CliDescriptorTest.php', 'file_link' => 'phpstorm://open?file=/Users/ogi/symfony/src/Symfony/Component/VarDumper/Tests/Command/Descriptor/CliDescriptorTest.php&line=30', ], ], << [ [ 'source' => [ 'name' => 'CliDescriptorTest.php', 'line' => 30, 'file_relative' => 'src/Symfony/Component/VarDumper/Tests/Command/Descriptor/CliDescriptorTest.php', 'file_link' => 'phpstorm://open?file=/Users/ogi/symfony/src/Symfony/Component/VarDumper/Tests/Command/Descriptor/CliDescriptorTest.php&line=30', ], ], << [ [ 'cli' => [ 'identifier' => 'd8bece1c', 'command_line' => 'bin/phpunit', ], ], << [ [ 'request' => [ 'identifier' => 'd8bece1c', 'controller' => new Data([['FooController.php']]), 'method' => 'GET', 'uri' => 'http://localhost/foo', ], ], << * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Tests\Command\Descriptor; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\Console\Output\BufferedOutput; use Symfony\Component\VarDumper\Cloner\Data; use Symfony\Component\VarDumper\Command\Descriptor\HtmlDescriptor; use Symfony\Component\VarDumper\Dumper\HtmlDumper; class HtmlDescriptorTest extends TestCase { private static string $timezone; public static function setUpBeforeClass(): void { self::$timezone = date_default_timezone_get(); date_default_timezone_set('UTC'); } public static function tearDownAfterClass(): void { date_default_timezone_set(self::$timezone); } public function testItOutputsStylesAndScriptsOnFirstDescribeCall() { $output = new BufferedOutput(); $dumper = $this->createStub(HtmlDumper::class); $dumper->method('dump')->willReturn('[DUMPED]'); $descriptor = new HtmlDescriptor($dumper); $descriptor->describe($output, new Data([[123]]), ['timestamp' => 1544804268.3668], 1); $this->assertStringMatchesFormat('%A', $output->fetch(), 'styles & scripts are output'); $descriptor->describe($output, new Data([[123]]), ['timestamp' => 1544804268.3668], 1); $this->assertDoesNotMatchRegularExpression('#(.*)#', $output->fetch(), 'styles & scripts are output only once'); } #[DataProvider('provideContext')] public function testDescribe(array $context, string $expectedOutput) { $output = new BufferedOutput(); $dumper = $this->createStub(HtmlDumper::class); $dumper->method('dump')->willReturn('[DUMPED]'); $descriptor = new HtmlDescriptor($dumper); $descriptor->describe($output, new Data([[123]]), $context + ['timestamp' => 1544804268.3668], 1); $this->assertStringMatchesFormat(trim($expectedOutput), trim(preg_replace('@@s', '', $output->fetch()))); } public static function provideContext() { yield 'source' => [ [ 'source' => [ 'name' => 'CliDescriptorTest.php', 'line' => 30, 'file' => '/Users/ogi/symfony/src/Symfony/Component/VarDumper/Tests/Command/Descriptor/CliDescriptorTest.php', ], ], <<

    -

    CliDescriptorTest.php on line 30

    [DUMPED]
    TXT, ]; yield 'source full' => [ [ 'source' => [ 'name' => 'CliDescriptorTest.php', 'project_dir' => 'src/Symfony/', 'line' => 30, 'file_relative' => 'src/Symfony/Component/VarDumper/Tests/Command/Descriptor/CliDescriptorTest.php', 'file' => '/Users/ogi/symfony/src/Symfony/Component/VarDumper/Tests/Command/Descriptor/CliDescriptorTest.php', 'file_link' => 'phpstorm://open?file=/Users/ogi/symfony/src/Symfony/Component/VarDumper/Tests/Command/Descriptor/CliDescriptorTest.php&line=30', ], ], <<

    -

    • project dirsrc/Symfony/

    CliDescriptorTest.php on line 30

    [DUMPED]
    TXT, ]; yield 'cli' => [ [ 'cli' => [ 'identifier' => 'd8bece1c', 'command_line' => 'bin/phpunit', ], ], <<

    $ bin/phpunit

    [DUMPED]
    TXT, ]; yield 'request' => [ [ 'request' => [ 'identifier' => 'd8bece1c', 'controller' => new Data([['FooController.php']]), 'method' => 'GET', 'uri' => 'http://localhost/foo', ], ], <<

    GET http://localhost/foo

    • controller[DUMPED]

    [DUMPED]
    TXT, ]; } } ================================================ FILE: Tests/Command/ServerDumpCommandTest.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Tests\Command; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\Console\Tester\CommandCompletionTester; use Symfony\Component\VarDumper\Command\ServerDumpCommand; use Symfony\Component\VarDumper\Server\DumpServer; class ServerDumpCommandTest extends TestCase { #[DataProvider('provideCompletionSuggestions')] public function testComplete(array $input, array $expectedSuggestions) { $tester = new CommandCompletionTester($this->createCommand()); $this->assertSame($expectedSuggestions, $tester->complete($input)); } public static function provideCompletionSuggestions() { yield 'option --format' => [ ['--format', ''], ['cli', 'html'], ]; } private function createCommand(): ServerDumpCommand { return new ServerDumpCommand($this->createStub(DumpServer::class)); } } ================================================ FILE: Tests/Dumper/CliDumperTest.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Tests\Dumper; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\VarDumper\Caster\ClassStub; use Symfony\Component\VarDumper\Caster\CutStub; use Symfony\Component\VarDumper\Cloner\Data; use Symfony\Component\VarDumper\Cloner\Stub; use Symfony\Component\VarDumper\Cloner\VarCloner; use Symfony\Component\VarDumper\Dumper\AbstractDumper; use Symfony\Component\VarDumper\Dumper\CliDumper; use Symfony\Component\VarDumper\Test\VarDumperTestTrait; use Symfony\Component\VarDumper\Tests\Fixtures\VirtualProperty; use Twig\Environment; use Twig\Loader\FilesystemLoader; /** * @author Nicolas Grekas */ class CliDumperTest extends TestCase { use VarDumperTestTrait; public function testGet() { require __DIR__.'/../Fixtures/dumb-var.php'; $dumper = new CliDumper('php://output'); $dumper->setColors(false); $cloner = new VarCloner(); $cloner->addCasters([ ':stream' => static function ($res, $a) { unset($a['uri'], $a['wrapper_data']); return $a; }, 'Symfony\Component\VarDumper\Tests\Fixture\DumbFoo' => static function ($foo, $a) { $a['foo'] = new CutStub($a['foo']); return $a; }, ]); $data = $cloner->cloneVar($var); ob_start(); $dumper->dump($data); $out = ob_get_clean(); $out = preg_replace('/[ \t]+$/m', '', $out); $intMax = \PHP_INT_MAX; $res = (int) $var['res']; $this->assertStringMatchesFormat( << 1 0 => &1 null "const" => 1.1 1 => true 2 => false 3 => NAN 4 => INF 5 => -INF 6 => {$intMax} "str" => "déjà\\n" 7 => b""" é\\x01test\\t\\n ing """ "bo\\u{FEFF}m" => "te\\u{FEFF}st" "[]" => [] "res" => stream resource {@{$res} %A wrapper_type: "plainfile" stream_type: "STDIO" mode: "r" unread_bytes: 0 seekable: true %A options: [] } "obj" => Symfony\Component\VarDumper\Tests\Fixture\DumbFoo {#%d +foo: ""…3 +"bar": "bar" } "closure" => Closure(\$a, ?PDO &\$b = null) {#%d class: "Symfony\Component\VarDumper\Tests\Dumper\CliDumperTest" this: Symfony\Component\VarDumper\Tests\Dumper\CliDumperTest {#%d …} file: "%s%eTests%eFixtures%edumb-var.php" line: "{$var['line']} to {$var['line']}" } "line" => {$var['line']} "nobj" => array:1 [ 0 => &3 {#%d} ] "recurs" => &4 array:1 [ 0 => &4 array:1 [&4] ] 8 => &1 null "sobj" => Symfony\Component\VarDumper\Tests\Fixture\DumbFoo {#%d} "snobj" => &3 {#%d} "snobj2" => {#%d} "file" => "{$var['file']}" b"bin-key-é" => "" ] EOTXT, $out ); } #[DataProvider('provideDumpWithCommaFlagTests')] public function testDumpWithCommaFlag($expected, $flags) { $dumper = new CliDumper(null, null, $flags); $dumper->setColors(false); $cloner = new VarCloner(); $var = [ 'array' => ['a', 'b'], 'string' => 'hello', 'multiline string' => "this\nis\na\multiline\nstring", ]; $dump = $dumper->dump($cloner->cloneVar($var), true); $this->assertSame($expected, $dump); } public function testDumpWithCommaFlagsAndExceptionCodeExcerpt() { $dumper = new CliDumper(null, null, CliDumper::DUMP_TRAILING_COMMA); $dumper->setColors(false); $cloner = new VarCloner(); $ex = new \RuntimeException('foo'); $dump = $dumper->dump($cloner->cloneVar($ex)->withRefHandles(false), true); $this->assertStringMatchesFormat(<<<'EOTXT' RuntimeException { #message: "foo" #code: 0 #file: "%ACliDumperTest.php" #line: %d trace: { %ACliDumperTest.php:%d { Symfony\Component\VarDumper\Tests\Dumper\CliDumperTest->testDumpWithCommaFlagsAndExceptionCodeExcerpt() › › $ex = new \RuntimeException('foo'); › } %A } } EOTXT, $dump ); } public static function provideDumpWithCommaFlagTests() { $expected = <<<'EOTXT' array:3 [ "array" => array:2 [ 0 => "a", 1 => "b" ], "string" => "hello", "multiline string" => """ this\n is\n a\multiline\n string """ ] EOTXT; yield [$expected, CliDumper::DUMP_COMMA_SEPARATOR]; $expected = <<<'EOTXT' array:3 [ "array" => array:2 [ 0 => "a", 1 => "b", ], "string" => "hello", "multiline string" => """ this\n is\n a\multiline\n string """, ] EOTXT; yield [$expected, CliDumper::DUMP_TRAILING_COMMA]; } public function testJsonCast() { $var = (array) json_decode('{"0":{},"1":null}'); foreach ($var as &$v) { } $var[] = &$v; $var[''] = 2; $this->assertDumpMatchesFormat( <<<'EOTXT' array:4 [ 0 => {} 1 => &1 null 2 => &1 null "" => 2 ] EOTXT, $var ); } public function testObjectCast() { $var = (object) [1 => 1]; $var->{1} = 2; $this->assertDumpMatchesFormat( <<<'EOTXT' { +"1": 2 } EOTXT, $var ); } public function testClosedResource() { $var = fopen(__FILE__, 'r'); fclose($var); $dumper = new CliDumper('php://output'); $dumper->setColors(false); $cloner = new VarCloner(); $data = $cloner->cloneVar($var); ob_start(); $dumper->dump($data); $out = ob_get_clean(); $res = (int) $var; $this->assertStringMatchesFormat( << 'bar'], ]; $this->assertDumpEquals( << (3) "foo" 2 => (3) "bar" ] ] EOTXT, $var ); putenv('DUMP_LIGHT_ARRAY='); putenv('DUMP_STRING_LENGTH='); } public function testVirtualProperties() { $this->assertDumpEquals(<<setColors(false); $cloner = new VarCloner(); $cloner->addCasters([ ':stream' => static function ($res, $a) { unset($a['wrapper_data']); return $a; }, ]); $cloner->addCasters([ ':stream' => eval('return function () use ($twig) { try { $twig->render([]); } catch (\Twig\Error\RuntimeError $e) { throw $e->getPrevious(); } };'), ]); $ref = (int) $out; $data = $cloner->cloneVar($out); $dumper->dump($data, $out); $out = stream_get_contents($out, -1, 0); $this->assertStringMatchesFormat( <<doDisplay(array \$context, array \$blocks = []): array › foo bar › twig source › } %A%eTemplate.php:%d { …} %s%eTests%eDumper%eCliDumperTest.php:%d { …} %A } } %Awrapper_type: "PHP" stream_type: "MEMORY" mode: "%s+b" unread_bytes: 0 seekable: true uri: "php://memory" %Aoptions: [] } EOTXT, $out ); } public function testRefsInProperties() { $var = (object) ['foo' => 'foo']; $var->bar = &$var->foo; $dumper = new CliDumper(); $dumper->setColors(false); $cloner = new VarCloner(); $data = $cloner->cloneVar($var); $out = $dumper->dump($data, true); $this->assertStringMatchesFormat( <<assertDumpMatchesFormat( << 'bar'], 0, << "\e[1;38;5;113mbar\e[0;38;5;208m"\e[m \e[0;38;5;208m]\e[m EOTXT, ]; yield [[], AbstractDumper::DUMP_LIGHT_ARRAY, "\e[0;38;5;208m[]\e[m\n"]; yield [ ['foo' => 'bar'], AbstractDumper::DUMP_LIGHT_ARRAY, << "\e[1;38;5;113mbar\e[0;38;5;208m"\e[m \e[0;38;5;208m]\e[m EOTXT, ]; yield [[], 0, "\e[0;38;5;208m[]\e[m\n"]; } #[DataProvider('provideDumpArrayWithColor')] public function testDumpArrayWithColor($value, $flags, $expectedOut) { if ('\\' === \DIRECTORY_SEPARATOR) { $this->markTestSkipped('Windows console does not support coloration'); } $out = ''; $dumper = new CliDumper(static function ($line, $depth) use (&$out) { if ($depth >= 0) { $out .= str_repeat(' ', $depth).$line."\n"; } }, null, $flags); $dumper->setColors(true); $cloner = new VarCloner(); $dumper->dump($cloner->cloneVar($value)); $this->assertSame($expectedOut, $out); } public function testCollapse() { if ('\\' === \DIRECTORY_SEPARATOR) { $this->markTestSkipped('This test cannot be run on Windows.'); } $stub = new Stub(); $stub->type = Stub::TYPE_OBJECT; $stub->class = 'stdClass'; $stub->position = 1; $data = new Data([ [ $stub, ], [ "\0~collapse=1\0foo" => 123, "\0+\0bar" => [1 => 2], ], [ 'bar' => 123, ], ]); $dumper = new CliDumper(); $dump = $dumper->dump($data, true); $this->assertSame( <<<'EOTXT' { foo: 123 +"bar": array:1 [ "bar" => 123 ] } EOTXT, $dump ); } public function testFileLinkFormat() { $data = new Data([ [ new ClassStub(self::class), ], ]); $ide = $_ENV['SYMFONY_IDE'] ?? null; $_ENV['SYMFONY_IDE'] = 'vscode'; try { $dumper = new CliDumper(); $dumper->setColors(true); $dump = $dumper->dump($data, true); $this->assertStringMatchesFormat('%svscode:%sCliDumperTest%s', $dump); } finally { if (null === $ide) { unset($_ENV['SYMFONY_IDE']); } else { $_ENV['SYMFONY_IDE'] = $ide; } } } } ================================================ FILE: Tests/Dumper/ContextProvider/RequestContextProviderTest.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Dumper\ContextProvider; use PHPUnit\Framework\Attributes\RequiresMethod; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\VarDumper\Cloner\Data; use Symfony\Component\VarDumper\Dumper\ContextProvider\RequestContextProvider; #[RequiresMethod(RequestStack::class, '__construct')] class RequestContextProviderTest extends TestCase { public function testGetContextOnNullRequest() { $requestStack = new RequestStack(); $provider = new RequestContextProvider($requestStack); $this->assertNull($provider->getContext()); } public function testGetContextOnRequest() { $request = Request::create('https://example.org/', 'POST'); $request->attributes->set('_controller', 'MyControllerClass'); $requestStack = new RequestStack(); $requestStack->push($request); $context = (new RequestContextProvider($requestStack))->getContext(); $this->assertSame('https://example.org/', $context['uri']); $this->assertSame('POST', $context['method']); $this->assertInstanceOf(Data::class, $context['controller']); $this->assertSame('MyControllerClass', $context['controller']->getValue()); $this->assertSame('https://example.org/', $context['uri']); $this->assertArrayHasKey('identifier', $context); } } ================================================ FILE: Tests/Dumper/ContextualizedDumperTest.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Tests\Dumper; use PHPUnit\Framework\Attributes\BackupGlobals; use PHPUnit\Framework\TestCase; use Symfony\Component\VarDumper\Cloner\VarCloner; use Symfony\Component\VarDumper\Dumper\CliDumper; use Symfony\Component\VarDumper\Dumper\ContextProvider\SourceContextProvider; use Symfony\Component\VarDumper\Dumper\ContextualizedDumper; /** * @author Kévin Thérage */ #[BackupGlobals(true)] class ContextualizedDumperTest extends TestCase { public function testContextualizedCliDumper() { $_ENV['SYMFONY_IDE'] = $_SERVER['SYMFONY_IDE'] = ''; $wrappedDumper = new CliDumper('php://output'); $wrappedDumper->setColors(true); $var = 'example'; $href = \sprintf('file://%s#L%s', __FILE__, 40); $dumper = new ContextualizedDumper($wrappedDumper, [new SourceContextProvider()]); $cloner = new VarCloner(); $data = $cloner->cloneVar($var); ob_start(); $dumper->dump($data); $out = ob_get_clean(); $this->assertStringContainsString("\e]8;;{$href}\e\\^\e]", $out); $this->assertStringContainsString("m{$var}\e[", $out); } } ================================================ FILE: Tests/Dumper/FunctionsTest.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Tests\Dumper; use PHPUnit\Framework\TestCase; use Symfony\Component\VarDumper\Cloner\VarCloner; use Symfony\Component\VarDumper\Dumper\CliDumper; use Symfony\Component\VarDumper\VarDumper; class FunctionsTest extends TestCase { public function testDumpWithoutArg() { $this->setupVarDumper(); ob_start(); $return = dump(); ob_end_clean(); $this->assertNull($return); } public function testDumpReturnsFirstArg() { $this->setupVarDumper(); $var1 = 'a'; ob_start(); $return = dump($var1); ob_end_clean(); $this->assertSame($var1, $return); } public function testDumpReturnsFirstNamedArgWithoutSectionName() { $this->setupVarDumper(); $var1 = 'a'; ob_start(); $return = dump(first: $var1); ob_end_clean(); $this->assertSame($var1, $return); } public function testDumpReturnsAllArgsInArray() { $this->setupVarDumper(); $var1 = 'a'; $var2 = 'b'; $var3 = 'c'; ob_start(); $return = dump($var1, $var2, $var3); ob_end_clean(); $this->assertSame([$var1, $var2, $var3], $return); } public function testDumpReturnsAllNamedArgsInArray() { $this->setupVarDumper(); $var1 = 'a'; $var2 = 'b'; $var3 = 'c'; ob_start(); $return = dump($var1, second: $var2, third: $var3); ob_end_clean(); $this->assertSame([$var1, 'second' => $var2, 'third' => $var3], $return); } protected function setupVarDumper() { $cloner = new VarCloner(); $dumper = new CliDumper('php://output'); VarDumper::setHandler(static function ($var) use ($cloner, $dumper) { $dumper->dump($cloner->cloneVar($var)); }); } } ================================================ FILE: Tests/Dumper/HtmlDumperTest.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Tests\Dumper; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\VarDumper\Caster\ImgStub; use Symfony\Component\VarDumper\Cloner\VarCloner; use Symfony\Component\VarDumper\Dumper\HtmlDumper; use Symfony\Component\VarDumper\Tests\Fixtures\VirtualProperty; /** * @author Nicolas Grekas */ class HtmlDumperTest extends TestCase { public function testGet() { if (\ini_get('xdebug.file_link_format') || get_cfg_var('xdebug.file_link_format')) { $this->markTestSkipped('A custom file_link_format is defined.'); } require __DIR__.'/../Fixtures/dumb-var.php'; $dumper = new HtmlDumper('php://output'); $dumper->setDumpHeader(''); $dumper->setDumpBoundaries('', ''); $cloner = new VarCloner(); $cloner->addCasters([ ':stream' => static function ($res, $a) { unset($a['uri'], $a['wrapper_data']); return $a; }, ]); $data = $cloner->cloneVar($var); ob_start(); $dumper->dump($data); $out = ob_get_clean(); $out = preg_replace('/[ \t]+$/m', '', $out); $var['file'] = htmlspecialchars($var['file'], \ENT_QUOTES, 'UTF-8'); $intMax = \PHP_INT_MAX; preg_match('/sf-dump-\d+/', $out, $dumpId); $dumpId = $dumpId[0]; $res = (int) $var['res']; $this->assertStringMatchesFormat( <<array:25 [ "number" => 1 0 => &1 null "const" => 1.1 1 => true 2 => false 3 => NAN 4 => INF 5 => -INF 6 => {$intMax} "str" => "d&%s;j&%s;\\n" 7 => b""" é\\x01test\\t\\n ing """ "bo\\u{FEFF}m" => "te\\u{FEFF}st" "[]" => [] "res" => stream resource @{$res} %A wrapper_type: "plainfile" stream_type: "STDIO" mode: "r" unread_bytes: 0 seekable: true %A options: [] } "obj" => Symfony\Component\VarDumper\Tests\Fixture\DumbFoo {#%d +foo: "foo" +"bar": "bar" } "closure" => Closure(\$a, ?PDO &\$b = null) {#%d class: "Symfony\Component\VarDumper\Tests\Dumper\HtmlDumperTest" this: Symfony\Component\VarDumper\Tests\Dumper\HtmlDumperTest {#%d &%s;} file: "%s%eVarDumper%eTests%eFixtures%edumb-var.php" line: "{$var['line']} to {$var['line']}" } "line" => {$var['line']} "nobj" => array:1 [ 0 => &3 {#%d} ] "recurs" => &4 array:1 [ 0 => &4 array:1 [&4] ] 8 => &1 null "sobj" => Symfony\Component\VarDumper\Tests\Fixture\DumbFoo {#%d} "snobj" => &3 {#%d} "snobj2" => {#%d} "file" => "{$var['file']}" b"bin-key-&%s;" => "" ] EOTXT, $out ); } public function testVirtualProperties() { $dumper = new HtmlDumper('php://output'); $dumper->setDumpHeader(''); $dumper->setDumpBoundaries('', ''); $cloner = new VarCloner(); $data = $cloner->cloneVar(new VirtualProperty()); $out = $dumper->dump($data, true); $this->assertStringMatchesFormat(<<Symfony\Component\VarDumper\Tests\Fixtures\VirtualProperty {#%i +firstName: "John" +lastName: "Doe" +fullName: ~ string -noType: ~ } EODUMP, $out ); } public function testCharset() { $var = mb_convert_encoding('Словарь', 'CP1251', 'UTF-8'); $dumper = new HtmlDumper('php://output', 'CP1251'); $dumper->setDumpHeader(''); $dumper->setDumpBoundaries('', ''); $cloner = new VarCloner(); $data = $cloner->cloneVar($var); $out = $dumper->dump($data, true); $this->assertStringMatchesFormat( <<<'EOTXT' b"Словарь" EOTXT, $out ); } public function testAppend() { $out = fopen('php://memory', 'r+'); $dumper = new HtmlDumper(); $dumper->setDumpHeader(''); $dumper->setDumpBoundaries('', ''); $cloner = new VarCloner(); $dumper->dump($cloner->cloneVar(123), $out); $dumper->dump($cloner->cloneVar(456), $out); $out = stream_get_contents($out, -1, 0); $this->assertSame(<<<'EOTXT' 123 456 EOTXT, $out ); } #[DataProvider('varToDumpProvider')] public function testDumpString($var, $needle) { $dumper = new HtmlDumper(); $cloner = new VarCloner(); ob_start(); $dumper->dump($cloner->cloneVar($var)); $out = ob_get_clean(); $this->assertStringContainsString($needle, $out); } public static function varToDumpProvider() { return [ [['dummy' => new ImgStub('dummy', 'img/png', '100em')], ''], ['foo', 'foo'], ]; } } ================================================ FILE: Tests/Dumper/ServerDumperTest.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Tests\Dumper; use PHPUnit\Framework\TestCase; use Symfony\Component\Process\PhpProcess; use Symfony\Component\Process\Process; use Symfony\Component\VarDumper\Cloner\VarCloner; use Symfony\Component\VarDumper\Dumper\ContextProvider\ContextProviderInterface; use Symfony\Component\VarDumper\Dumper\DataDumperInterface; use Symfony\Component\VarDumper\Dumper\ServerDumper; class ServerDumperTest extends TestCase { private const VAR_DUMPER_SERVER = 'tcp://127.0.0.1:9913'; public function testDumpForwardsToWrappedDumperWhenServerIsUnavailable() { $wrappedDumper = $this->createMock(DataDumperInterface::class); $dumper = new ServerDumper(self::VAR_DUMPER_SERVER, $wrappedDumper); $cloner = new VarCloner(); $data = $cloner->cloneVar('foo'); $wrappedDumper->expects($this->once())->method('dump')->with($data); $dumper->dump($data); } public function testDump() { if ('\\' === \DIRECTORY_SEPARATOR) { $this->markTestSkipped('Skip transient test on Windows'); } $wrappedDumper = $this->createMock(DataDumperInterface::class); $wrappedDumper->expects($this->never())->method('dump'); // test wrapped dumper is not used $cloner = new VarCloner(); $data = $cloner->cloneVar('foo'); $dumper = new ServerDumper(self::VAR_DUMPER_SERVER, $wrappedDumper, [ 'foo_provider' => new class implements ContextProviderInterface { public function getContext(): ?array { return ['foo']; } }, ]); $dumped = null; $process = $this->getServerProcess(); $process->start(function ($type, $buffer) use ($process, &$dumped, $dumper, $data) { if (Process::ERR === $type) { $process->stop(); $this->fail(); } elseif ("READY\n" === $buffer) { $dumper->dump($data); } else { $dumped .= $buffer; } }); $process->wait(); $this->assertTrue($process->isSuccessful()); $this->assertStringMatchesFormat(<<<'DUMP' (3) "foo" [ "timestamp" => %d.%d "foo_provider" => [ (3) "foo" ] ] %d DUMP, $dumped ); } private function getServerProcess(): Process { $process = new PhpProcess(file_get_contents(__DIR__.'/../Fixtures/dump_server.php'), null, [ 'COMPONENT_ROOT' => __DIR__.'/../../', 'VAR_DUMPER_SERVER' => self::VAR_DUMPER_SERVER, ]); return $process->setTimeout('\\' === \DIRECTORY_SEPARATOR ? 19 : 9); } } ================================================ FILE: Tests/Dumper/functions/dd_with_accept_header_json_array.phpt ================================================ --TEST-- Test dd() with "Accept: application/json" uses CliDumper with php://output --FILE-- "foo1" 1 => "foo2" ] ================================================ FILE: Tests/Dumper/functions/dd_with_multiple_args.phpt ================================================ --TEST-- Test dd() with multiple args shows line number --FILE-- cloneVar($var); if (null !== $label) { $var = $var->withContext(['label' => $label]); } $dumper->dump($var); }; VarDumper::setHandler($handler); $handler($var, $label); }); $arrayObject = new \ArrayObject(); dump($arrayObject); $arrayObject['X'] = 'A'; $arrayObject['Y'] = new \ArrayObject(['type' => 'object']); $arrayObject['Y']['Z'] = 'B'; $arrayIterator = new \ArrayIterator(); dump($arrayIterator); $arrayIterator['X'] = 'A'; $arrayIterator['Y'] = new \ArrayIterator(['type' => 'object']); $arrayIterator['Y']['Z'] = 'B'; $recursiveArrayIterator = new \RecursiveArrayIterator(); dump($recursiveArrayIterator); $recursiveArrayIterator['X'] = 'A'; $recursiveArrayIterator['Y'] = new \RecursiveArrayIterator(['type' => 'object']); $recursiveArrayIterator['Y']['Z'] = 'B'; --EXPECTF-- %s on line %d: ArrayObject {#%d storage: [] flag::STD_PROP_LIST: false flag::ARRAY_AS_PROPS: false iteratorClass: "ArrayIterator" } %s on line %d: ArrayIterator {#%d storage: [] flag::STD_PROP_LIST: false flag::ARRAY_AS_PROPS: false } %s on line %d: RecursiveArrayIterator {#%d storage: [] flag::STD_PROP_LIST: false flag::ARRAY_AS_PROPS: false } ================================================ FILE: Tests/Dumper/functions/dump_with_accept_header_html.phpt ================================================ --TEST-- Test dump() with "Accept: text/html" uses HTML dumper --FILE-- Test with HTML "foo1" 1 => "foo2" ] ================================================ FILE: Tests/Dumper/functions/dump_with_accept_header_json_multiple_args.phpt ================================================ --TEST-- Test dump() with "Accept: application/json" uses CliDumper with php://output --FILE-- "foo1" 1 => "foo2" ] ================================================ FILE: Tests/Dumper/functions/dump_with_accept_header_json_string.phpt ================================================ --TEST-- Test dump() with "Accept: application/json" uses CliDumper with php://output --FILE-- Test with wildcard * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Tests\Fixtures; #[MyAttribute] final class LotsOfAttributes { #[RepeatableAttribute('one'), RepeatableAttribute('two')] public const SOME_CONSTANT = 'some value'; #[MyAttribute('one', extra: 'hello')] private string $someProperty; #[MyAttribute('two')] public function someMethod( #[MyAttribute('three')] string $someParameter, ): void { } } ================================================ FILE: Tests/Fixtures/MyAttribute.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Tests\Fixtures; #[\Attribute] final class MyAttribute { public function __construct( private string $foo = 'default', private ?string $extra = null, ) { } public function getFoo(): string { return $this->foo; } public function getExtra(): ?string { return $this->extra; } } ================================================ FILE: Tests/Fixtures/NotLoadableClass.php ================================================ p2 = new \stdClass(); $this->p3 = &$this->p2; } } ================================================ FILE: Tests/Fixtures/Php81Enums.php ================================================ e1 = UnitEnumFixture::Hearts; $this->e2 = BackedEnumFixture::Diamonds; } } ================================================ FILE: Tests/Fixtures/Php82NullStandaloneReturnType.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Tests\Fixtures; class Php82NullStandaloneReturnType { public function foo(null $bar): null { return null; } } ================================================ FILE: Tests/Fixtures/ReflectionIntersectionTypeFixture.php ================================================ createError(); } } /* foo.twig */ class __TwigTemplate_VarDumperFixture_u75a09 extends AbstractTwigTemplate { private $path; public function __construct(?Twig\Environment $env = null, $path = null) { if (null !== $env) { parent::__construct($env); } $this->parent = false; $this->blocks = []; $this->path = $path; } protected function doDisplay(array $context, array $blocks = []): array { // line 2 throw new \Exception('Foobar'); } public function getTemplateName(): string { return 'foo.twig'; } public function getDebugInfo(): array { return [33 => 1, 34 => 2]; } public function getSourceContext(): Twig\Source { return new Twig\Source(" foo bar\n twig source\n\n", 'foo.twig', $this->path ?: __FILE__); } } ================================================ FILE: Tests/Fixtures/UnitEnumFixture.php ================================================ firstName.' '.$this->lastName; } } private $noType { get { return null; } } } ================================================ FILE: Tests/Fixtures/dumb-var.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Tests\Fixture; if (!class_exists(\Symfony\Component\VarDumper\Tests\Fixture\DumbFoo::class)) { #[\AllowDynamicProperties] class DumbFoo { public $foo = 'foo'; } } $foo = new DumbFoo(); $foo->bar = 'bar'; $g = fopen(__FILE__, 'r'); $var = [ 'number' => 1, null, 'const' => 1.1, true, false, \NAN, \INF, -\INF, \PHP_INT_MAX, 'str' => "déjà\n", "\xE9\x01test\t\ning", "bo\u{feff}m" => "te\u{feff}st", '[]' => [], 'res' => $g, 'obj' => $foo, 'closure' => function ($a, ?\PDO &$b = null) {}, 'line' => __LINE__ - 1, 'nobj' => [(object) []], ]; $r = []; $r[] = &$r; $var['recurs'] = &$r; $var[] = &$var[0]; $var['sobj'] = $var['obj']; $var['snobj'] = &$var['nobj'][0]; $var['snobj2'] = $var['nobj'][0]; $var['file'] = __FILE__; $var["bin-key-\xE9"] = ''; unset($g, $r); ================================================ FILE: Tests/Fixtures/dump_server.php ================================================ setMaxItems(-1); $dumper = new CliDumper(null, null, CliDumper::DUMP_LIGHT_ARRAY | CliDumper::DUMP_STRING_LENGTH); $dumper->setColors(false); VarDumper::setHandler(function ($var) use ($cloner, $dumper) { $data = $cloner->cloneVar($var)->withRefHandles(false); $dumper->dump($data); }); $server = new DumpServer(getenv('VAR_DUMPER_SERVER')); $server->start(); echo "READY\n"; $server->listen(function (Data $data, array $context, $clientId) { dump((string) $data, $context, $clientId); exit(0); }); ================================================ FILE: Tests/Fixtures/xml_reader.xml ================================================ With text ================================================ FILE: Tests/Server/ConnectionTest.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Tests\Server; use PHPUnit\Framework\TestCase; use Symfony\Component\Process\PhpProcess; use Symfony\Component\Process\Process; use Symfony\Component\VarDumper\Cloner\VarCloner; use Symfony\Component\VarDumper\Dumper\ContextProvider\ContextProviderInterface; use Symfony\Component\VarDumper\Server\Connection; class ConnectionTest extends TestCase { private const VAR_DUMPER_SERVER = 'tcp://127.0.0.1:9913'; public function testDump() { if ('\\' === \DIRECTORY_SEPARATOR) { $this->markTestSkipped('Skip transient test on Windows'); } $cloner = new VarCloner(); $data = $cloner->cloneVar('foo'); $connection = new Connection(self::VAR_DUMPER_SERVER, [ 'foo_provider' => new class implements ContextProviderInterface { public function getContext(): ?array { return ['foo']; } }, ]); $dumped = null; $process = $this->getServerProcess(); $process->start(function ($type, $buffer) use ($process, &$dumped, $connection, $data) { if (Process::ERR === $type) { $process->stop(); $this->fail(); } elseif ("READY\n" === $buffer) { $connection->write($data); } else { $dumped .= $buffer; } }); $process->wait(); $this->assertTrue($process->isSuccessful()); $this->assertStringMatchesFormat(<<<'DUMP' (3) "foo" [ "timestamp" => %d.%d "foo_provider" => [ (3) "foo" ] ] %d DUMP, $dumped ); } public function testNoServer() { $cloner = new VarCloner(); $data = $cloner->cloneVar('foo'); $connection = new Connection(self::VAR_DUMPER_SERVER); $start = microtime(true); $this->assertFalse($connection->write($data)); $this->assertLessThan(4, microtime(true) - $start); } private function getServerProcess(): Process { $process = new PhpProcess(file_get_contents(__DIR__.'/../Fixtures/dump_server.php'), null, [ 'COMPONENT_ROOT' => __DIR__.'/../../', 'VAR_DUMPER_SERVER' => self::VAR_DUMPER_SERVER, ]); return $process->setTimeout(9); } } ================================================ FILE: Tests/Test/VarDumperTestTraitTest.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Tests\Test; use PHPUnit\Framework\TestCase; use Symfony\Component\VarDumper\Cloner\Stub; use Symfony\Component\VarDumper\Dumper\CliDumper; use Symfony\Component\VarDumper\Test\VarDumperTestTrait; class VarDumperTestTraitTest extends TestCase { use VarDumperTestTrait; public function testItComparesLargeData() { $howMany = 700; $data = array_fill_keys(range(0, $howMany), ['a', 'b', 'c', 'd']); $expected = \sprintf("array:%d [\n", $howMany + 1); for ($i = 0; $i <= $howMany; ++$i) { $expected .= << array:4 [ 0 => "a" 1 => "b" 2 => "c" 3 => "d" ]\n EODUMP; } $expected .= "]\n"; $this->assertDumpEquals($expected, $data); } public function testAllowsNonScalarExpectation() { $this->assertDumpEquals(new \ArrayObject(['bim' => 'bam']), new \ArrayObject(['bim' => 'bam'])); } public function testItCanBeConfigured() { $this->setUpVarDumper($casters = [ \DateTimeInterface::class => static function (\DateTimeInterface $date, array $a, Stub $stub): array { $stub->class = 'DateTimeImmutable'; return ['date' => $date->format('d/m/Y')]; }, ], CliDumper::DUMP_LIGHT_ARRAY | CliDumper::DUMP_COMMA_SEPARATOR); $this->assertSame(CliDumper::DUMP_LIGHT_ARRAY | CliDumper::DUMP_COMMA_SEPARATOR, $this->varDumperConfig['flags']); $this->assertSame($casters, $this->varDumperConfig['casters']); $this->assertDumpEquals(<<tearDownVarDumper(); $this->assertNull($this->varDumperConfig['flags']); $this->assertSame([], $this->varDumperConfig['casters']); } } ================================================ FILE: VarDumper.php ================================================ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper; use Symfony\Component\ErrorHandler\ErrorRenderer\FileLinkFormatter; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\VarDumper\Caster\ReflectionCaster; use Symfony\Component\VarDumper\Cloner\VarCloner; use Symfony\Component\VarDumper\Dumper\CliDumper; use Symfony\Component\VarDumper\Dumper\ContextProvider\CliContextProvider; use Symfony\Component\VarDumper\Dumper\ContextProvider\RequestContextProvider; use Symfony\Component\VarDumper\Dumper\ContextProvider\SourceContextProvider; use Symfony\Component\VarDumper\Dumper\ContextualizedDumper; use Symfony\Component\VarDumper\Dumper\DataDumperInterface; use Symfony\Component\VarDumper\Dumper\HtmlDumper; use Symfony\Component\VarDumper\Dumper\ServerDumper; // Load the global dump() function require_once __DIR__.'/Resources/functions/dump.php'; /** * @author Nicolas Grekas */ class VarDumper { /** * @var callable|null */ private static $handler; public static function dump(mixed $var, ?string $label = null): mixed { if (null === self::$handler) { self::register(); } return (self::$handler)($var, $label); } public static function setHandler(?callable $callable): ?callable { $prevHandler = self::$handler; // Prevent replacing the handler with expected format as soon as the env var was set: if (isset($_SERVER['VAR_DUMPER_FORMAT'])) { return $prevHandler; } self::$handler = $callable; return $prevHandler; } private static function register(): void { $cloner = new VarCloner(); $cloner->addCasters(ReflectionCaster::UNSET_CLOSURE_FILE_INFO); $format = $_SERVER['VAR_DUMPER_FORMAT'] ?? null; $dumper = match ($format) { 'html' => new HtmlDumper(), 'cli' => new CliDumper(), 'server' => self::selectDumperForAccept($_SERVER['VAR_DUMPER_SERVER'] ?? '127.0.0.1:9912'), default => self::selectDumperForAccept( $format && 'tcp' === parse_url($format, \PHP_URL_SCHEME) ? $format : null, ), }; if (!$dumper instanceof ServerDumper) { $dumper = new ContextualizedDumper($dumper, [new SourceContextProvider()]); } self::$handler = static 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 selectDumperForAccept(?string $serverHost): DataDumperInterface { $isCliSapi = \in_array(\PHP_SAPI, ['cli', 'phpdbg', 'embed'], true); $accept = $_SERVER['HTTP_ACCEPT'] ?? ($isCliSapi ? 'txt' : 'html'); $dumper = match (true) { str_contains($accept, 'html'), str_contains($accept, '*/*') => new HtmlDumper(), $isCliSapi => new CliDumper(), default => new CliDumper('php://output'), }; if (null !== $serverHost) { $dumper = new ServerDumper($serverHost, $dumper, self::getDefaultContextProviders()); } return $dumper; } private static function getDefaultContextProviders(): array { $contextProviders = []; if (!\in_array(\PHP_SAPI, ['cli', 'phpdbg', 'embed'], true) && class_exists(Request::class)) { $requestStack = new RequestStack(); $requestStack->push(Request::createFromGlobals()); $contextProviders['request'] = new RequestContextProvider($requestStack); } $fileLinkFormatter = class_exists(FileLinkFormatter::class) ? new FileLinkFormatter(null, $requestStack ?? null) : null; return $contextProviders + [ 'cli' => new CliContextProvider(), 'source' => new SourceContextProvider(null, null, $fileLinkFormatter), ]; } } ================================================ FILE: composer.json ================================================ { "name": "symfony/var-dumper", "type": "library", "description": "Provides mechanisms for walking through any arbitrary PHP variable", "keywords": ["dump", "debug"], "homepage": "https://symfony.com", "license": "MIT", "authors": [ { "name": "Nicolas Grekas", "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "require": { "php": ">=8.4", "symfony/polyfill-mbstring": "^1.0" }, "require-dev": { "symfony/console": "^7.4|^8.0", "symfony/http-kernel": "^7.4|^8.0", "symfony/process": "^7.4|^8.0", "symfony/uid": "^7.4|^8.0", "twig/twig": "^3.12" }, "conflict": { "symfony/console": "<7.4", "symfony/error-handler": "<7.4" }, "autoload": { "files": [ "Resources/functions/dump.php" ], "psr-4": { "Symfony\\Component\\VarDumper\\": "" }, "exclude-from-classmap": [ "/Tests/" ] }, "bin": [ "Resources/bin/var-dump-server" ], "minimum-stability": "dev" } ================================================ FILE: phpunit.xml.dist ================================================ ./Tests/ ./Tests/Dumper/functions/ ./ ./Resources ./Tests ./vendor