-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Import simplified version of Common\Util\Debug for var dumping purposes
- Loading branch information
Showing
6 changed files
with
346 additions
and
7 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,177 @@ | ||
<?php | ||
|
||
namespace Doctrine\DBAL\Tools; | ||
|
||
use ArrayIterator; | ||
use ArrayObject; | ||
use DateTimeInterface; | ||
use Doctrine\Common\Collections\Collection; | ||
use Doctrine\Common\Persistence\Proxy; | ||
use stdClass; | ||
use function array_keys; | ||
use function class_exists; | ||
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_get_clean; | ||
use function ob_start; | ||
use function strip_tags; | ||
use function strrpos; | ||
use function substr; | ||
use function var_dump; | ||
|
||
/** | ||
* Static class used to dump the variable to be used on output. | ||
* Simplified port of Util\Debug from doctrine/common. | ||
* | ||
* @internal | ||
*/ | ||
final class Dumper | ||
{ | ||
/** | ||
* Private constructor (prevents instantiation). | ||
*/ | ||
private function __construct() | ||
{ | ||
} | ||
|
||
/** | ||
* Returns 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 !== true) { | ||
ini_set('html_errors', true); | ||
} | ||
|
||
if (extension_loaded('xdebug')) { | ||
ini_set('xdebug.var_display_max_depth', $maxDepth); | ||
} | ||
|
||
$var = self::export($var, $maxDepth); | ||
|
||
ob_start(); | ||
var_dump($var); | ||
|
||
try { | ||
return strip_tags(html_entity_decode(ob_get_clean())); | ||
} finally { | ||
ini_set('html_errors', $html); | ||
} | ||
} | ||
|
||
/** | ||
* @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 === 0) { | ||
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__ = self::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", $key); | ||
$name = end($aux); | ||
if ($aux[0] === '') { | ||
$name .= ':' . ($aux[1] === '*' ? 'protected' : $aux[1] . ':private'); | ||
} | ||
$return->$name = self::export($clone[$key], $maxDepth - 1); | ||
; | ||
} | ||
|
||
return $return; | ||
} | ||
|
||
/** | ||
* @param object $object | ||
*/ | ||
private static function getClass($object) : string | ||
{ | ||
$class = get_class($object); | ||
|
||
if (! class_exists(Proxy::class)) { | ||
return $class; | ||
} | ||
|
||
$pos = strrpos($class, '\\' . Proxy::MARKER . '\\'); | ||
|
||
if ($pos === false) { | ||
return $class; | ||
} | ||
|
||
return substr($class, $pos + Proxy::MARKER_LENGTH + 2); | ||
} | ||
} |
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,137 @@ | ||
<?php | ||
|
||
namespace Doctrine\Tests\DBAL\Tools; | ||
|
||
use ArrayIterator; | ||
use ArrayObject; | ||
use DateTime; | ||
use DateTimeImmutable; | ||
use DateTimeZone; | ||
use Doctrine\DBAL\Tools\Dumper; | ||
use Doctrine\Tests\DbalTestCase; | ||
use function print_r; | ||
use function strpos; | ||
use function substr; | ||
|
||
class DumperTest extends DbalTestCase | ||
{ | ||
public function testExportObject() | ||
{ | ||
$obj = new \stdClass(); | ||
$obj->foo = 'bar'; | ||
$obj->bar = 1234; | ||
|
||
$var = Dumper::export($obj, 2); | ||
self::assertEquals('stdClass', $var->__CLASS__); | ||
} | ||
|
||
public function testExportObjectWithReference() | ||
{ | ||
$foo = 'bar'; | ||
$bar = ['foo' => & $foo]; | ||
$baz = (object) $bar; | ||
|
||
$var = Dumper::export($baz, 2); | ||
$baz->foo = 'tab'; | ||
|
||
self::assertEquals('bar', $var->foo); | ||
self::assertEquals('tab', $bar['foo']); | ||
} | ||
|
||
public function testExportArray() | ||
{ | ||
$array = ['a' => 'b', 'b' => ['c', 'd' => ['e', 'f']]]; | ||
$var = Dumper::export($array, 2); | ||
$expected = $array; | ||
$expected['b']['d'] = 'Array(2)'; | ||
self::assertEquals($expected, $var); | ||
} | ||
|
||
public function testExportDateTime() | ||
{ | ||
$obj = new DateTime('2010-10-10 10:10:10', new DateTimeZone('UTC')); | ||
|
||
$var = Dumper::export($obj, 2); | ||
self::assertEquals('DateTime', $var->__CLASS__); | ||
self::assertEquals('2010-10-10T10:10:10+00:00', $var->date); | ||
} | ||
|
||
public function testExportDateTimeImmutable() | ||
{ | ||
$obj = new DateTimeImmutable('2010-10-10 10:10:10', new DateTimeZone('UTC')); | ||
|
||
$var = Dumper::export($obj, 2); | ||
self::assertEquals('DateTimeImmutable', $var->__CLASS__); | ||
self::assertEquals('2010-10-10T10:10:10+00:00', $var->date); | ||
} | ||
|
||
public function testExportDateTimeZone() | ||
{ | ||
$obj = new DateTimeImmutable('2010-10-10 12:34:56', new DateTimeZone('Europe/Rome')); | ||
|
||
$var = Dumper::export($obj, 2); | ||
self::assertEquals('DateTimeImmutable', $var->__CLASS__); | ||
self::assertEquals('2010-10-10T12:34:56+02:00', $var->date); | ||
} | ||
|
||
public function testExportArrayTraversable() | ||
{ | ||
$obj = new ArrayObject(['foobar']); | ||
|
||
$var = Dumper::export($obj, 2); | ||
self::assertContains('foobar', $var->__STORAGE__); | ||
|
||
$it = new ArrayIterator(['foobar']); | ||
|
||
$var = Dumper::export($it, 5); | ||
self::assertContains('foobar', $var->__STORAGE__); | ||
} | ||
|
||
/** | ||
* @param string[] $expected | ||
* | ||
* @dataProvider provideAttributesCases | ||
*/ | ||
public function testExportParentAttributes(TestAsset\ParentClass $class, array $expected) | ||
{ | ||
$print_r_class = print_r($class, true); | ||
$print_r_expected = print_r($expected, true); | ||
|
||
$print_r_class = substr($print_r_class, strpos($print_r_class, '(')); | ||
$print_r_expected = substr($print_r_expected, strpos($print_r_expected, '(')); | ||
|
||
self::assertSame($print_r_class, $print_r_expected); | ||
|
||
$var = Dumper::export($class, 3); | ||
$var = (array) $var; | ||
unset($var['__CLASS__']); | ||
|
||
self::assertSame($expected, $var); | ||
} | ||
|
||
public function provideAttributesCases() | ||
{ | ||
return [ | ||
'different-attributes' => [ | ||
new TestAsset\ChildClass(), | ||
[ | ||
'childPublicAttribute' => 4, | ||
'childProtectedAttribute:protected' => 5, | ||
'childPrivateAttribute:Doctrine\Tests\DBAL\Tools\TestAsset\ChildClass:private' => 6, | ||
'parentPublicAttribute' => 1, | ||
'parentProtectedAttribute:protected' => 2, | ||
'parentPrivateAttribute:Doctrine\Tests\DBAL\Tools\TestAsset\ParentClass:private' => 3, | ||
], | ||
], | ||
'same-attributes' => [ | ||
new TestAsset\ChildWithSameAttributesClass(), | ||
[ | ||
'parentPublicAttribute' => 4, | ||
'parentProtectedAttribute:protected' => 5, | ||
'parentPrivateAttribute:Doctrine\Tests\DBAL\Tools\TestAsset\ChildWithSameAttributesClass:private' => 6, | ||
'parentPrivateAttribute:Doctrine\Tests\DBAL\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,10 @@ | ||
<?php | ||
|
||
namespace Doctrine\Tests\DBAL\Tools\TestAsset; | ||
|
||
final class ChildClass extends ParentClass | ||
{ | ||
public $childPublicAttribute = 4; | ||
protected $childProtectedAttribute = 5; | ||
private $childPrivateAttribute = 6; | ||
} |
10 changes: 10 additions & 0 deletions
10
tests/Doctrine/Tests/DBAL/Tools/TestAsset/ChildWithSameAttributesClass.php
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,10 @@ | ||
<?php | ||
|
||
namespace Doctrine\Tests\DBAL\Tools\TestAsset; | ||
|
||
final class ChildWithSameAttributesClass extends ParentClass | ||
{ | ||
public $parentPublicAttribute = 4; | ||
protected $parentProtectedAttribute = 5; | ||
private $parentPrivateAttribute = 6; | ||
} |
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,10 @@ | ||
<?php | ||
|
||
namespace Doctrine\Tests\DBAL\Tools\TestAsset; | ||
|
||
abstract class ParentClass | ||
{ | ||
public $parentPublicAttribute = 1; | ||
protected $parentProtectedAttribute = 2; | ||
private $parentPrivateAttribute = 3; | ||
} |