Skip to content

Commit

Permalink
Merge pull request #3180 from Majkl578/replace-common-debug
Browse files Browse the repository at this point in the history
Import simplified version of Common\Util\Debug for var dumping purposes
  • Loading branch information
morozov authored Jun 7, 2018
2 parents 8338e8a + 30d377a commit fafac8d
Show file tree
Hide file tree
Showing 7 changed files with 365 additions and 7 deletions.
9 changes: 2 additions & 7 deletions lib/Doctrine/DBAL/Tools/Console/Command/RunSqlCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,13 @@

namespace Doctrine\DBAL\Tools\Console\Command;

use Doctrine\DBAL\Tools\Dumper;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Input\InputOption;
use function is_numeric;
use function ob_get_clean;
use function ob_start;
use function stripos;

/**
Expand Down Expand Up @@ -84,10 +83,6 @@ protected function execute(InputInterface $input, OutputInterface $output)
$resultSet = $conn->executeUpdate($sql);
}

ob_start();
\Doctrine\Common\Util\Debug::dump($resultSet, (int) $depth);
$message = ob_get_clean();

$output->write($message);
$output->write(Dumper::dump($resultSet, (int) $depth));
}
}
177 changes: 177 additions & 0 deletions lib/Doctrine/DBAL/Tools/Dumper.php
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);
}
}
4 changes: 4 additions & 0 deletions phpcs.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,8 @@
<rule ref="Generic.NamingConventions.UpperCaseConstantName.ClassConstantNotUpperCase">
<exclude-pattern>lib/Doctrine/DBAL/Events.php</exclude-pattern>
</rule>

<rule ref="SlevomatCodingStandard.Classes.UnusedPrivateElements.UnusedProperty">
<exclude-pattern>tests/Doctrine/Tests/DBAL/Tools/TestAsset/*</exclude-pattern>
</rule>
</ruleset>
137 changes: 137 additions & 0 deletions tests/Doctrine/Tests/DBAL/Tools/DumperTest.php
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,
],
],
];
}
}
15 changes: 15 additions & 0 deletions tests/Doctrine/Tests/DBAL/Tools/TestAsset/ChildClass.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

namespace Doctrine\Tests\DBAL\Tools\TestAsset;

final class ChildClass extends ParentClass
{
/** @var int */
public $childPublicAttribute = 4;

/** @var int */
protected $childProtectedAttribute = 5;

/** @var int */
private $childPrivateAttribute = 6;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

namespace Doctrine\Tests\DBAL\Tools\TestAsset;

final class ChildWithSameAttributesClass extends ParentClass
{
/** @var int */
public $parentPublicAttribute = 4;

/** @var int */
protected $parentProtectedAttribute = 5;

/** @var int */
private $parentPrivateAttribute = 6;
}
Loading

0 comments on commit fafac8d

Please sign in to comment.