PluginProbe
Packeta / 1.6.1
Packeta v1.6.1
2.3.2 2.3.1 trunk 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.3.0 1.3.1 1.3.2 1.4 1.4.1 1.4.2 1.4.3 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 All 56 releases
packeta / deps / nette / php-generator / src / PhpGenerator / Dumper.php

Dumper.php in Packeta 1.6.1, at deps/nette/php-generator/src/PhpGenerator/Dumper.php

181 lines 7.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * This file is part of the Nette Framework (https://nette.org)
5 * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
6 */
7 declare (strict_types=1);
8 namespace Packetery\Nette\PhpGenerator;
9
10 use Packetery\Nette;
11 /**
12 * PHP code generator utils.
13 */
14 final class Dumper
15 {
16 private const INDENT_LENGTH = 4;
17 /** @var int */
18 public $maxDepth = 50;
19 /** @var int */
20 public $wrapLength = 120;
21 /**
22 * Returns a PHP representation of a variable.
23 */
24 public function dump($var, int $column = 0) : string
25 {
26 return $this->dumpVar($var, [], 0, $column);
27 }
28 private function dumpVar(&$var, array $parents = [], int $level = 0, int $column = 0) : string
29 {
30 if ($var === null) {
31 return 'null';
32 } elseif (\is_string($var)) {
33 return $this->dumpString($var);
34 } elseif (\is_array($var)) {
35 return $this->dumpArray($var, $parents, $level, $column);
36 } elseif (\is_object($var)) {
37 if ($var instanceof Literal || $var instanceof Closure) {
38 return \ltrim(\Packetery\Nette\Utils\Strings::indent(\trim((string) $var), $level), "\t");
39 }
40 return $this->dumpObject($var, $parents, $level);
41 } elseif (\is_resource($var)) {
42 throw new \Packetery\Nette\InvalidArgumentException('Cannot dump resource.');
43 } else {
44 return \var_export($var, \true);
45 }
46 }
47 private function dumpString(string $var) : string
48 {
49 if (\preg_match('#[^\\x09\\x20-\\x7E\\xA0-\\x{10FFFF}]#u', $var) || \preg_last_error()) {
50 static $table;
51 if ($table === null) {
52 foreach (\array_merge(\range("\x00", "\x1f"), \range("", "\xff")) as $ch) {
53 $table[$ch] = '\\x' . \str_pad(\dechex(\ord($ch)), 2, '0', \STR_PAD_LEFT);
54 }
55 $table['\\'] = '\\\\';
56 $table["\r"] = '\\r';
57 $table["\n"] = '\\n';
58 $table["\t"] = '\\t';
59 $table['$'] = '\\$';
60 $table['"'] = '\\"';
61 }
62 return '"' . \strtr($var, $table) . '"';
63 }
64 return "'" . \preg_replace('#\'|\\\\(?=[\'\\\\]|$)#D', '\\\\$0', $var) . "'";
65 }
66 private function dumpArray(array &$var, array $parents, int $level, int $column) : string
67 {
68 if (empty($var)) {
69 return '[]';
70 } elseif ($level > $this->maxDepth || \in_array($var, $parents ?? [], \true)) {
71 throw new \Packetery\Nette\InvalidArgumentException('Nesting level too deep or recursive dependency.');
72 }
73 $space = \str_repeat("\t", $level);
74 $outInline = '';
75 $outWrapped = "\n{$space}";
76 $parents[] = $var;
77 $counter = 0;
78 $hideKeys = \is_int(($tmp = \array_keys($var))[0]) && $tmp === \range($tmp[0], $tmp[0] + \count($var) - 1);
79 foreach ($var as $k => &$v) {
80 $keyPart = $hideKeys && $k === $counter ? '' : $this->dumpVar($k) . ' => ';
81 $counter = \is_int($k) ? \max($k + 1, $counter) : $counter;
82 $outInline .= ($outInline === '' ? '' : ', ') . $keyPart;
83 $outInline .= $this->dumpVar($v, $parents, 0, $column + \strlen($outInline));
84 $outWrapped .= "\t" . $keyPart . $this->dumpVar($v, $parents, $level + 1, \strlen($keyPart)) . ",\n{$space}";
85 }
86 \array_pop($parents);
87 $wrap = \strpos($outInline, "\n") !== \false || $level * self::INDENT_LENGTH + $column + \strlen($outInline) + 3 > $this->wrapLength;
88 // 3 = [],
89 return '[' . ($wrap ? $outWrapped : $outInline) . ']';
90 }
91 private function dumpObject(&$var, array $parents, int $level) : string
92 {
93 if ($var instanceof \Serializable) {
94 return 'unserialize(' . $this->dumpString(\serialize($var)) . ')';
95 } elseif ($var instanceof \Closure) {
96 throw new \Packetery\Nette\InvalidArgumentException('Cannot dump closure.');
97 }
98 $class = \get_class($var);
99 if ((new \ReflectionObject($var))->isAnonymous()) {
100 throw new \Packetery\Nette\InvalidArgumentException('Cannot dump anonymous class.');
101 } elseif (\in_array($class, [\DateTime::class, \DateTimeImmutable::class], \true)) {
102 return $this->format("new \\{$class}(?, new \\DateTimeZone(?))", $var->format('Y-m-d H:i:s.u'), $var->getTimeZone()->getName());
103 }
104 $arr = (array) $var;
105 $space = \str_repeat("\t", $level);
106 if ($level > $this->maxDepth || \in_array($var, $parents ?? [], \true)) {
107 throw new \Packetery\Nette\InvalidArgumentException('Nesting level too deep or recursive dependency.');
108 }
109 $out = "\n";
110 $parents[] = $var;
111 if (\method_exists($var, '__sleep')) {
112 foreach ($var->__sleep() as $v) {
113 $props[$v] = $props["\x00*\x00{$v}"] = $props["\x00{$class}\x00{$v}"] = \true;
114 }
115 }
116 foreach ($arr as $k => &$v) {
117 if (!isset($props) || isset($props[$k])) {
118 $out .= "{$space}\t" . ($keyPart = $this->dumpVar($k) . ' => ') . $this->dumpVar($v, $parents, $level + 1, \strlen($keyPart)) . ",\n";
119 }
120 }
121 \array_pop($parents);
122 $out .= $space;
123 return $class === \stdClass::class ? "(object) [{$out}]" : '\\' . self::class . "::createObject('{$class}', [{$out}])";
124 }
125 /**
126 * Generates PHP statement.
127 */
128 public function format(string $statement, ...$args) : string
129 {
130 $tokens = \preg_split('#(\\.\\.\\.\\?:?|\\$\\?|->\\?|::\\?|\\\\\\?|\\?\\*|\\?)#', $statement, -1, \PREG_SPLIT_DELIM_CAPTURE);
131 $res = '';
132 foreach ($tokens as $n => $token) {
133 if ($n % 2 === 0) {
134 $res .= $token;
135 } elseif ($token === '\\?') {
136 $res .= '?';
137 } elseif (!$args) {
138 throw new \Packetery\Nette\InvalidArgumentException('Insufficient number of arguments.');
139 } elseif ($token === '?') {
140 $res .= $this->dump(\array_shift($args), \strlen($res) - \strrpos($res, "\n"));
141 } elseif ($token === '...?' || $token === '...?:' || $token === '?*') {
142 $arg = \array_shift($args);
143 if (!\is_array($arg)) {
144 throw new \Packetery\Nette\InvalidArgumentException('Argument must be an array.');
145 }
146 $res .= $this->dumpArguments($arg, \strlen($res) - \strrpos($res, "\n"), $token === '...?:');
147 } else {
148 // $ -> ::
149 $arg = \array_shift($args);
150 if ($arg instanceof Literal || !Helpers::isIdentifier($arg)) {
151 $arg = '{' . $this->dumpVar($arg) . '}';
152 }
153 $res .= \substr($token, 0, -1) . $arg;
154 }
155 }
156 if ($args) {
157 throw new \Packetery\Nette\InvalidArgumentException('Insufficient number of placeholders.');
158 }
159 return $res;
160 }
161 private function dumpArguments(array &$var, int $column, bool $named) : string
162 {
163 $outInline = $outWrapped = '';
164 foreach ($var as $k => &$v) {
165 $k = !$named || \is_int($k) ? '' : $k . ': ';
166 $outInline .= $outInline === '' ? '' : ', ';
167 $outInline .= $k . $this->dumpVar($v, [$var], 0, $column + \strlen($outInline));
168 $outWrapped .= ($outWrapped === '' ? '' : ',') . "\n\t" . $k . $this->dumpVar($v, [$var], 1);
169 }
170 return \count($var) > 1 && (\strpos($outInline, "\n") !== \false || $column + \strlen($outInline) > $this->wrapLength) ? $outWrapped . "\n" : $outInline;
171 }
172 /**
173 * @return object
174 * @internal
175 */
176 public static function createObject(string $class, array $props)
177 {
178 return \unserialize('O' . \substr(\serialize($class), 1, -1) . \substr(\serialize($props), 1));
179 }
180 }
181