yiisoft /
var-dumper
| 1 | <?php |
||
| 2 | |||
| 3 | declare(strict_types=1); |
||
| 4 | |||
| 5 | namespace Yiisoft\VarDumper; |
||
| 6 | |||
| 7 | use __PHP_Incomplete_Class; |
||
| 8 | use Closure; |
||
| 9 | use DateTimeInterface; |
||
| 10 | use Exception; |
||
| 11 | use IteratorAggregate; |
||
| 12 | use JsonException; |
||
| 13 | use JsonSerializable; |
||
| 14 | use ReflectionException; |
||
| 15 | use ReflectionObject; |
||
| 16 | use Yiisoft\Arrays\ArrayableInterface; |
||
| 17 | use Yiisoft\VarDumper\Handler\EchoHandler; |
||
| 18 | |||
| 19 | use function array_keys; |
||
| 20 | use function gettype; |
||
| 21 | use function method_exists; |
||
| 22 | use function next; |
||
| 23 | use function spl_object_id; |
||
| 24 | use function str_repeat; |
||
| 25 | use function strtr; |
||
| 26 | use function trim; |
||
| 27 | use function var_export; |
||
| 28 | |||
| 29 | /** |
||
| 30 | * VarDumper provides enhanced versions of the PHP functions {@see var_dump()} and {@see var_export()}. |
||
| 31 | * It can: |
||
| 32 | * |
||
| 33 | * - Correctly identify the recursively referenced objects in a complex object structure. |
||
| 34 | * - Recursively control depth to avoid indefinite recursive display of some peculiar variables. |
||
| 35 | * - Export closures and objects. |
||
| 36 | * - Highlight output. |
||
| 37 | * - Format output. |
||
| 38 | */ |
||
| 39 | final class VarDumper |
||
| 40 | { |
||
| 41 | public const VAR_TYPE_PROPERTY = '$__type__$'; |
||
| 42 | public const OBJECT_ID_PROPERTY = '$__id__$'; |
||
| 43 | public const OBJECT_CLASS_PROPERTY = '$__class__$'; |
||
| 44 | public const DEPTH_LIMIT_EXCEEDED_PROPERTY = '$__depth_limit_exceeded__$'; |
||
| 45 | |||
| 46 | public const VAR_TYPE_ARRAY = 'array'; |
||
| 47 | public const VAR_TYPE_OBJECT = 'object'; |
||
| 48 | public const VAR_TYPE_RESOURCE = 'resource'; |
||
| 49 | private static ?HandlerInterface $defaultHandler = null; |
||
| 50 | |||
| 51 | /** |
||
| 52 | * @var string[] Variables using in closure scope. |
||
| 53 | */ |
||
| 54 | private array $useVarInClosures = []; |
||
| 55 | private bool $serializeObjects = true; |
||
| 56 | /** |
||
| 57 | * @var string Offset to use to indicate nesting level. |
||
| 58 | */ |
||
| 59 | private string $offset = ' '; |
||
| 60 | private static ?ClosureExporter $closureExporter = null; |
||
| 61 | |||
| 62 | /** |
||
| 63 | * @param mixed $variable Variable to dump. |
||
| 64 | */ |
||
| 65 | 149 | private function __construct(private mixed $variable) |
|
| 66 | { |
||
| 67 | 149 | } |
|
| 68 | |||
| 69 | public static function setDefaultHandler(HandlerInterface $handler): void |
||
| 70 | { |
||
| 71 | self::$defaultHandler = $handler; |
||
| 72 | } |
||
| 73 | |||
| 74 | 6 | public static function getDefaultHandler(): HandlerInterface |
|
| 75 | { |
||
| 76 | 6 | return self::$defaultHandler ??= new EchoHandler(); |
|
| 77 | } |
||
| 78 | |||
| 79 | /** |
||
| 80 | * @param mixed $variable Variable to dump. |
||
| 81 | * |
||
| 82 | * @return static An instance containing variable to dump. |
||
| 83 | */ |
||
| 84 | 149 | public static function create(mixed $variable): self |
|
| 85 | { |
||
| 86 | 149 | return new self($variable); |
|
| 87 | } |
||
| 88 | |||
| 89 | /** |
||
| 90 | * Prints a variable. |
||
| 91 | * |
||
| 92 | * This method achieves the similar functionality as {@see var_dump()} and {@see print_r()} |
||
| 93 | * but is more robust when handling complex objects. |
||
| 94 | * |
||
| 95 | * @param mixed $variable Variable to be dumped. |
||
| 96 | * @param int $depth Maximum depth that the dumper should go into the variable. Defaults to 10. |
||
| 97 | * @param bool $highlight Whether the result should be syntax-highlighted. |
||
| 98 | * |
||
| 99 | * @throws ReflectionException |
||
| 100 | */ |
||
| 101 | 6 | public static function dump(mixed $variable, int $depth = 10, bool $highlight = true): void |
|
| 102 | { |
||
| 103 | 6 | self::getDefaultHandler()->handle($variable, $depth, $highlight); |
|
| 104 | } |
||
| 105 | |||
| 106 | /** |
||
| 107 | * Sets offset string to use to indicate nesting level. |
||
| 108 | * |
||
| 109 | * @param string $offset The offset string. |
||
| 110 | * |
||
| 111 | * @return static New instance with a given offset. |
||
| 112 | */ |
||
| 113 | public function withOffset(string $offset): self |
||
| 114 | { |
||
| 115 | $new = clone $this; |
||
| 116 | $new->offset = $offset; |
||
| 117 | |||
| 118 | return $new; |
||
| 119 | } |
||
| 120 | |||
| 121 | /** |
||
| 122 | * Dumps a variable in terms of a string. |
||
| 123 | * |
||
| 124 | * This method achieves the similar functionality as {@see var_dump()} and {@see print_r()} |
||
| 125 | * but is more robust when handling complex objects. |
||
| 126 | * |
||
| 127 | * @param int $depth Maximum depth that the dumper should go into the variable. Defaults to 10. |
||
| 128 | * @param bool $highlight Whether the result should be syntax-highlighted. |
||
| 129 | * |
||
| 130 | * @throws ReflectionException |
||
| 131 | * |
||
| 132 | * @return string The string representation of the variable. |
||
| 133 | */ |
||
| 134 | 35 | public function asString(int $depth = 10): string |
|
| 135 | { |
||
| 136 | 35 | return $this->dumpInternal($this->variable, true, $depth, 0); |
|
| 137 | } |
||
| 138 | |||
| 139 | /** |
||
| 140 | * Dumps a variable as a JSON string. |
||
| 141 | * |
||
| 142 | * This method provides similar functionality to the {@see json_encode()} |
||
| 143 | * but is more robust when handling complex objects. |
||
| 144 | * |
||
| 145 | * @param bool $format Use whitespaces in returned data to format it |
||
| 146 | * @param int $depth Maximum depth that the dumper should go into the variable. Defaults to 10. |
||
| 147 | * |
||
| 148 | * @throws JsonException |
||
| 149 | * |
||
| 150 | * @return string The json representation of the variable. |
||
| 151 | */ |
||
| 152 | 30 | public function asJson(bool $format = true, int $depth = 10): string |
|
| 153 | { |
||
| 154 | /** @var mixed $output */ |
||
| 155 | 30 | $output = $this->asPrimitives($depth); |
|
| 156 | |||
| 157 | 30 | if ($format) { |
|
| 158 | 30 | return json_encode($output, JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT); |
|
| 159 | } |
||
| 160 | |||
| 161 | return json_encode($output, JSON_THROW_ON_ERROR); |
||
| 162 | } |
||
| 163 | |||
| 164 | /** |
||
| 165 | * Dumps the variable as PHP primitives in JSON decoded style. |
||
| 166 | * |
||
| 167 | * @param int $depth Maximum depth that the dumper should go into the variable. Defaults to 10. |
||
| 168 | * |
||
| 169 | * @throws ReflectionException |
||
| 170 | * |
||
| 171 | * @return mixed |
||
| 172 | */ |
||
| 173 | 60 | public function asPrimitives(int $depth = 10): mixed |
|
| 174 | { |
||
| 175 | 60 | return $this->exportPrimitives($this->variable, $depth, 0); |
|
| 176 | } |
||
| 177 | |||
| 178 | /** |
||
| 179 | * Exports a variable as a string containing PHP code. |
||
| 180 | * |
||
| 181 | * The string is a valid PHP expression that can be evaluated by PHP parser |
||
| 182 | * and the evaluation result will give back the variable value. |
||
| 183 | * |
||
| 184 | * This method is similar to {@see var_export()}. The main difference is that |
||
| 185 | * it generates more compact string representation using short array syntax. |
||
| 186 | * |
||
| 187 | * It also handles closures with {@see ClosureExporter} and objects |
||
| 188 | * by using the PHP functions {@see serialize()} and {@see unserialize()}. |
||
| 189 | * |
||
| 190 | * @param bool $format Whatever to format code. |
||
| 191 | * @param string[] $useVariables Array of variables used in `use` statement (['$params', '$config']) |
||
| 192 | * @param bool $serializeObjects If it is true all objects will be serialized except objects with closure(s). If it |
||
| 193 | * is false only objects of internal classes will be serialized. |
||
| 194 | * |
||
| 195 | * @throws ReflectionException |
||
| 196 | * |
||
| 197 | * @return string A PHP code representation of the variable. |
||
| 198 | */ |
||
| 199 | 56 | public function export(bool $format = true, array $useVariables = [], bool $serializeObjects = true): string |
|
| 200 | { |
||
| 201 | 56 | $this->useVarInClosures = $useVariables; |
|
| 202 | 56 | $this->serializeObjects = $serializeObjects; |
|
| 203 | 56 | return $this->exportInternal($this->variable, $format, 0); |
|
| 204 | } |
||
| 205 | |||
| 206 | /** |
||
| 207 | * @param mixed $var Variable to be dumped. |
||
| 208 | * @param bool $format Whatever to format code. |
||
| 209 | * @param int $depth Maximum depth. |
||
| 210 | * @param int $level Current depth. |
||
| 211 | * |
||
| 212 | * @throws ReflectionException |
||
| 213 | * |
||
| 214 | * @return string |
||
| 215 | */ |
||
| 216 | 35 | private function dumpInternal(mixed $var, bool $format, int $depth, int $level): string |
|
| 217 | { |
||
| 218 | 35 | switch (gettype($var)) { |
|
| 219 | 35 | case 'resource': |
|
| 220 | 34 | case 'resource (closed)': |
|
| 221 | 1 | return '{resource}'; |
|
| 222 | 34 | case 'NULL': |
|
| 223 | 1 | return 'null'; |
|
| 224 | 33 | case 'array': |
|
| 225 | 6 | if ($depth <= $level) { |
|
| 226 | 1 | return '[...]'; |
|
| 227 | } |
||
| 228 | |||
| 229 | 5 | if (empty($var)) { |
|
| 230 | 2 | return '[]'; |
|
| 231 | } |
||
| 232 | |||
| 233 | 3 | $output = ''; |
|
| 234 | 3 | $keys = array_keys($var); |
|
| 235 | 3 | $spaces = str_repeat($this->offset, $level); |
|
| 236 | 3 | $output .= '['; |
|
| 237 | |||
| 238 | 3 | foreach ($keys as $name) { |
|
| 239 | 3 | if ($format) { |
|
| 240 | 3 | $output .= "\n" . $spaces . $this->offset; |
|
| 241 | } |
||
| 242 | 3 | $output .= $this->exportVariable($name); |
|
| 243 | 3 | $output .= ' => '; |
|
| 244 | 3 | $output .= $this->dumpInternal($var[$name], $format, $depth, $level + 1); |
|
| 245 | } |
||
| 246 | |||
| 247 | 3 | return $format |
|
| 248 | 3 | ? $output . "\n" . $spaces . ']' |
|
| 249 | 3 | : $output . ']'; |
|
| 250 | 31 | case 'object': |
|
| 251 | 17 | if ($var instanceof Closure) { |
|
| 252 | 11 | return $this->exportClosure($var); |
|
| 253 | } |
||
| 254 | 8 | if ($var instanceof DateTimeInterface) { |
|
| 255 | 1 | return $this->exportDateTime($var); |
|
| 256 | } |
||
| 257 | |||
| 258 | 7 | if ($depth <= $level) { |
|
| 259 | 1 | return $this->getObjectDescription($var) . ' (...)'; |
|
| 260 | } |
||
| 261 | |||
| 262 | 6 | $spaces = str_repeat($this->offset, $level); |
|
| 263 | 6 | $output = $this->getObjectDescription($var) . "\n" . $spaces . '('; |
|
| 264 | 6 | $objectProperties = $this->getObjectProperties($var); |
|
| 265 | |||
| 266 | /** @psalm-var mixed $value */ |
||
| 267 | 6 | foreach ($objectProperties as $name => $value) { |
|
| 268 | 4 | $propertyName = strtr(trim((string) $name), "\0", '::'); |
|
| 269 | 4 | $output .= "\n" . $spaces . $this->offset . '[' . $propertyName . '] => '; |
|
| 270 | 4 | $output .= $this->dumpInternal($value, $format, $depth, $level + 1); |
|
| 271 | } |
||
| 272 | 6 | return $output . "\n" . $spaces . ')'; |
|
| 273 | default: |
||
| 274 | 16 | return $this->exportVariable($var); |
|
| 275 | } |
||
| 276 | } |
||
| 277 | |||
| 278 | /** |
||
| 279 | * @param mixed $variable Variable to be exported. |
||
| 280 | * @param bool $format Whatever to format code. |
||
| 281 | * @param int $level Current depth. |
||
| 282 | * |
||
| 283 | * @throws ReflectionException |
||
| 284 | * |
||
| 285 | * @return string |
||
| 286 | */ |
||
| 287 | 56 | private function exportInternal(mixed $variable, bool $format, int $level): string |
|
| 288 | { |
||
| 289 | 56 | $spaces = str_repeat($this->offset, $level); |
|
| 290 | 56 | switch (gettype($variable)) { |
|
| 291 | 56 | case 'NULL': |
|
| 292 | 2 | return 'null'; |
|
| 293 | 54 | case 'array': |
|
| 294 | 9 | if (empty($variable)) { |
|
| 295 | 2 | return '[]'; |
|
| 296 | } |
||
| 297 | |||
| 298 | 7 | $keys = array_keys($variable); |
|
| 299 | 7 | $outputKeys = $keys !== array_keys($keys); |
|
| 300 | 7 | $output = '['; |
|
| 301 | |||
| 302 | 7 | foreach ($keys as $key) { |
|
| 303 | 7 | if ($format) { |
|
| 304 | 4 | $output .= "\n" . $spaces . $this->offset; |
|
| 305 | } |
||
| 306 | 7 | if ($outputKeys) { |
|
| 307 | 3 | $output .= $this->exportVariable($key); |
|
| 308 | 3 | $output .= ' => '; |
|
| 309 | } |
||
| 310 | 7 | $output .= $this->exportInternal($variable[$key], $format, $level + 1); |
|
| 311 | 7 | if ($format || next($keys) !== false) { |
|
| 312 | 6 | $output .= ','; |
|
| 313 | } |
||
| 314 | } |
||
| 315 | |||
| 316 | 7 | return $format |
|
| 317 | 4 | ? $output . "\n" . $spaces . ']' |
|
| 318 | 7 | : $output . ']'; |
|
| 319 | 52 | case 'object': |
|
| 320 | 35 | if ($variable instanceof Closure) { |
|
| 321 | 25 | return $this->exportClosure($variable, $level); |
|
| 322 | } |
||
| 323 | 15 | if ($variable instanceof DateTimeInterface) { |
|
| 324 | 4 | return $this->exportDateTime($variable); |
|
| 325 | } |
||
| 326 | |||
| 327 | |||
| 328 | 11 | $reflectionObject = new ReflectionObject($variable); |
|
| 329 | try { |
||
| 330 | 11 | if ($this->serializeObjects || $reflectionObject->isInternal() || $reflectionObject->isAnonymous()) { |
|
| 331 | 9 | return "unserialize({$this->exportVariable(serialize($variable))})"; |
|
| 332 | } |
||
| 333 | |||
| 334 | 2 | return $this->exportObject($variable, $reflectionObject, $format, $level); |
|
| 335 | 6 | } catch (Exception) { |
|
| 336 | // Serialize may fail, for example: if object contains a `\Closure` instance so we use a fallback. |
||
| 337 | 6 | if ($this->serializeObjects && !$reflectionObject->isInternal() && !$reflectionObject->isAnonymous()) { |
|
| 338 | try { |
||
| 339 | 4 | return $this->exportObject($variable, $reflectionObject, $format, $level); |
|
| 340 | } catch (Exception) { |
||
| 341 | return $this->exportObjectFallback($variable, $format, $level); |
||
| 342 | } |
||
| 343 | } |
||
| 344 | |||
| 345 | 2 | return $this->exportObjectFallback($variable, $format, $level); |
|
| 346 | } |
||
| 347 | default: |
||
| 348 | 19 | return $this->exportVariable($variable); |
|
| 349 | } |
||
| 350 | } |
||
| 351 | |||
| 352 | /** |
||
| 353 | * @throws ReflectionException |
||
| 354 | * |
||
| 355 | * @return mixed |
||
| 356 | * @psalm-param mixed $var |
||
| 357 | */ |
||
| 358 | 60 | private function exportPrimitives(mixed $var, int $depth, int $level): mixed |
|
| 359 | { |
||
| 360 | 60 | switch (gettype($var)) { |
|
| 361 | 60 | case 'resource': |
|
| 362 | 58 | case 'resource (closed)': |
|
| 363 | /** @var resource $var */ |
||
| 364 | 4 | $id = get_resource_id($var); |
|
| 365 | 4 | $type = get_resource_type($var); |
|
| 366 | |||
| 367 | 4 | return [ |
|
| 368 | 4 | self::VAR_TYPE_PROPERTY => self::VAR_TYPE_RESOURCE, |
|
| 369 | 4 | 'id' => $id, |
|
| 370 | 4 | 'type' => $type, |
|
| 371 | 4 | 'closed' => !is_resource($var), |
|
| 372 | 4 | ]; |
|
| 373 | 56 | case 'array': |
|
| 374 | 12 | if ($depth <= $level) { |
|
| 375 | 2 | return [ |
|
| 376 | 2 | self::VAR_TYPE_PROPERTY => self::VAR_TYPE_ARRAY, |
|
| 377 | 2 | self::DEPTH_LIMIT_EXCEEDED_PROPERTY => true, |
|
| 378 | 2 | ]; |
|
| 379 | } |
||
| 380 | |||
| 381 | /** @psalm-suppress MissingClosureReturnType */ |
||
| 382 | 12 | return array_map(fn ($value) => $this->exportPrimitives($value, $depth, $level + 1), $var); |
|
| 383 | 52 | case 'object': |
|
| 384 | 34 | if ($var instanceof Closure) { |
|
| 385 | 20 | return $this->exportClosure($var); |
|
| 386 | } |
||
| 387 | |||
| 388 | 16 | $objectClass = $var::class; |
|
| 389 | 16 | $objectId = $this->getObjectId($var); |
|
| 390 | 16 | if ($depth <= $level) { |
|
| 391 | 2 | return [ |
|
| 392 | 2 | self::VAR_TYPE_PROPERTY => self::VAR_TYPE_OBJECT, |
|
| 393 | 2 | self::OBJECT_ID_PROPERTY => $objectId, |
|
| 394 | 2 | self::OBJECT_CLASS_PROPERTY => $objectClass, |
|
| 395 | 2 | self::DEPTH_LIMIT_EXCEEDED_PROPERTY => true, |
|
| 396 | 2 | ]; |
|
| 397 | } |
||
| 398 | |||
| 399 | 16 | $objectProperties = $this->getObjectProperties($var); |
|
| 400 | |||
| 401 | 16 | $output = [ |
|
| 402 | 16 | self::OBJECT_ID_PROPERTY => $objectId, |
|
| 403 | 16 | self::OBJECT_CLASS_PROPERTY => $objectClass, |
|
| 404 | 16 | ]; |
|
| 405 | /** |
||
| 406 | * @psalm-var mixed $value |
||
| 407 | * @psalm-var string $name |
||
| 408 | */ |
||
| 409 | 16 | foreach ($objectProperties as $name => $value) { |
|
| 410 | 14 | $propertyName = $this->getPropertyName($name); |
|
| 411 | /** @psalm-suppress MixedAssignment */ |
||
| 412 | 14 | $output[$propertyName] = $this->exportPrimitives($value, $depth, $level + 1); |
|
| 413 | } |
||
| 414 | 16 | return $output; |
|
| 415 | default: |
||
| 416 | 28 | return $var; |
|
| 417 | } |
||
| 418 | } |
||
| 419 | |||
| 420 | 20 | private function getPropertyName(string|int $property): string |
|
| 421 | { |
||
| 422 | 20 | if (is_int($property)) { |
|
| 423 | 2 | return (string) $property; |
|
| 424 | } |
||
| 425 | 18 | $property = str_replace("\0", '::', trim($property)); |
|
| 426 | |||
| 427 | 18 | if (str_starts_with($property, '*::')) { |
|
| 428 | return substr($property, 3); |
||
| 429 | } |
||
| 430 | |||
| 431 | 18 | if (($pos = strpos($property, '::')) !== false) { |
|
| 432 | 6 | return substr($property, $pos + 2); |
|
| 433 | } |
||
| 434 | |||
| 435 | 12 | return $property; |
|
| 436 | } |
||
| 437 | |||
| 438 | /** |
||
| 439 | * @throws ReflectionException |
||
| 440 | * |
||
| 441 | * @return string |
||
| 442 | */ |
||
| 443 | 2 | private function exportObjectFallback(object $variable, bool $format, int $level): string |
|
| 444 | { |
||
| 445 | 2 | if ($variable instanceof ArrayableInterface) { |
|
| 446 | return $this->exportInternal($variable->toArray(), $format, $level); |
||
| 447 | } |
||
| 448 | |||
| 449 | 2 | if ($variable instanceof JsonSerializable) { |
|
| 450 | return $this->exportInternal($variable->jsonSerialize(), $format, $level); |
||
| 451 | } |
||
| 452 | |||
| 453 | 2 | if ($variable instanceof IteratorAggregate) { |
|
| 454 | return $this->exportInternal(iterator_to_array($variable), $format, $level); |
||
| 455 | } |
||
| 456 | |||
| 457 | /** @psalm-suppress RedundantCondition */ |
||
| 458 | 2 | if ('__PHP_Incomplete_Class' !== $variable::class && method_exists($variable, '__toString')) { |
|
| 459 | return $this->exportVariable($variable->__toString()); |
||
| 460 | } |
||
| 461 | |||
| 462 | 2 | return $this->exportVariable(self::create($variable)->asString()); |
|
| 463 | } |
||
| 464 | |||
| 465 | 6 | private function exportObject(object $variable, ReflectionObject $reflectionObject, bool $format, int $level): string |
|
| 466 | { |
||
| 467 | 6 | $spaces = str_repeat($this->offset, $level); |
|
| 468 | 6 | $objectProperties = $this->getObjectProperties($variable); |
|
| 469 | 6 | $class = $variable::class; |
|
| 470 | 6 | $use = $this->useVarInClosures === [] ? '' : ' use (' . implode(', ', $this->useVarInClosures) . ')'; |
|
| 471 | 6 | $lines = ['(static function ()' . $use . ' {',]; |
|
| 472 | 6 | if ($reflectionObject->getConstructor() === null) { |
|
| 473 | 2 | $lines = [ |
|
| 474 | 2 | ...$lines, |
|
| 475 | 2 | $this->offset . '$object = new ' . $class . '();', |
|
| 476 | 2 | $this->offset . '(function ()' . $use . ' {', |
|
| 477 | 2 | ]; |
|
| 478 | } else { |
||
| 479 | 4 | $lines = [ |
|
| 480 | 4 | ...$lines, |
|
| 481 | 4 | $this->offset . '$class = new \ReflectionClass(\'' . $class . '\');', |
|
| 482 | 4 | $this->offset . '$object = $class->newInstanceWithoutConstructor();', |
|
| 483 | 4 | $this->offset . '(function ()' . $use . ' {', |
|
| 484 | 4 | ]; |
|
| 485 | } |
||
| 486 | 6 | $endLines = [ |
|
| 487 | 6 | $this->offset . '})->bindTo($object, \'' . $class . '\')();', |
|
| 488 | 6 | '', |
|
| 489 | 6 | $this->offset . 'return $object;', |
|
| 490 | 6 | '})()', |
|
| 491 | 6 | ]; |
|
| 492 | |||
| 493 | /** |
||
| 494 | * @psalm-var mixed $value |
||
| 495 | * @psalm-var string $name |
||
| 496 | */ |
||
| 497 | 6 | foreach ($objectProperties as $name => $value) { |
|
| 498 | 6 | $propertyName = $this->getPropertyName($name); |
|
| 499 | 6 | $lines[] = $this->offset . $this->offset . '$this->' . $propertyName . ' = ' . |
|
| 500 | 6 | $this->exportInternal($value, $format, $level + 2) . ';'; |
|
| 501 | } |
||
| 502 | |||
| 503 | 6 | return implode("\n" . ($format ? $spaces : ''), array_merge($lines, $endLines)); |
|
| 504 | } |
||
| 505 | |||
| 506 | /** |
||
| 507 | * Exports a {@see \Closure} instance. |
||
| 508 | * |
||
| 509 | * @param Closure $closure Closure instance. |
||
| 510 | * |
||
| 511 | * @throws ReflectionException |
||
| 512 | * |
||
| 513 | * @return string |
||
| 514 | */ |
||
| 515 | 56 | private function exportClosure(Closure $closure, int $level = 0): string |
|
| 516 | { |
||
| 517 | 56 | if (self::$closureExporter === null) { |
|
| 518 | 1 | self::$closureExporter = new ClosureExporter(); |
|
| 519 | } |
||
| 520 | |||
| 521 | 56 | return self::$closureExporter->export($closure, $level); |
|
|
0 ignored issues
–
show
|
|||
| 522 | } |
||
| 523 | |||
| 524 | /** |
||
| 525 | * @return string |
||
| 526 | */ |
||
| 527 | 42 | private function exportVariable(mixed $variable): string |
|
| 528 | { |
||
| 529 | 42 | return var_export($variable, true); |
|
| 530 | } |
||
| 531 | |||
| 532 | 7 | private function getObjectDescription(object $object): string |
|
| 533 | { |
||
| 534 | 7 | return $object::class . '#' . $this->getObjectId($object); |
|
| 535 | } |
||
| 536 | |||
| 537 | 23 | private function getObjectId(object $object): string |
|
| 538 | { |
||
| 539 | 23 | return (string) spl_object_id($object); |
|
| 540 | } |
||
| 541 | |||
| 542 | 28 | private function getObjectProperties(object $var): array |
|
| 543 | { |
||
| 544 | 28 | if (!$var instanceof __PHP_Incomplete_Class && method_exists($var, '__debugInfo')) { |
|
| 545 | /** @var array $var */ |
||
| 546 | 3 | $var = $var->__debugInfo(); |
|
| 547 | } |
||
| 548 | |||
| 549 | 28 | return (array) $var; |
|
| 550 | } |
||
| 551 | |||
| 552 | 5 | private function exportDateTime(DateTimeInterface $variable): string |
|
| 553 | { |
||
| 554 | 5 | return sprintf( |
|
| 555 | 5 | "new \%s('%s', new \DateTimeZone('%s'))", |
|
| 556 | 5 | $variable::class, |
|
| 557 | 5 | $variable->format(DateTimeInterface::RFC3339_EXTENDED), |
|
| 558 | 5 | $variable->getTimezone()->getName() |
|
| 559 | 5 | ); |
|
| 560 | } |
||
| 561 | } |
||
| 562 |
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.
This is most likely a typographical error or the method has been renamed.