Dumper.php 4.43 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
<?php

namespace Doctrine\DBAL\Tools;

use ArrayIterator;
use ArrayObject;
use DateTimeInterface;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Persistence\Proxy;
use stdClass;
11

12
use function array_keys;
Sergei Morozov's avatar
Sergei Morozov committed
13
use function assert;
14 15 16 17 18 19 20 21 22 23
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_set;
use function is_array;
use function is_object;
Sergei Morozov's avatar
Sergei Morozov committed
24
use function is_string;
25 26 27
use function ob_get_clean;
use function ob_start;
use function strip_tags;
Sergei Morozov's avatar
Sergei Morozov committed
28
use function strlen;
29 30 31 32 33 34 35 36 37 38 39 40 41 42
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).
43 44
     *
     * @codeCoverageIgnore
45 46 47 48 49 50 51 52 53 54 55 56 57
     */
    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.
     */
58
    public static function dump($var, int $maxDepth = 2): string
59
    {
60 61
        $html = ini_set('html_errors', '1');
        assert(is_string($html));
62 63

        if (extension_loaded('xdebug')) {
64
            ini_set('xdebug.var_display_max_depth', (string) $maxDepth);
65 66 67 68 69 70 71 72
        }

        $var = self::export($var, $maxDepth);

        ob_start();
        var_dump($var);

        try {
Sergei Morozov's avatar
Sergei Morozov committed
73 74
            $output = ob_get_clean();
            assert(is_string($output));
75

Sergei Morozov's avatar
Sergei Morozov committed
76
            return strip_tags(html_entity_decode($output));
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
        } 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');
            }
Grégoire Paris's avatar
Grégoire Paris committed
156

157 158 159 160 161 162 163 164 165
            $return->$name = self::export($clone[$key], $maxDepth - 1);
        }

        return $return;
    }

    /**
     * @param object $object
     */
166
    private static function getClass($object): string
167 168 169 170 171 172 173 174 175 176 177 178 179
    {
        $class = get_class($object);

        if (! class_exists(Proxy::class)) {
            return $class;
        }

        $pos = strrpos($class, '\\' . Proxy::MARKER . '\\');

        if ($pos === false) {
            return $class;
        }

Sergei Morozov's avatar
Sergei Morozov committed
180
        return substr($class, $pos + strlen(Proxy::MARKER) + 2);
181 182
    }
}