Skip to content

Commit d870572

Browse files
authored
Merge pull request #250 from phpDocumentor/fix/modified-backtrace-arguments
Fix issue with modified backtrace
2 parents 584a54c + 6ff90eb commit d870572

File tree

3 files changed

+110
-20
lines changed

3 files changed

+110
-20
lines changed

phpcs.xml.dist

+4
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,8 @@
1515
<rule ref="SlevomatCodingStandard.Classes.SuperfluousAbstractClassNaming.SuperfluousPrefix">
1616
<exclude-pattern>*/src/*/Abstract*.php</exclude-pattern>
1717
</rule>
18+
19+
<rule ref="SlevomatCodingStandard.Classes.UnusedPrivateElements.UnusedMethod">
20+
<exclude-pattern>*/src/DocBlock/Tags/InvalidTag.php</exclude-pattern>
21+
</rule>
1822
</ruleset>

src/DocBlock/Tags/InvalidTag.php

+31-20
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@
88
use Exception;
99
use phpDocumentor\Reflection\DocBlock\Tag;
1010
use ReflectionClass;
11+
use ReflectionException;
1112
use ReflectionFunction;
1213
use Throwable;
1314
use function array_map;
14-
use function array_walk_recursive;
1515
use function get_class;
1616
use function get_resource_type;
17+
use function is_array;
1718
use function is_object;
1819
use function is_resource;
1920
use function sprintf;
@@ -80,29 +81,12 @@ private function flattenExceptionBacktrace(Throwable $exception) : void
8081
$traceProperty = (new ReflectionClass(Exception::class))->getProperty('trace');
8182
$traceProperty->setAccessible(true);
8283

83-
$flatten =
84-
/** @param mixed $value */
85-
static function (&$value) : void {
86-
if ($value instanceof Closure) {
87-
$closureReflection = new ReflectionFunction($value);
88-
$value = sprintf(
89-
'(Closure at %s:%s)',
90-
$closureReflection->getFileName(),
91-
$closureReflection->getStartLine()
92-
);
93-
} elseif (is_object($value)) {
94-
$value = sprintf('object(%s)', get_class($value));
95-
} elseif (is_resource($value)) {
96-
$value = sprintf('resource(%s)', get_resource_type($value));
97-
}
98-
};
99-
10084
do {
10185
$trace = $exception->getTrace();
10286
if (isset($trace[0]['args'])) {
10387
$trace = array_map(
104-
static function (array $call) use ($flatten) : array {
105-
array_walk_recursive($call['args'], $flatten);
88+
function (array $call) : array {
89+
$call['args'] = array_map([$this, 'flattenArguments'], $call['args']);
10690

10791
return $call;
10892
},
@@ -117,6 +101,33 @@ static function (array $call) use ($flatten) : array {
117101
$traceProperty->setAccessible(false);
118102
}
119103

104+
/**
105+
* @param mixed $value
106+
*
107+
* @return mixed
108+
*
109+
* @throws ReflectionException
110+
*/
111+
private function flattenArguments($value)
112+
{
113+
if ($value instanceof Closure) {
114+
$closureReflection = new ReflectionFunction($value);
115+
$value = sprintf(
116+
'(Closure at %s:%s)',
117+
$closureReflection->getFileName(),
118+
$closureReflection->getStartLine()
119+
);
120+
} elseif (is_object($value)) {
121+
$value = sprintf('object(%s)', get_class($value));
122+
} elseif (is_resource($value)) {
123+
$value = sprintf('resource(%s)', get_resource_type($value));
124+
} elseif (is_array($value)) {
125+
$value = array_map([$this, 'flattenArguments'], $value);
126+
}
127+
128+
return $value;
129+
}
130+
120131
public function render(?Formatter $formatter = null) : string
121132
{
122133
if ($formatter === null) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace phpDocumentor\Reflection;
6+
7+
8+
use PHPUnit\Framework\TestCase;
9+
10+
/**
11+
* @coversNothing
12+
*/
13+
class ModifyBackTraceSafeTest extends TestCase
14+
{
15+
public function testBackTraceModificationDoesNotImpactFunctionArguments()
16+
{
17+
$traverser = new Traverser();
18+
$node1 = new Node();
19+
$node1->children[] = new Node();
20+
$node1->children[] = new Node();
21+
22+
$traverser->traverse([new Node(), $node1]);
23+
}
24+
}
25+
26+
27+
class Node {
28+
public $children = [];
29+
}
30+
31+
class Traverser
32+
{
33+
public function traverse(array $nodes)
34+
{
35+
$this->traverseArray($nodes);
36+
}
37+
38+
public function traverseArray(array $nodes): array
39+
{
40+
$doNodes = [];
41+
42+
foreach ($nodes as &$node) {
43+
$node = $this->callback($node);
44+
$node = $this->traverseNode($node);
45+
46+
$doNodes[] = $node;
47+
}
48+
49+
return $doNodes;
50+
}
51+
52+
public function callback(Node $class) : Node
53+
{
54+
$docblock = <<<DOCBLOCK
55+
/**
56+
* @see sql.php
57+
*/
58+
DOCBLOCK;
59+
60+
$factor = DocBlockFactory::createInstance();
61+
62+
$factor->create($docblock);
63+
64+
return $class;
65+
}
66+
67+
private function traverseNode(Node $node) : Node
68+
{
69+
if ($node->children) {
70+
$this->traverseArray($node->children);
71+
}
72+
73+
return $node;
74+
}
75+
}

0 commit comments

Comments
 (0)