mirror of
https://github.com/barryvdh/ReflectionDocBlock.git
synced 2026-08-18 01:57:13 +00:00
Fix serialization issue of InvalidTag
This commit is contained in:
@@ -4,7 +4,10 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace phpDocumentor\Reflection\DocBlock\Tags;
|
namespace phpDocumentor\Reflection\DocBlock\Tags;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
use phpDocumentor\Reflection\DocBlock\Tag;
|
use phpDocumentor\Reflection\DocBlock\Tag;
|
||||||
|
use ReflectionClass;
|
||||||
|
use ReflectionFunction;
|
||||||
use Throwable;
|
use Throwable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -56,12 +59,54 @@ final class InvalidTag implements Tag
|
|||||||
|
|
||||||
public function withError(Throwable $exception) : self
|
public function withError(Throwable $exception) : self
|
||||||
{
|
{
|
||||||
|
$this->flattenExceptionBacktrace($exception);
|
||||||
$tag = new self($this->name, $this->body);
|
$tag = new self($this->name, $this->body);
|
||||||
$tag->throwable = $exception;
|
$tag->throwable = $exception;
|
||||||
|
|
||||||
return $tag;
|
return $tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all complex types from backtrace
|
||||||
|
*
|
||||||
|
* Not all objects are serializable. So we need to remove them from the
|
||||||
|
* stored exception to be sure that we do not break existing library usage.
|
||||||
|
*/
|
||||||
|
private function flattenExceptionBacktrace(Throwable $exception) : void
|
||||||
|
{
|
||||||
|
$traceProperty = (new ReflectionClass('Exception'))->getProperty('trace');
|
||||||
|
$traceProperty->setAccessible(true);
|
||||||
|
|
||||||
|
$flatten = static function (&$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));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
do {
|
||||||
|
$trace = array_map(
|
||||||
|
static function($call) use ($flatten) {
|
||||||
|
array_walk_recursive($call['args'], $flatten);
|
||||||
|
|
||||||
|
return $call;
|
||||||
|
},
|
||||||
|
$exception->getTrace()
|
||||||
|
);
|
||||||
|
$traceProperty->setValue($exception, $trace);
|
||||||
|
} while ($exception = $exception->getPrevious());
|
||||||
|
|
||||||
|
$traceProperty->setAccessible(false);
|
||||||
|
}
|
||||||
|
|
||||||
public function render(?Formatter $formatter = null) : string
|
public function render(?Formatter $formatter = null) : string
|
||||||
{
|
{
|
||||||
if ($formatter === null) {
|
if ($formatter === null) {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
|||||||
namespace phpDocumentor\Reflection\DocBlock\Tags;
|
namespace phpDocumentor\Reflection\DocBlock\Tags;
|
||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
|
use InvalidArgumentException;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -28,6 +29,7 @@ final class InvalidTagTest extends TestCase
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @covers ::withError
|
* @covers ::withError
|
||||||
|
* @covers ::__toString
|
||||||
*/
|
*/
|
||||||
public function testCreationWithError() : void
|
public function testCreationWithError() : void
|
||||||
{
|
{
|
||||||
@@ -36,6 +38,59 @@ final class InvalidTagTest extends TestCase
|
|||||||
|
|
||||||
self::assertSame('name', $tag->getName());
|
self::assertSame('name', $tag->getName());
|
||||||
self::assertSame('@name Body', $tag->render());
|
self::assertSame('@name Body', $tag->render());
|
||||||
|
self::assertSame('Body', (string)$tag);
|
||||||
self::assertSame($exception, $tag->getException());
|
self::assertSame($exception, $tag->getException());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testCreationWithErrorContainingClosure() : void
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->throwExceptionFromClosureWithClosureArgument();
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$parentException = new Exception('test', 0, $e);
|
||||||
|
$tag = InvalidTag::create('Body', 'name')->withError($parentException);
|
||||||
|
self::assertSame('name', $tag->getName());
|
||||||
|
self::assertSame('@name Body', $tag->render());
|
||||||
|
self::assertSame($parentException, $tag->getException());
|
||||||
|
self::assertStringStartsWith('(Closure at', $tag->getException()->getPrevious()->getTrace()[0]['args'][0]);
|
||||||
|
self::assertStringContainsString(__FILE__, $tag->getException()->getPrevious()->getTrace()[0]['args'][0]);
|
||||||
|
self::assertEquals($parentException, unserialize(serialize($parentException)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function throwExceptionFromClosureWithClosureArgument()
|
||||||
|
{
|
||||||
|
$function = function() {
|
||||||
|
throw new InvalidArgumentException();
|
||||||
|
};
|
||||||
|
|
||||||
|
$function($function);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCreationWithErrorContainingResource()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->throwExceptionWithResourceArgument();
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$parentException = new Exception('test', 0, $e);
|
||||||
|
$tag = InvalidTag::create('Body', 'name')->withError($parentException);
|
||||||
|
self::assertSame('name', $tag->getName());
|
||||||
|
self::assertSame('@name Body', $tag->render());
|
||||||
|
self::assertSame($parentException, $tag->getException());
|
||||||
|
self::assertStringStartsWith(
|
||||||
|
'resource(stream)',
|
||||||
|
$tag->getException()->getPrevious()->getTrace()[0]['args'][0])
|
||||||
|
;
|
||||||
|
self::assertEquals($parentException, unserialize(serialize($parentException)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function throwExceptionWithResourceArgument()
|
||||||
|
{
|
||||||
|
$function = function() {
|
||||||
|
throw new InvalidArgumentException();
|
||||||
|
};
|
||||||
|
|
||||||
|
$function(fopen(__FILE__, 'r'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user