Skip to content

Commit 6e1cc8d

Browse files
committed
Helpers divided into Helpers & Dumper
1 parent 2118996 commit 6e1cc8d

File tree

9 files changed

+447
-419
lines changed

9 files changed

+447
-419
lines changed

src/PhpGenerator/Dumper.php

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
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+
8+
declare(strict_types=1);
9+
10+
namespace Nette\PhpGenerator;
11+
12+
use Nette;
13+
14+
15+
/**
16+
* PHP code generator utils.
17+
*/
18+
final class Dumper
19+
{
20+
use Nette\StaticClass;
21+
22+
public const WRAP_LENGTH = 100;
23+
24+
private const INDENT_LENGTH = 4;
25+
26+
private const MAX_DEPTH = 50;
27+
28+
29+
/**
30+
* Returns a PHP representation of a variable.
31+
*/
32+
public static function dump($var): string
33+
{
34+
return self::_dump($var);
35+
}
36+
37+
38+
private static function _dump(&$var, int $level = 0)
39+
{
40+
if ($var instanceof PhpLiteral) {
41+
return (string) $var;
42+
43+
} elseif (is_float($var)) {
44+
return var_export($var, true);
45+
46+
} elseif ($var === null) {
47+
return 'null';
48+
49+
} elseif (is_string($var) && (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("\x7F", "\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+
} elseif (is_string($var)) {
65+
return "'" . preg_replace('#\'|\\\\(?=[\'\\\\]|$)#D', '\\\\$0', $var) . "'";
66+
67+
} elseif (is_array($var)) {
68+
$space = str_repeat("\t", $level);
69+
70+
static $marker;
71+
if ($marker === null) {
72+
$marker = uniqid("\x00", true);
73+
}
74+
if (empty($var)) {
75+
$out = '';
76+
77+
} elseif ($level > self::MAX_DEPTH || isset($var[$marker])) {
78+
throw new Nette\InvalidArgumentException('Nesting level too deep or recursive dependency.');
79+
80+
} else {
81+
$out = '';
82+
$outWrapped = "\n$space";
83+
$var[$marker] = true;
84+
$counter = 0;
85+
foreach ($var as $k => &$v) {
86+
if ($k !== $marker) {
87+
$item = ($k === $counter ? '' : self::_dump($k, $level + 1) . ' => ') . self::_dump($v, $level + 1);
88+
$counter = is_int($k) ? max($k + 1, $counter) : $counter;
89+
$out .= ($out === '' ? '' : ', ') . $item;
90+
$outWrapped .= "\t$item,\n$space";
91+
}
92+
}
93+
unset($var[$marker]);
94+
}
95+
$wrap = strpos($out, "\n") !== false || strlen($out) > self::WRAP_LENGTH - $level * self::INDENT_LENGTH;
96+
return '[' . ($wrap ? $outWrapped : $out) . ']';
97+
98+
} elseif ($var instanceof \Serializable) {
99+
$var = serialize($var);
100+
return 'unserialize(' . self::_dump($var, $level) . ')';
101+
102+
} elseif ($var instanceof \Closure) {
103+
throw new Nette\InvalidArgumentException('Cannot dump closure.');
104+
105+
} elseif (is_object($var)) {
106+
$class = get_class($var);
107+
if ((new \ReflectionObject($var))->isAnonymous()) {
108+
throw new Nette\InvalidArgumentException('Cannot dump anonymous class.');
109+
110+
} elseif (in_array($class, ['DateTime', 'DateTimeImmutable'], true)) {
111+
return self::format("new $class(?, new DateTimeZone(?))", $var->format('Y-m-d H:i:s.u'), $var->getTimeZone()->getName());
112+
}
113+
114+
$arr = (array) $var;
115+
$space = str_repeat("\t", $level);
116+
117+
static $list = [];
118+
if ($level > self::MAX_DEPTH || in_array($var, $list, true)) {
119+
throw new Nette\InvalidArgumentException('Nesting level too deep or recursive dependency.');
120+
121+
} else {
122+
$out = "\n";
123+
$list[] = $var;
124+
if (method_exists($var, '__sleep')) {
125+
foreach ($var->__sleep() as $v) {
126+
$props[$v] = $props["\x00*\x00$v"] = $props["\x00$class\x00$v"] = true;
127+
}
128+
}
129+
foreach ($arr as $k => &$v) {
130+
if (!isset($props) || isset($props[$k])) {
131+
$out .= "$space\t" . self::_dump($k, $level + 1) . ' => ' . self::_dump($v, $level + 1) . ",\n";
132+
}
133+
}
134+
array_pop($list);
135+
$out .= $space;
136+
}
137+
return $class === 'stdClass'
138+
? "(object) [$out]"
139+
: __CLASS__ . "::createObject('$class', [$out])";
140+
141+
} elseif (is_resource($var)) {
142+
throw new Nette\InvalidArgumentException('Cannot dump resource.');
143+
144+
} else {
145+
return var_export($var, true);
146+
}
147+
}
148+
149+
150+
/**
151+
* Generates PHP statement.
152+
*/
153+
public static function format(string $statement, ...$args): string
154+
{
155+
$tokens = preg_split('#(\.\.\.\?|\$\?|->\?|::\?|\\\\\?|\?\*|\?)#', $statement, -1, PREG_SPLIT_DELIM_CAPTURE);
156+
$res = '';
157+
foreach ($tokens as $n => $token) {
158+
if ($n % 2 === 0) {
159+
$res .= $token;
160+
} elseif ($token === '\\?') {
161+
$res .= '?';
162+
} elseif (!$args) {
163+
throw new Nette\InvalidArgumentException('Insufficient number of arguments.');
164+
} elseif ($token === '?') {
165+
$res .= self::dump(array_shift($args));
166+
} elseif ($token === '...?' || $token === '?*') {
167+
$arg = array_shift($args);
168+
if (!is_array($arg)) {
169+
throw new Nette\InvalidArgumentException('Argument must be an array.');
170+
}
171+
$items = [];
172+
foreach ($arg as $tmp) {
173+
$items[] = self::dump($tmp);
174+
}
175+
$res .= strlen($tmp = implode(', ', $items)) > self::WRAP_LENGTH && count($items) > 1
176+
? "\n" . Nette\Utils\Strings::indent(implode(",\n", $items)) . "\n"
177+
: $tmp;
178+
179+
} else { // $ -> ::
180+
$res .= substr($token, 0, -1) . self::formatMember(array_shift($args));
181+
}
182+
}
183+
if ($args) {
184+
throw new Nette\InvalidArgumentException('Insufficient number of placeholders.');
185+
}
186+
return $res;
187+
}
188+
189+
190+
/**
191+
* Returns a PHP representation of a object member.
192+
*/
193+
private static function formatMember($name): string
194+
{
195+
return $name instanceof PhpLiteral || !Helpers::isIdentifier($name)
196+
? '{' . self::_dump($name) . '}'
197+
: $name;
198+
}
199+
200+
201+
/**
202+
* @return object
203+
* @internal
204+
*/
205+
public static function createObject(string $class, array $props)
206+
{
207+
return unserialize('O' . substr(serialize($class), 1, -1) . substr(serialize($props), 1));
208+
}
209+
}

0 commit comments

Comments
 (0)