Skip to content

Commit

Permalink
Merge pull request #250 from phpDocumentor/fix/modified-backtrace-arg…
Browse files Browse the repository at this point in the history
…uments

Fix issue with modified backtrace
  • Loading branch information
jaapio authored Aug 15, 2020
2 parents 584a54c + 6ff90eb commit d870572
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 20 deletions.
4 changes: 4 additions & 0 deletions phpcs.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,8 @@
<rule ref="SlevomatCodingStandard.Classes.SuperfluousAbstractClassNaming.SuperfluousPrefix">
<exclude-pattern>*/src/*/Abstract*.php</exclude-pattern>
</rule>

<rule ref="SlevomatCodingStandard.Classes.UnusedPrivateElements.UnusedMethod">
<exclude-pattern>*/src/DocBlock/Tags/InvalidTag.php</exclude-pattern>
</rule>
</ruleset>
51 changes: 31 additions & 20 deletions src/DocBlock/Tags/InvalidTag.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@
use Exception;
use phpDocumentor\Reflection\DocBlock\Tag;
use ReflectionClass;
use ReflectionException;
use ReflectionFunction;
use Throwable;
use function array_map;
use function array_walk_recursive;
use function get_class;
use function get_resource_type;
use function is_array;
use function is_object;
use function is_resource;
use function sprintf;
Expand Down Expand Up @@ -80,29 +81,12 @@ private function flattenExceptionBacktrace(Throwable $exception) : void
$traceProperty = (new ReflectionClass(Exception::class))->getProperty('trace');
$traceProperty->setAccessible(true);

$flatten =
/** @param mixed $value */
static function (&$value) : void {
if ($value instanceof Closure) {
$closureReflection = new ReflectionFunction($value);
$value = sprintf(
'(Closure at %s:%s)',
$closureReflection->getFileName(),
$closureReflection->getStartLine()
);
} elseif (is_object($value)) {
$value = sprintf('object(%s)', get_class($value));
} elseif (is_resource($value)) {
$value = sprintf('resource(%s)', get_resource_type($value));
}
};

do {
$trace = $exception->getTrace();
if (isset($trace[0]['args'])) {
$trace = array_map(
static function (array $call) use ($flatten) : array {
array_walk_recursive($call['args'], $flatten);
function (array $call) : array {
$call['args'] = array_map([$this, 'flattenArguments'], $call['args']);

return $call;
},
Expand All @@ -117,6 +101,33 @@ static function (array $call) use ($flatten) : array {
$traceProperty->setAccessible(false);
}

/**
* @param mixed $value
*
* @return mixed
*
* @throws ReflectionException
*/
private function flattenArguments($value)
{
if ($value instanceof Closure) {
$closureReflection = new ReflectionFunction($value);
$value = sprintf(
'(Closure at %s:%s)',
$closureReflection->getFileName(),
$closureReflection->getStartLine()
);
} elseif (is_object($value)) {
$value = sprintf('object(%s)', get_class($value));
} elseif (is_resource($value)) {
$value = sprintf('resource(%s)', get_resource_type($value));
} elseif (is_array($value)) {
$value = array_map([$this, 'flattenArguments'], $value);
}

return $value;
}

public function render(?Formatter $formatter = null) : string
{
if ($formatter === null) {
Expand Down
75 changes: 75 additions & 0 deletions tests/integration/ModifyBackTraceSafeTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?php

declare(strict_types=1);

namespace phpDocumentor\Reflection;


use PHPUnit\Framework\TestCase;

/**
* @coversNothing
*/
class ModifyBackTraceSafeTest extends TestCase
{
public function testBackTraceModificationDoesNotImpactFunctionArguments()
{
$traverser = new Traverser();
$node1 = new Node();
$node1->children[] = new Node();
$node1->children[] = new Node();

$traverser->traverse([new Node(), $node1]);
}
}


class Node {
public $children = [];
}

class Traverser
{
public function traverse(array $nodes)
{
$this->traverseArray($nodes);
}

public function traverseArray(array $nodes): array
{
$doNodes = [];

foreach ($nodes as &$node) {
$node = $this->callback($node);
$node = $this->traverseNode($node);

$doNodes[] = $node;
}

return $doNodes;
}

public function callback(Node $class) : Node
{
$docblock = <<<DOCBLOCK
/**
* @see sql.php
*/
DOCBLOCK;

$factor = DocBlockFactory::createInstance();

$factor->create($docblock);

return $class;
}

private function traverseNode(Node $node) : Node
{
if ($node->children) {
$this->traverseArray($node->children);
}

return $node;
}
}

0 comments on commit d870572

Please sign in to comment.