1: <?php
2:
3: /*
4: * The MIT License (MIT)
5: *
6: * Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
7: *
8: * Permission is hereby granted, free of charge, to any person obtaining a copy of
9: * this software and associated documentation files (the "Software"), to deal in
10: * the Software without restriction, including without limitation the rights to
11: * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
12: * the Software, and to permit persons to whom the Software is furnished to do so,
13: * subject to the following conditions:
14: *
15: * The above copyright notice and this permission notice shall be included in all
16: * copies or substantial portions of the Software.
17: *
18: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19: * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
20: * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
21: * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
22: * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23: * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24: */
25:
26: namespace Kint\Renderer;
27:
28: use Kint\Kint;
29: use Kint\Object\BasicObject;
30: use Kint\Object\InstanceObject;
31: use Kint\Utils;
32:
33: class TextRenderer extends Renderer
34: {
35: /**
36: * TextRenderer plugins should be instances of Kint\Renderer\Text\Plugin.
37: */
38: public static $plugins = array(
39: 'blacklist' => 'Kint\\Renderer\\Text\\BlacklistPlugin',
40: 'depth_limit' => 'Kint\\Renderer\\Text\\DepthLimitPlugin',
41: 'microtime' => 'Kint\\Renderer\\Text\\MicrotimePlugin',
42: 'recursion' => 'Kint\\Renderer\\Text\\RecursionPlugin',
43: 'trace' => 'Kint\\Renderer\\Text\\TracePlugin',
44: );
45:
46: /**
47: * Parser plugins must be instanceof one of these or
48: * it will be removed for performance reasons.
49: */
50: public static $parser_plugin_whitelist = array(
51: 'Kint\\Parser\\BlacklistPlugin',
52: 'Kint\\Parser\\MicrotimePlugin',
53: 'Kint\\Parser\\StreamPlugin',
54: 'Kint\\Parser\\TracePlugin',
55: );
56:
57: /**
58: * The maximum length of a string before it is truncated.
59: *
60: * Falsey to disable
61: *
62: * @var int
63: */
64: public static $strlen_max = 0;
65:
66: /**
67: * The default width of the terminal for headers.
68: *
69: * @var int
70: */
71: public static $default_width = 80;
72:
73: /**
74: * Indentation width.
75: *
76: * @var int
77: */
78: public static $default_indent = 4;
79:
80: /**
81: * Decorate the header and footer.
82: *
83: * @var bool
84: */
85: public static $decorations = true;
86:
87: /**
88: * Sort mode for object properties.
89: *
90: * @var int
91: */
92: public static $sort = self::SORT_NONE;
93:
94: public $header_width = 80;
95: public $indent_width = 4;
96:
97: protected $plugin_objs = array();
98:
99: public function __construct()
100: {
101: $this->header_width = self::$default_width;
102: $this->indent_width = self::$default_indent;
103: }
104:
105: public function render(BasicObject $o)
106: {
107: if ($plugin = $this->getPlugin(self::$plugins, $o->hints)) {
108: if (\strlen($output = $plugin->render($o))) {
109: return $output;
110: }
111: }
112:
113: $out = '';
114:
115: if (0 == $o->depth) {
116: $out .= $this->colorTitle($this->renderTitle($o)).PHP_EOL;
117: }
118:
119: $out .= $this->renderHeader($o);
120: $out .= $this->renderChildren($o).PHP_EOL;
121:
122: return $out;
123: }
124:
125: public function renderNothing()
126: {
127: if (self::$decorations) {
128: return $this->colorTitle(
129: $this->boxText('No argument', $this->header_width)
130: ).PHP_EOL;
131: }
132:
133: return $this->colorTitle('No argument').PHP_EOL;
134: }
135:
136: public function boxText($text, $width)
137: {
138: $out = '┌'.\str_repeat('─', $width - 2).'┐'.PHP_EOL;
139:
140: if (\strlen($text)) {
141: $text = Utils::truncateString($text, $width - 4);
142: $text = \str_pad($text, $width - 4);
143:
144: $out .= '│ '.$this->escape($text).' │'.PHP_EOL;
145: }
146:
147: $out .= '└'.\str_repeat('─', $width - 2).'┘';
148:
149: return $out;
150: }
151:
152: public function renderTitle(BasicObject $o)
153: {
154: $name = (string) $o->getName();
155:
156: if (self::$decorations) {
157: return $this->boxText($name, $this->header_width);
158: }
159:
160: return Utils::truncateString($name, $this->header_width);
161: }
162:
163: public function renderHeader(BasicObject $o)
164: {
165: $output = array();
166:
167: if ($o->depth) {
168: if (null !== ($s = $o->getModifiers())) {
169: $output[] = $s;
170: }
171:
172: if (null !== $o->name) {
173: $output[] = $this->escape(\var_export($o->name, true));
174:
175: if (null !== ($s = $o->getOperator())) {
176: $output[] = $this->escape($s);
177: }
178: }
179: }
180:
181: if (null !== ($s = $o->getType())) {
182: if ($o->reference) {
183: $s = '&'.$s;
184: }
185:
186: $output[] = $this->colorType($this->escape($s));
187: }
188:
189: if (null !== ($s = $o->getSize())) {
190: $output[] = '('.$this->escape($s).')';
191: }
192:
193: if (null !== ($s = $o->getValueShort())) {
194: if (self::$strlen_max) {
195: $s = Utils::truncateString($s, self::$strlen_max);
196: }
197: $output[] = $this->colorValue($this->escape($s));
198: }
199:
200: return \str_repeat(' ', $o->depth * $this->indent_width).\implode(' ', $output);
201: }
202:
203: public function renderChildren(BasicObject $o)
204: {
205: if ('array' === $o->type) {
206: $output = ' [';
207: } elseif ('object' === $o->type) {
208: $output = ' (';
209: } else {
210: return '';
211: }
212:
213: $children = '';
214:
215: if ($o->value && \is_array($o->value->contents)) {
216: if ($o instanceof InstanceObject && 'properties' === $o->value->getName()) {
217: foreach (self::sortProperties($o->value->contents, self::$sort) as $obj) {
218: $children .= $this->render($obj);
219: }
220: } else {
221: foreach ($o->value->contents as $child) {
222: $children .= $this->render($child);
223: }
224: }
225: }
226:
227: if ($children) {
228: $output .= PHP_EOL.$children;
229: $output .= \str_repeat(' ', $o->depth * $this->indent_width);
230: }
231:
232: if ('array' === $o->type) {
233: $output .= ']';
234: } else {
235: $output .= ')';
236: }
237:
238: return $output;
239: }
240:
241: public function colorValue($string)
242: {
243: return $string;
244: }
245:
246: public function colorType($string)
247: {
248: return $string;
249: }
250:
251: public function colorTitle($string)
252: {
253: return $string;
254: }
255:
256: public function postRender()
257: {
258: if (self::$decorations) {
259: $output = \str_repeat('═', $this->header_width);
260: } else {
261: $output = '';
262: }
263:
264: if (!$this->show_trace) {
265: return $this->colorTitle($output);
266: }
267:
268: if ($output) {
269: $output .= PHP_EOL;
270: }
271:
272: return $this->colorTitle($output.$this->calledFrom().PHP_EOL);
273: }
274:
275: public function filterParserPlugins(array $plugins)
276: {
277: $return = array();
278:
279: foreach ($plugins as $index => $plugin) {
280: foreach (self::$parser_plugin_whitelist as $whitelist) {
281: if ($plugin instanceof $whitelist) {
282: $return[] = $plugin;
283: continue 2;
284: }
285: }
286: }
287:
288: return $return;
289: }
290:
291: public function ideLink($file, $line)
292: {
293: return $this->escape(Kint::shortenPath($file)).':'.$line;
294: }
295:
296: public function escape($string, $encoding = false)
297: {
298: return $string;
299: }
300:
301: protected function calledFrom()
302: {
303: $output = '';
304:
305: if (isset($this->call_info['callee']['file'])) {
306: $output .= 'Called from '.$this->ideLink(
307: $this->call_info['callee']['file'],
308: $this->call_info['callee']['line']
309: );
310: }
311:
312: if (isset($this->call_info['callee']['function']) && (
313: !empty($this->call_info['callee']['class']) ||
314: !\in_array(
315: $this->call_info['callee']['function'],
316: array('include', 'include_once', 'require', 'require_once'),
317: true
318: )
319: )
320: ) {
321: $output .= ' [';
322: if (isset($this->call_info['callee']['class'])) {
323: $output .= $this->call_info['callee']['class'];
324: }
325: if (isset($this->call_info['callee']['type'])) {
326: $output .= $this->call_info['callee']['type'];
327: }
328: $output .= $this->call_info['callee']['function'].'()]';
329: }
330:
331: return $output;
332: }
333:
334: protected function getPlugin(array $plugins, array $hints)
335: {
336: if ($plugins = $this->matchPlugins($plugins, $hints)) {
337: $plugin = \end($plugins);
338:
339: if (!isset($this->plugin_objs[$plugin])) {
340: $this->plugin_objs[$plugin] = new $plugin($this);
341: }
342:
343: return $this->plugin_objs[$plugin];
344: }
345: }
346: }
347: