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 ($var === null) {
48: return 'null';
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:
155: public static function format($statement, ...$args)
156: {
157: return self::formatArgs($statement, $args);
158: }
159:
160:
161: 162: 163: 164: 165:
166: public static function formatArgs($statement, array $args)
167: {
168: $tokens = preg_split('#(\.\.\.\?|\$\?|->\?|::\?|\\\\\?|\?\*|\?)#', $statement, -1, PREG_SPLIT_DELIM_CAPTURE);
169: $res = '';
170: foreach ($tokens as $n => $token) {
171: if ($n % 2 === 0) {
172: $res .= $token;
173: } elseif ($token === '\\?') {
174: $res .= '?';
175: } elseif (!$args) {
176: throw new Nette\InvalidArgumentException('Insufficient number of arguments.');
177: } elseif ($token === '?') {
178: $res .= self::dump(array_shift($args));
179: } elseif ($token === '...?' || $token === '?*') {
180: $arg = array_shift($args);
181: if (!is_array($arg)) {
182: throw new Nette\InvalidArgumentException('Argument must be an array.');
183: }
184: $sep = '';
185: foreach ($arg as $tmp) {
186: $res .= $sep . self::dump($tmp);
187: $sep = strlen($res) - strrpos($res, "\n") > self::WRAP_LENGTH ? ",\n\t" : ', ';
188: }
189: } else {
190: $res .= substr($token, 0, -1) . self::formatMember(array_shift($args));
191: }
192: }
193: return $res;
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:
213: public static function ($content)
214: {
215: if (($s = trim($content)) === '') {
216: return '';
217: } elseif (strpos($content, "\n") === false) {
218: return "/** $s */\n";
219: } else {
220: return str_replace("\n", "\n * ", "/**\n$s") . "\n */\n";
221: }
222: }
223:
224:
225: 226: 227: 228:
229: public static function ($comment)
230: {
231: return preg_replace('#^\s*\* ?#m', '', trim(trim(trim($comment), '/*')));
232: }
233:
234:
235: 236: 237:
238: public static function isIdentifier($value)
239: {
240: return is_string($value) && preg_match('#^' . self::PHP_IDENT . '\z#', $value);
241: }
242:
243:
244: 245: 246:
247: public static function isNamespaceIdentifier($value, $allowLeadingSlash = false)
248: {
249: $re = '#^' . ($allowLeadingSlash ? '\\\\?' : '') . self::PHP_IDENT . '(\\\\' . self::PHP_IDENT . ')*\z#';
250: return is_string($value) && preg_match($re, $value);
251: }
252:
253:
254: 255: 256: 257: 258:
259: public static function createObject($class, array $props)
260: {
261: return unserialize('O' . substr(serialize((string) $class), 1, -1) . substr(serialize($props), 1));
262: }
263:
264:
265: 266: 267: 268:
269: public static function ($name)
270: {
271: return ($pos = strrpos($name, '\\')) ? substr($name, 0, $pos) : '';
272: }
273:
274:
275: 276: 277: 278:
279: public static function ($name)
280: {
281: return ($pos = strrpos($name, '\\')) === false ? $name : substr($name, $pos + 1);
282: }
283: }
284: