-
Notifications
You must be signed in to change notification settings - Fork 2.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Copy Debug class from doctrine/common
This reduces our dependency to this shared library that now holds very little code we use. The class has not been copied verbatim: - Unused parameters and methods have been removed. - The class is final and internal. - Coding standards have been enforced, including enabling strict_types, which lead to casting a variable to string before feeding it to explode(). - A bug found by static analysis has been addressed, where an INI setting obtained with ini_get() was compared with true, which is never returned by that function.
- Loading branch information
Showing
9 changed files
with
368 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Doctrine\ORM\Tools; | ||
|
||
use ArrayIterator; | ||
use ArrayObject; | ||
use DateTimeInterface; | ||
use Doctrine\Common\Collections\Collection; | ||
use Doctrine\Common\Util\ClassUtils; | ||
use Doctrine\Persistence\Proxy; | ||
use stdClass; | ||
|
||
use function array_keys; | ||
use function count; | ||
use function end; | ||
use function explode; | ||
use function extension_loaded; | ||
use function get_class; | ||
use function html_entity_decode; | ||
use function ini_get; | ||
use function ini_set; | ||
use function is_array; | ||
use function is_object; | ||
use function ob_end_clean; | ||
use function ob_get_contents; | ||
use function ob_start; | ||
use function strip_tags; | ||
use function var_dump; | ||
|
||
/** | ||
* Static class containing most used debug methods. | ||
* | ||
* @internal | ||
* | ||
* @link www.doctrine-project.org | ||
*/ | ||
final class Debug | ||
{ | ||
/** | ||
* Private constructor (prevents instantiation). | ||
*/ | ||
private function __construct() | ||
{ | ||
} | ||
|
||
/** | ||
* Prints a dump of the public, protected and private properties of $var. | ||
* | ||
* @link https://xdebug.org/ | ||
* | ||
* @param mixed $var The variable to dump. | ||
* @param int $maxDepth The maximum nesting level for object properties. | ||
*/ | ||
public static function dump($var, int $maxDepth = 2): string | ||
{ | ||
$html = ini_get('html_errors'); | ||
|
||
if ($html !== '1') { | ||
ini_set('html_errors', 'on'); | ||
} | ||
|
||
if (extension_loaded('xdebug')) { | ||
ini_set('xdebug.var_display_max_depth', (string) $maxDepth); | ||
} | ||
|
||
$var = self::export($var, $maxDepth); | ||
|
||
ob_start(); | ||
var_dump($var); | ||
|
||
$dump = ob_get_contents(); | ||
|
||
ob_end_clean(); | ||
|
||
$dumpText = strip_tags(html_entity_decode($dump)); | ||
|
||
ini_set('html_errors', $html); | ||
|
||
return $dumpText; | ||
} | ||
|
||
/** | ||
* @param mixed $var | ||
* | ||
* @return mixed | ||
*/ | ||
public static function export($var, int $maxDepth) | ||
{ | ||
$return = null; | ||
$isObj = is_object($var); | ||
|
||
if ($var instanceof Collection) { | ||
$var = $var->toArray(); | ||
} | ||
|
||
if (! $maxDepth) { | ||
return is_object($var) ? get_class($var) | ||
: (is_array($var) ? 'Array(' . count($var) . ')' : $var); | ||
} | ||
|
||
if (is_array($var)) { | ||
$return = []; | ||
|
||
foreach ($var as $k => $v) { | ||
$return[$k] = self::export($v, $maxDepth - 1); | ||
} | ||
|
||
return $return; | ||
} | ||
|
||
if (! $isObj) { | ||
return $var; | ||
} | ||
|
||
$return = new stdClass(); | ||
if ($var instanceof DateTimeInterface) { | ||
$return->__CLASS__ = get_class($var); | ||
$return->date = $var->format('c'); | ||
$return->timezone = $var->getTimezone()->getName(); | ||
|
||
return $return; | ||
} | ||
|
||
$return->__CLASS__ = ClassUtils::getClass($var); | ||
|
||
if ($var instanceof Proxy) { | ||
$return->__IS_PROXY__ = true; | ||
$return->__PROXY_INITIALIZED__ = $var->__isInitialized(); | ||
} | ||
|
||
if ($var instanceof ArrayObject || $var instanceof ArrayIterator) { | ||
$return->__STORAGE__ = self::export($var->getArrayCopy(), $maxDepth - 1); | ||
} | ||
|
||
return self::fillReturnWithClassAttributes($var, $return, $maxDepth); | ||
} | ||
|
||
/** | ||
* Fill the $return variable with class attributes | ||
* Based on obj2array function from {@see https://secure.php.net/manual/en/function.get-object-vars.php#47075} | ||
* | ||
* @param object $var | ||
* | ||
* @return mixed | ||
*/ | ||
private static function fillReturnWithClassAttributes($var, stdClass $return, int $maxDepth) | ||
{ | ||
$clone = (array) $var; | ||
|
||
foreach (array_keys($clone) as $key) { | ||
$aux = explode("\0", (string) $key); | ||
$name = end($aux); | ||
if ($aux[0] === '') { | ||
$name .= ':' . ($aux[1] === '*' ? 'protected' : $aux[1] . ':private'); | ||
} | ||
|
||
$return->$name = self::export($clone[$key], $maxDepth - 1); | ||
} | ||
|
||
return $return; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Doctrine\Tests\ORM\Tools; | ||
|
||
use ArrayIterator; | ||
use ArrayObject; | ||
use DateTime; | ||
use DateTimeImmutable; | ||
use DateTimeZone; | ||
use Doctrine\ORM\Tools\Debug; | ||
use Doctrine\Tests\DoctrineTestCase; | ||
use stdClass; | ||
|
||
use function print_r; | ||
use function strpos; | ||
use function substr; | ||
|
||
class DebugTest extends DoctrineTestCase | ||
{ | ||
public function testExportObject(): void | ||
{ | ||
$obj = new stdClass(); | ||
$obj->foo = 'bar'; | ||
$obj->bar = 1234; | ||
|
||
$var = Debug::export($obj, 2); | ||
self::assertEquals('stdClass', $var->__CLASS__); | ||
} | ||
|
||
public function testExportObjectWithReference(): void | ||
{ | ||
$foo = 'bar'; | ||
$bar = ['foo' => & $foo]; | ||
$baz = (object) $bar; | ||
|
||
$var = Debug::export($baz, 2); | ||
$baz->foo = 'tab'; | ||
|
||
self::assertEquals('bar', $var->foo); | ||
self::assertEquals('tab', $bar['foo']); | ||
} | ||
|
||
public function testExportArray(): void | ||
{ | ||
$array = ['a' => 'b', 'b' => ['c', 'd' => ['e', 'f']]]; | ||
$var = Debug::export($array, 2); | ||
$expected = $array; | ||
$expected['b']['d'] = 'Array(2)'; | ||
self::assertEquals($expected, $var); | ||
} | ||
|
||
public function testExportDateTime(): void | ||
{ | ||
$obj = new DateTime('2010-10-10 10:10:10', new DateTimeZone('UTC')); | ||
|
||
$var = Debug::export($obj, 2); | ||
self::assertEquals('DateTime', $var->__CLASS__); | ||
self::assertEquals('2010-10-10T10:10:10+00:00', $var->date); | ||
} | ||
|
||
public function testExportDateTimeImmutable(): void | ||
{ | ||
$obj = new DateTimeImmutable('2010-10-10 10:10:10', new DateTimeZone('UTC')); | ||
|
||
$var = Debug::export($obj, 2); | ||
self::assertEquals('DateTimeImmutable', $var->__CLASS__); | ||
self::assertEquals('2010-10-10T10:10:10+00:00', $var->date); | ||
} | ||
|
||
public function testExportDateTimeZone(): void | ||
{ | ||
$obj = new DateTimeImmutable('2010-10-10 12:34:56', new DateTimeZone('Europe/Rome')); | ||
|
||
$var = Debug::export($obj, 2); | ||
self::assertEquals('DateTimeImmutable', $var->__CLASS__); | ||
self::assertEquals('2010-10-10T12:34:56+02:00', $var->date); | ||
} | ||
|
||
public function testExportArrayTraversable(): void | ||
{ | ||
$obj = new ArrayObject(['foobar']); | ||
|
||
$var = Debug::export($obj, 2); | ||
self::assertContains('foobar', $var->__STORAGE__); | ||
|
||
$it = new ArrayIterator(['foobar']); | ||
|
||
$var = Debug::export($it, 5); | ||
self::assertContains('foobar', $var->__STORAGE__); | ||
} | ||
|
||
/** | ||
* @param array<string, int> $expected | ||
* | ||
* @dataProvider provideAttributesCases | ||
* | ||
* @requires PHP < 8.1.0 | ||
*/ | ||
public function testExportParentAttributes(TestAsset\ParentClass $class, array $expected): void | ||
{ | ||
$actualRepresentation = print_r($class, true); | ||
$expectedRepresentation = print_r($expected, true); | ||
|
||
$actualRepresentation = substr($actualRepresentation, strpos($actualRepresentation, '(')); | ||
$expectedRepresentation = substr($expectedRepresentation, strpos($expectedRepresentation, '(')); | ||
|
||
self::assertSame($actualRepresentation, $expectedRepresentation); | ||
|
||
$var = Debug::export($class, 3); | ||
$var = (array) $var; | ||
unset($var['__CLASS__']); | ||
|
||
self::assertSame($expected, $var); | ||
} | ||
|
||
/** | ||
* @psalm-return array<string, array{TestAsset\ParentClass, mixed[]}> | ||
*/ | ||
public function provideAttributesCases(): iterable | ||
{ | ||
return [ | ||
'different-attributes' => [ | ||
new TestAsset\ChildClass(), | ||
[ | ||
'childPublicAttribute' => 4, | ||
'childProtectedAttribute:protected' => 5, | ||
'childPrivateAttribute:Doctrine\Tests\ORM\Tools\TestAsset\ChildClass:private' => 6, | ||
'parentPublicAttribute' => 1, | ||
'parentProtectedAttribute:protected' => 2, | ||
'parentPrivateAttribute:Doctrine\Tests\ORM\Tools\TestAsset\ParentClass:private' => 3, | ||
], | ||
], | ||
'same-attributes' => [ | ||
new TestAsset\ChildWithSameAttributesClass(), | ||
[ | ||
'parentPublicAttribute' => 4, | ||
'parentProtectedAttribute:protected' => 5, | ||
'parentPrivateAttribute:Doctrine\Tests\ORM\Tools\TestAsset\ChildWithSameAttributesClass:private' => 6, | ||
'parentPrivateAttribute:Doctrine\Tests\ORM\Tools\TestAsset\ParentClass:private' => 3, | ||
], | ||
], | ||
]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Doctrine\Tests\ORM\Tools\TestAsset; | ||
|
||
final class ChildClass extends ParentClass | ||
{ | ||
/** @var int */ | ||
public $childPublicAttribute = 4; | ||
/** @var int */ | ||
protected $childProtectedAttribute = 5; | ||
/** @var int */ | ||
private $childPrivateAttribute = 6; | ||
} |
Oops, something went wrong.