1: <?php
2:
3: 4: 5: 6:
7:
8: namespace Nette\PhpGenerator;
9:
10: use Nette;
11:
12:
13: 14: 15:
16: class Helpers
17: {
18: use Nette\StaticClass;
19:
20: const PHP_IDENT = '[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*';
21: const MAX_DEPTH = 50;
22: const WRAP_LENGTH = 70;
23:
24:
25: 26: 27: 28:
29: public static function dump($var)
30: {
31: return self::_dump($var);
32: }
33:
34:
35: private static function _dump(&$var, $level = 0)
36: {
37: if ($var instanceof PhpLiteral) {
38: return (string) $var;
39:
40: } elseif (is_float($var)) {
41: if (is_finite($var)) {
42: $var = var_export($var, TRUE);
43: return strpos($var, '.') === FALSE ? $var . '.0' : $var;
44: }
45: return str_replace('.0', '', var_export($var, TRUE));
46:
47: } elseif (is_bool($var)) {
48: return $var ? 'TRUE' : 'FALSE';
49:
50: } elseif (is_string($var) && (preg_match('#[^\x09\x20-\x7E\xA0-\x{10FFFF}]#u', $var) || preg_last_error())) {
51: static $table;
52: if ($table === NULL) {
53: foreach (array_merge(range("\x00", "\x1F"), range("\x7F", "\xFF")) as $ch) {
54: $table[$ch] = '\x' . str_pad(dechex(ord($ch)), 2, '0', STR_PAD_LEFT);
55: }
56: $table['\\'] = '\\\\';
57: $table["\r"] = '\r';
58: $table["\n"] = '\n';
59: $table["\t"] = '\t';
60: $table['$'] = '\$';
61: $table['"'] = '\"';
62: }
63: return '"' . strtr($var, $table) . '"';
64:
65: } elseif (is_string($var)) {
66: return "'" . preg_replace('#\'|\\\\(?=[\'\\\\]|\z)#', '\\\\$0', $var) . "'";
67:
68: } elseif (is_array($var)) {
69: $space = str_repeat("\t", $level);
70:
71: static $marker;
72: if ($marker === NULL) {
73: $marker = uniqid("\x00", TRUE);
74: }
75: if (empty($var)) {
76: $out = '';
77:
78: } elseif ($level > self::MAX_DEPTH || isset($var[$marker])) {
79: throw new Nette\InvalidArgumentException('Nesting level too deep or recursive dependency.');
80:
81: } else {
82: $out = '';
83: $outAlt = "\n$space";
84: $var[$marker] = TRUE;
85: $counter = 0;
86: foreach ($var as $k => &$v) {
87: if ($k !== $marker) {
88: $item = ($k === $counter ? '' : self::_dump($k, $level + 1) . ' => ') . self::_dump($v, $level + 1);
89: $counter = is_int($k) ? max($k + 1, $counter) : $counter;
90: $out .= ($out === '' ? '' : ', ') . $item;
91: $outAlt .= "\t$item,\n$space";
92: }
93: }
94: unset($var[$marker]);
95: }
96: return '[' . (strpos($out, "\n") === FALSE && strlen($out) < self::WRAP_LENGTH ? $out : $outAlt) . ']';
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 (PHP_VERSION_ID >= 70000 && (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::formatArgs("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: 152: 153:
154: public static function format($statement, ...$args)
155: {
156: return self::formatArgs($statement, $args);
157: }
158:
159:
160: 161: 162: 163:
164: public static function formatArgs($statement, array $args)
165: {
166: $a = strpos($statement, '?');
167: while ($a !== FALSE) {
168: if (!$args) {
169: throw new Nette\InvalidArgumentException('Insufficient number of arguments.');
170: }
171: $arg = array_shift($args);
172: if (substr($statement, $a + 1, 1) === '*') {
173: if (!is_array($arg)) {
174: throw new Nette\InvalidArgumentException('Argument must be an array.');
175: }
176: $s = substr($statement, 0, $a);
177: $sep = '';
178: foreach ($arg as $tmp) {
179: $s .= $sep . self::dump($tmp);
180: $sep = strlen($s) - strrpos($s, "\n") > self::WRAP_LENGTH ? ",\n\t" : ', ';
181: }
182: $statement = $s . substr($statement, $a + 2);
183: $a = strlen($s);
184:
185: } else {
186: $arg = substr($statement, $a - 1, 1) === '$' || in_array(substr($statement, $a - 2, 2), ['->', '::'], TRUE)
187: ? self::formatMember($arg) : self::_dump($arg);
188: $statement = substr_replace($statement, $arg, $a, 1);
189: $a += strlen($arg);
190: }
191: $a = strpos($statement, '?', $a);
192: }
193: return $statement;
194: }
195:
196:
197: 198: 199: 200:
201: public static function formatMember($name)
202: {
203: return $name instanceof PhpLiteral || !self::isIdentifier($name)
204: ? '{' . self::_dump($name) . '}'
205: : $name;
206: }
207:
208:
209: 210: 211:
212: public static function ($content)
213: {
214: if (($s = trim($content)) === '') {
215: return '';
216: } elseif (strpos($content, "\n") === FALSE) {
217: return "/** $s */\n";
218: } else {
219: return str_replace("\n", "\n * ", "/**\n$s") . "\n */\n";
220: }
221: }
222:
223:
224: 225: 226:
227: public static function ($comment)
228: {
229: return preg_replace('#^\s*\* ?#m', '', trim(trim(trim($comment), '/*')));
230: }
231:
232:
233: 234: 235:
236: public static function isIdentifier($value)
237: {
238: return is_string($value) && preg_match('#^' . self::PHP_IDENT . '\z#', $value);
239: }
240:
241:
242:
243: public static function createObject($class, array $props)
244: {
245: return unserialize('O' . substr(serialize((string) $class), 1, -1) . substr(serialize($props), 1));
246: }
247:
248:
249: 250: 251: 252:
253: public static function ($name)
254: {
255: return ($pos = strrpos($name, '\\')) ? substr($name, 0, $pos) : '';
256: }
257:
258:
259: 260: 261: 262:
263: public static function ($name)
264: {
265: return ($pos = strrpos($name, '\\')) === FALSE ? $name : substr($name, $pos + 1);
266: }
267:
268: }
269: