1<?php 2 3namespace Doctrine\DBAL\Tools; 4 5use ArrayIterator; 6use ArrayObject; 7use DateTimeInterface; 8use Doctrine\Common\Collections\Collection; 9use Doctrine\Common\Persistence\Proxy; 10use stdClass; 11 12use function array_keys; 13use function assert; 14use function class_exists; 15use function count; 16use function end; 17use function explode; 18use function extension_loaded; 19use function get_class; 20use function html_entity_decode; 21use function ini_set; 22use function is_array; 23use function is_object; 24use function is_string; 25use function ob_get_clean; 26use function ob_start; 27use function strip_tags; 28use function strlen; 29use function strrpos; 30use function substr; 31use function var_dump; 32 33/** 34 * Static class used to dump the variable to be used on output. 35 * Simplified port of Util\Debug from doctrine/common. 36 * 37 * @internal 38 */ 39final class Dumper 40{ 41 /** 42 * Private constructor (prevents instantiation). 43 * 44 * @codeCoverageIgnore 45 */ 46 private function __construct() 47 { 48 } 49 50 /** 51 * Returns a dump of the public, protected and private properties of $var. 52 * 53 * @link https://xdebug.org/ 54 * 55 * @param mixed $var The variable to dump. 56 * @param int $maxDepth The maximum nesting level for object properties. 57 */ 58 public static function dump($var, int $maxDepth = 2): string 59 { 60 $html = ini_set('html_errors', '1'); 61 assert(is_string($html)); 62 63 if (extension_loaded('xdebug')) { 64 ini_set('xdebug.var_display_max_depth', (string) $maxDepth); 65 } 66 67 $var = self::export($var, $maxDepth); 68 69 ob_start(); 70 var_dump($var); 71 72 try { 73 $output = ob_get_clean(); 74 assert(is_string($output)); 75 76 return strip_tags(html_entity_decode($output)); 77 } finally { 78 ini_set('html_errors', $html); 79 } 80 } 81 82 /** 83 * @param mixed $var 84 * 85 * @return mixed 86 */ 87 public static function export($var, int $maxDepth) 88 { 89 $return = null; 90 $isObj = is_object($var); 91 92 if ($var instanceof Collection) { 93 $var = $var->toArray(); 94 } 95 96 if ($maxDepth === 0) { 97 return is_object($var) ? get_class($var) 98 : (is_array($var) ? 'Array(' . count($var) . ')' : $var); 99 } 100 101 if (is_array($var)) { 102 $return = []; 103 104 foreach ($var as $k => $v) { 105 $return[$k] = self::export($v, $maxDepth - 1); 106 } 107 108 return $return; 109 } 110 111 if (! $isObj) { 112 return $var; 113 } 114 115 $return = new stdClass(); 116 if ($var instanceof DateTimeInterface) { 117 $return->__CLASS__ = get_class($var); 118 $return->date = $var->format('c'); 119 $return->timezone = $var->getTimezone()->getName(); 120 121 return $return; 122 } 123 124 $return->__CLASS__ = self::getClass($var); 125 126 if ($var instanceof Proxy) { 127 $return->__IS_PROXY__ = true; 128 $return->__PROXY_INITIALIZED__ = $var->__isInitialized(); 129 } 130 131 if ($var instanceof ArrayObject || $var instanceof ArrayIterator) { 132 $return->__STORAGE__ = self::export($var->getArrayCopy(), $maxDepth - 1); 133 } 134 135 return self::fillReturnWithClassAttributes($var, $return, $maxDepth); 136 } 137 138 /** 139 * Fill the $return variable with class attributes 140 * Based on obj2array function from {@see https://secure.php.net/manual/en/function.get-object-vars.php#47075} 141 * 142 * @param object $var 143 * 144 * @return mixed 145 */ 146 private static function fillReturnWithClassAttributes($var, stdClass $return, int $maxDepth) 147 { 148 $clone = (array) $var; 149 150 foreach (array_keys($clone) as $key) { 151 $aux = explode("\0", $key); 152 $name = end($aux); 153 if ($aux[0] === '') { 154 $name .= ':' . ($aux[1] === '*' ? 'protected' : $aux[1] . ':private'); 155 } 156 157 $return->$name = self::export($clone[$key], $maxDepth - 1); 158 } 159 160 return $return; 161 } 162 163 /** 164 * @param object $object 165 */ 166 private static function getClass($object): string 167 { 168 $class = get_class($object); 169 170 if (! class_exists(Proxy::class)) { 171 return $class; 172 } 173 174 $pos = strrpos($class, '\\' . Proxy::MARKER . '\\'); 175 176 if ($pos === false) { 177 return $class; 178 } 179 180 return substr($class, $pos + strlen(Proxy::MARKER) + 2); 181 } 182} 183