/** * Formats a stack trace based on the supplied options. * * ### Options * * - `depth` - The number of stack frames to return. Defaults to 999 * - `format` - The format you want the return. Defaults to the currently selected format. If * format is 'array' or 'points' the return will be an array. * - `args` - Should arguments for functions be shown? If true, the arguments for each method call * will be displayed. * - `start` - The stack frame to start generating a trace from. Defaults to 0 * * @param array|\Exception $backtrace Trace as array or an exception object. * @param array $options Format for outputting stack trace. * @return mixed Formatted stack trace. * @link http://book.cakephp.org/3.0/en/development/debugging.html#generating-stack-traces */ public static function formatTrace($backtrace, $options = []) { if ($backtrace instanceof Exception) { $backtrace = $backtrace->getTrace(); } $self = Debugger::getInstance(); $defaults = ['depth' => 999, 'format' => $self->_outputFormat, 'args' => false, 'start' => 0, 'scope' => null, 'exclude' => ['call_user_func_array', 'trigger_error']]; $options = ArrayObject::fromArray($defaults)->merge($options); $count = count($backtrace); $back = []; $_trace = ['line' => '??', 'file' => '[internal]', 'class' => null, 'function' => '[main]']; for ($i = $options['start']; $i < $count && $i < $options['depth']; $i++) { $trace = $backtrace[$i] + ['file' => '[internal]', 'line' => '??']; $signature = $reference = '[main]'; if (isset($backtrace[$i + 1])) { $next = $backtrace[$i + 1] + $_trace; $signature = $reference = $next['function']; if (!empty($next['class'])) { $signature = $next['class'] . '::' . $next['function']; $reference = $signature . '('; if ($options['args'] && isset($next['args'])) { $args = []; foreach ($next['args'] as $arg) { $args[] = Debugger::exportVar($arg); } $reference .= implode(', ', $args); } $reference .= ')'; } } if (in_array($signature, $options['exclude'])) { continue; } if ($options['format'] === 'points' && $trace['file'] !== '[internal]') { $back[] = ['file' => $trace['file'], 'line' => $trace['line']]; } elseif ($options['format'] === 'array') { $back[] = $trace; } else { if (isset($self->_templates[$options['format']]['traceLine'])) { $tpl = $self->_templates[$options['format']]['traceLine']; } else { $tpl = $self->_templates['base']['traceLine']; } $trace['path'] = static::trimPath($trace['file']); $trace['reference'] = $reference; unset($trace['object'], $trace['args']); (new Text($tpl))->insert($trace); $back[] = (new Text($tpl))->insert($trace); } } if ($options['format'] === 'array' || $options['format'] === 'points') { return $back; } return implode("\n", $back); }
/** * Test array merge * * @return void */ public function testMerge() { $a = new ArrayObject('foo'); $a->merge(['bar']); $this->assertEquals(['foo', 'bar'], $a->toStandardArray()); $a = new ArrayObject('foo'); $a->merge(['user' => 'bob', 'no-bar'], 'bar'); $this->assertEquals(['foo', 'user' => 'bob', 'no-bar', 'bar'], $a->toStandardArray()); $a = ArrayObject::fromArray(['foo', 'foo2']); $b = ['bar', 'bar2']; $a->merge($b); $expected = ['foo', 'foo2', 'bar', 'bar2']; $this->assertEquals($expected, $a->toStandardArray()); $a = ArrayObject::fromArray(['foo' => 'bar', 'bar' => 'foo']); $b = ['foo' => 'no-bar', 'bar' => 'no-foo']; $a->merge($b); $expected = ['foo' => 'no-bar', 'bar' => 'no-foo']; $this->assertEquals($expected, $a->toStandardArray()); $a = ArrayObject::fromArray(['users' => ['bob', 'jim']]); $b = ['users' => ['lisa', 'tina']]; $a->merge($b); $expected = ['users' => ['bob', 'jim', 'lisa', 'tina']]; $this->assertEquals($expected, $a->toStandardArray()); $a = ArrayObject::fromArray(['users' => ['jim', 'bob']]); $b = ['users' => 'none']; $a->merge($b); $expected = ['users' => 'none']; $this->assertEquals($expected, $a->toStandardArray()); $a = ArrayObject::fromArray(['users' => ['lisa' => ['id' => 5, 'pw' => 'secret']], 'coretyson']); $b = ['users' => ['lisa' => ['pw' => 'new-pass', 'age' => 23]], 'ice-cream']; $a->merge($b); $expected = ['users' => ['lisa' => ['id' => 5, 'pw' => 'new-pass', 'age' => 23]], 'cakephp', 'ice-cream']; $this->assertEquals($expected, $a->toStandardArray()); $c = ['users' => ['lisa' => ['pw' => 'you-will-never-guess', 'age' => 25, 'pet' => 'dog']], 'chocolate']; $expected = ['users' => ['lisa' => ['id' => 5, 'pw' => 'you-will-never-guess', 'age' => 25, 'pet' => 'dog']], 'cakephp', 'ice-cream', 'chocolate']; $a->merge($b, $c); $this->assertEquals($expected, $a->toStandardArray()); $a->merge($b, [], $c); $this->assertEquals($expected, $a->toStandardArray()); $a = ArrayObject::fromArray(['Tree', 'CounterCache', 'Upload' => ['folder' => 'products', 'fields' => ['image_1_id', 'image_2_id', 'image_3_id', 'image_4_id', 'image_5_id']]]); $b = ['Cacheable' => ['enabled' => false], 'Limit', 'Bindable', 'Validator', 'Transactional']; $a->merge($b); $expected = ['Tree', 'CounterCache', 'Upload' => ['folder' => 'products', 'fields' => ['image_1_id', 'image_2_id', 'image_3_id', 'image_4_id', 'image_5_id']], 'Cacheable' => ['enabled' => false], 'Limit', 'Bindable', 'Validator', 'Transactional']; $this->assertEquals($expected, $a->toStandardArray()); }