Completed
Branch master (254118)
by Alexander
01:40
created

Debug::trace()   B

Complexity

Conditions 6
Paths 17

Size

Total Lines 20
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 16
nc 17
nop 0
dl 0
loc 20
rs 8.8571
c 0
b 0
f 0
1
<?php
2
/**
3
 * Global debug methods
4
 */
5
namespace alkemann\h2l\internals\debug;
6
7
class Debug
8
{
9
10
    public static $defaults = array(
11
        'echo' => true,
12
        'mode' => 'Html',
13
        'depth' => 10,
14
        'avoid' => array(),
15
        'docroot' => '/web',
16
        'blacklist' => array(
17
            'class' => array(),
18
            'property' => array(),
19
            'key' => array()
20
        )
21
    );
22
23
    protected static $__instance = null;
24
25
    public $current_depth;
26
    public $object_references;
27
    public $options;
28
    public $output = array('<style type="text/css">@import url("/css/debug.css");</style>');
29
30
	/**
31
	 * Get the singleton instance
32
	 *
33
	 * @return alkemann\hl\debug\util\Debug
34
	 */
35
    public static function get_instance() : Debug
36
    {
37
        if (!static::$__instance) {
38
            $class = __CLASS__;
39
            static::$__instance = new $class();
40
        }
41
        return static::$__instance;
42
    }
43
44
	/**
45
	 * Dump
46
	 *
47
	 * @param mixed $var
48
	 * @param array $options
49
	 */
50
    public function dump($var, $options = array()) : void
51
    {
52
        $options += self::$defaults + array('split' => false, 'trace' => false);
53
        $this->options = $options;
54
        $this->current_depth = 0;
55
        $this->object_references = array();
56
57
        if (!$options['trace']) $options['trace'] = debug_backtrace();
58
        extract($options);
59
        $location = $this->location($trace);
60
61
        $dump = array();
62
        if ($options['split'] && is_array($var)) {
63
            $this->current_depth = 0;
64
            foreach ($var as $one) {
65
				$dump = array_merge($dump, array($this->dump_it($one), ' - '));
66
			}
67
			$dump = array_slice($dump, 0, -1);
68
        } else
69
            $dump[] = $this->dump_it($var);
70
71
        switch ($mode) {
72
			default :
73
				$locString = \alkemann\h2l\internals\debug\adapters\Html::locationString($location);
74
				$this->output[] = '<div class="debug-dump"><div class="debug-location">' . $locString . '</div>'.
75
					'<div class="debug-content"> ' . implode("<br>\n", $dump) . '</div></div>';
76
				break;
77
		}
78
		if ($options['echo']) {
79
			$this->__out();
80
		}
81
    }
82
83
	/**
84
	 * Return output dump as array
85
	 *
86
	 * @param string $key
87
	 * @return array
88
	 */
89
	public function array_out($key = null) : array
90
    {
91
		if (count($this->output) < 2 || ($key && !isset($this->output[$key]))) {
92
			return [];
93
		}
94
		if ($key) {
95
			return $this->output[$key];
96
		}
97
		array_shift($this->output);
98
		return $this->output;
99
	}
100
101
	/**
102
	 * Echo out stored debugging
103
	 *
104
	 * @param int $key
105
	 */
106
	public function out($key = null) : void
107
    {
108
		if ($this->options['mode'] == 'Html') {
109
			\alkemann\h2l\internals\debug\adapters\Html::top($this->output, $key);
110
			return;
111
		}
112
		$this->__out($key);
113
	}
114
115
	private function __out($key = null) : void
116
    {
117
		if ($key !== null) {
118
			if (!isset($this->output[$key])) {
119
				throw new Exception('DEBUG: Not that many outputs in buffer');
120
			}
121
			echo $this->output[$key];
122
			return;
123
		}
124
		foreach ($this->output as $out) {
125
			echo $out;
126
		}
127
		$this->output = array();
128
	}
129
130
	/**
131
	 * Grab global defines, will start at 'FIRST_APP_CONSTANT', if defined
132
	 *
133
	 * @return array
134
	 */
135
    public function defines() : array
136
    {
137
        $defines = get_defined_constants();
138
        $ret = array(); $offset = -1;
139
        while ($def = array_slice($defines, $offset--, 1)) {
140
            $key = key($def);
141
            $value = current($def);
142
            if ($key  == 'FIRST_APP_CONSTANT') break;
143
            $ret[$key ] = $value;
144
        }
145
        return $ret;
146
    }
147
148
	/**
149
	 * Send a variable to the adapter and return it's formated output
150
	 *
151
	 * @param mixed $var
152
	 * @return string
153
	 */
154
    public function dump_it($var) : string
155
    {
156
		$adapter = '\alkemann\h2l\internals\debug\adapters\\'. $this->options['mode'];
157
        if (is_array($var))
158
            return $adapter::dump_array($var, $this);
159
        elseif (is_object($var))
160
            return $adapter::dump_object($var, $this);
161
        else
162
            return $adapter::dump_other($var);
163
    }
164
165
	/**
166
	 * Create an array that describes the location of the debug call
167
	 *
168
	 * @param string $trace
169
	 * @return array
170
	 */
171
    public function location($trace) : array
172
    {
173
        $root = substr($_SERVER['DOCUMENT_ROOT'], 0 , strlen(static::$defaults['docroot']) * -1);
174
        $file = implode('/', array_diff(explode('/', $trace[0]['file']), explode('/', $root)));
175
        $ret = array(
176
            'file' => $file,
177
            'line' => $trace[0]['line']
178
        );
179
        if (isset($trace[1]['function'])) $ret['function'] = $trace[1]['function'];
180
        if (isset($trace[1]['class'])) $ret['class'] = $trace[1]['class'];
181
        return $ret;
182
    }
183
184
    public function trace() : array
185
    {
186
        $root = substr($_SERVER['DOCUMENT_ROOT'], 0 , strlen(static::$defaults['docroot']) * -1);
187
        $trace = debug_backtrace();
188
        array_unshift($trace, array());
189
        $arr = array();
190
        foreach ($trace as $k => $one) {
191
            $arr[$k] = array();
192
            if (isset($one['file'])) {
193
                $file = implode('/', array_diff(explode('/', $one['file']), explode('/', $root)));
194
                $arr[$k]['file'] = $file;
195
            }
196
            if (isset($one['line'])) $arr[$k]['line'] = $one['line'];
197
            if (isset($one['class'])) $arr[$k-1]['class'] = $one['class'];
198
            if (isset($one['function'])) $arr[$k-1]['function'] = $one['function'];
199
        }
200
        array_shift($arr);
201
        array_shift($arr);
202
        return $arr;
203
    }
204
205
    public function api($var) : array
206
    {
207
        if (is_object($var)) {
208
            $class = get_class($var);
209
            $obj = $var;
210
        } else {
211
            if (!class_exists($var)) {
212
                throw new \Exception('Class ['.$var.'] doesn\'t exist');
213
            }
214
            $class = $var;
215
            try {
216
                $obj = new $class();
217
            } catch (\Exception $e) {
218
                throw new \Exception('Debug::api could not instantiate ['.$var.'], send it an object.');
219
            }
220
        }
221
        $reflection = new \ReflectionObject($obj);
222
        $properties = array();
223
        foreach (array(
224
            'public' => \ReflectionProperty::IS_PUBLIC,
225
            'protected' => \ReflectionProperty::IS_PROTECTED,
226
            'private' => \ReflectionProperty::IS_PRIVATE
227
            ) as $access => $rule) {
228
                $vars = $reflection->getProperties($rule);
229
                foreach ($vars as $refProp) {
230
                    $property = $refProp->getName();
231
                    $refProp->setAccessible(true);
232
                    $value = $refProp->getValue($obj);
233
                    $type = gettype($value);
234
                    if (is_object($value)) {
235
                        $value = get_class($value);
236
                    } elseif (is_array($value)) {
237
                        $value = 'array['.count($value).']';
238
                    }
239
240
                    $properties[$access][$property] = compact('value', 'type');
241
                }
242
        }
243
        $constants = $reflection->getConstants();
244
245
        $methods = array();
246
247
        foreach (array(
248
            'public' => \ReflectionMethod::IS_PUBLIC,
249
            'protected' => \ReflectionMethod::IS_PROTECTED,
250
            'private' => \ReflectionMethod::IS_PRIVATE
251
            ) as $access => $rule) {
252
                $refMethods = $reflection->getMethods($rule);
253
                foreach ($refMethods as $refMethod) {
254
                    $refParams = $refMethod->getParameters();
255
                    $params = array();
256
                    foreach ($refParams as $refParam) {
257
                        $params[] = $refParam->getName();
258
                    }/*
259
                    $required = $refMethod->getNumberOfRequiredParameters();
260
                    $requiredParams = array();
261
                    for ($i=0;$i<$required;$i++) {
262
                        $requiredParams[] = array_shift($params);
263
                    }*/
264
                    $method_name = $refMethod->getName();
265
266
                    $string = $access .' function '.$method_name.'(';
267
                    $paramString = '';
268
                    foreach ($params as $p) {
269
                        $paramString .= '$'.$p.', ';
270
                    }
271
                    $paramString  = substr($paramString,0,-2);
272
                    $string .= $paramString;
273
                    $string .= ')';
274
275
                    $comment = $refMethod->getDocComment();
276
                    $comment = trim(preg_replace('/^(\s*\/\*\*|\s*\*{1,2}\/|\s*\* ?)/m', '', $comment));
277
                    $comment = str_replace("\r\n", "\n", $comment);
278
                    $commentParts = explode('@', $comment);
279
                    $description = array_shift($commentParts);
280
                    $tags = array();
281
                    foreach ($commentParts as $part) {
282
                        $tagArr = explode(' ', $part, 2);
283
                        if ($tagArr[0] == 'param') {
284
                            $paramArr = preg_split("/[\s\t]+/", $tagArr[1]);
285
                            $type = array_shift($paramArr);
286
                            if (empty($type)) $type = array_shift($paramArr);
287
                            $name = array_shift($paramArr);
288
                            $info = implode(' ', $paramArr);
289
                            $tags['param'][$name] = compact('type', 'info');
290
                        } else {
291
                            $tags[$tagArr[0]] = isset($tagArr[1])?$tagArr[1]:'';
292
                        }
293
                    }
294
295
296
                    $methods[$access][$string] = compact('description', 'tags');
297
                }
298
        }
299
300
        return compact('properties', 'constants' ,'methods');
301
    }
302
303
}
304