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: const PHP_IDENT = '[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*';
19: const MAX_DEPTH = 50;
20:
21:
22: 23: 24: 25:
26: public static function dump($var)
27: {
28: return self::_dump($var);
29: }
30:
31:
32: private static function _dump(& $var, $level = 0)
33: {
34: if ($var instanceof PhpLiteral) {
35: return (string) $var;
36:
37: } elseif (is_float($var)) {
38: $var = var_export($var, TRUE);
39: return strpos($var, '.') === FALSE ? $var . '.0' : $var;
40:
41: } elseif (is_bool($var)) {
42: return $var ? 'TRUE' : 'FALSE';
43:
44: } elseif (is_string($var) && (preg_match('#[^\x09\x20-\x7E\xA0-\x{10FFFF}]#u', $var) || preg_last_error())) {
45: static $table;
46: if ($table === NULL) {
47: foreach (array_merge(range("\x00", "\x1F"), range("\x7F", "\xFF")) as $ch) {
48: $table[$ch] = '\x' . str_pad(dechex(ord($ch)), 2, '0', STR_PAD_LEFT);
49: }
50: $table['\\'] = '\\\\';
51: $table["\r"] = '\r';
52: $table["\n"] = '\n';
53: $table["\t"] = '\t';
54: $table['$'] = '\$';
55: $table['"'] = '\"';
56: }
57: return '"' . strtr($var, $table) . '"';
58:
59: } elseif (is_string($var)) {
60: return "'" . preg_replace('#\'|\\\\(?=[\'\\\\]|\z)#', '\\\\$0', $var) . "'";
61:
62: } elseif (is_array($var)) {
63: $space = str_repeat("\t", $level);
64:
65: static $marker;
66: if ($marker === NULL) {
67: $marker = uniqid("\x00", TRUE);
68: }
69: if (empty($var)) {
70: $out = '';
71:
72: } elseif ($level > self::MAX_DEPTH || isset($var[$marker])) {
73: throw new Nette\InvalidArgumentException('Nesting level too deep or recursive dependency.');
74:
75: } else {
76: $out = '';
77: $outAlt = "\n$space";
78: $var[$marker] = TRUE;
79: $counter = 0;
80: foreach ($var as $k => & $v) {
81: if ($k !== $marker) {
82: $item = ($k === $counter ? '' : self::_dump($k, $level + 1) . ' => ') . self::_dump($v, $level + 1);
83: $counter = is_int($k) ? max($k + 1, $counter) : $counter;
84: $out .= ($out === '' ? '' : ', ') . $item;
85: $outAlt .= "\t$item,\n$space";
86: }
87: }
88: unset($var[$marker]);
89: }
90: return 'array(' . (strpos($out, "\n") === FALSE && strlen($out) < 40 ? $out : $outAlt) . ')';
91:
92: } elseif ($var instanceof \Serializable) {
93: $var = serialize($var);
94: return 'unserialize(' . self::_dump($var, $level) . ')';
95:
96: } elseif ($var instanceof \Closure) {
97: throw new Nette\InvalidArgumentException('Cannot dump closure.');
98:
99: } elseif (is_object($var)) {
100: if (PHP_VERSION_ID >= 70000 && ($rc = new \ReflectionObject($var)) && $rc->isAnonymous()) {
101: throw new Nette\InvalidArgumentException('Cannot dump anonymous class.');
102: }
103: $arr = (array) $var;
104: $space = str_repeat("\t", $level);
105: $class = get_class($var);
106:
107: static $list = array();
108: if ($level > self::MAX_DEPTH || in_array($var, $list, TRUE)) {
109: throw new Nette\InvalidArgumentException('Nesting level too deep or recursive dependency.');
110:
111: } else {
112: $out = "\n";
113: $list[] = $var;
114: if (method_exists($var, '__sleep')) {
115: foreach ($var->__sleep() as $v) {
116: $props[$v] = $props["\x00*\x00$v"] = $props["\x00$class\x00$v"] = TRUE;
117: }
118: }
119: foreach ($arr as $k => & $v) {
120: if (!isset($props) || isset($props[$k])) {
121: $out .= "$space\t" . self::_dump($k, $level + 1) . ' => ' . self::_dump($v, $level + 1) . ",\n";
122: }
123: }
124: array_pop($list);
125: $out .= $space;
126: }
127: return $class === 'stdClass'
128: ? "(object) array($out)"
129: : __CLASS__ . "::createObject('$class', array($out))";
130:
131: } elseif (is_resource($var)) {
132: throw new Nette\InvalidArgumentException('Cannot dump resource.');
133:
134: } else {
135: return var_export($var, TRUE);
136: }
137: }
138:
139:
140: 141: 142: 143:
144: public static function format($statement)
145: {
146: $args = func_get_args();
147: return self::formatArgs(array_shift($args), $args);
148: }
149:
150:
151: 152: 153: 154:
155: public static function formatArgs($statement, array $args)
156: {
157: $a = strpos($statement, '?');
158: while ($a !== FALSE) {
159: if (!$args) {
160: throw new Nette\InvalidArgumentException('Insufficient number of arguments.');
161: }
162: $arg = array_shift($args);
163: if (substr($statement, $a + 1, 1) === '*') {
164: if (!is_array($arg)) {
165: throw new Nette\InvalidArgumentException('Argument must be an array.');
166: }
167: $s = substr($statement, 0, $a);
168: $sep = '';
169: foreach ($arg as $tmp) {
170: $s .= $sep . self::dump($tmp);
171: $sep = strlen($s) - strrpos($s, "\n") > 100 ? ",\n\t" : ', ';
172: }
173: $statement = $s . substr($statement, $a + 2);
174: $a = strlen($s);
175:
176: } else {
177: $arg = substr($statement, $a - 1, 1) === '$' || in_array(substr($statement, $a - 2, 2), array('->', '::'), TRUE)
178: ? self::formatMember($arg) : self::_dump($arg);
179: $statement = substr_replace($statement, $arg, $a, 1);
180: $a += strlen($arg);
181: }
182: $a = strpos($statement, '?', $a);
183: }
184: return $statement;
185: }
186:
187:
188: 189: 190: 191:
192: public static function formatMember($name)
193: {
194: return $name instanceof PhpLiteral || !self::isIdentifier($name)
195: ? '{' . self::_dump($name) . '}'
196: : $name;
197: }
198:
199:
200: 201: 202:
203: public static function isIdentifier($value)
204: {
205: return is_string($value) && preg_match('#^' . self::PHP_IDENT . '\z#', $value);
206: }
207:
208:
209:
210: public static function createObject($class, array $props)
211: {
212: return unserialize('O' . substr(serialize((string) $class), 1, -1) . substr(serialize($props), 1));
213: }
214:
215:
216: 217: 218: 219:
220: public static function ($name)
221: {
222: return ($pos = strrpos($name, '\\')) ? substr($name, 0, $pos) : '';
223: }
224:
225:
226: 227: 228: 229:
230: public static function ($name)
231: {
232: return ($pos = strrpos($name, '\\')) === FALSE ? $name : substr($name, $pos + 1);
233: }
234:
235: }
236: