annotate vendor/sebastian/exporter/src/Exporter.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents 1fec387a4317
children
rev   line source
Chris@0 1 <?php
Chris@0 2 /*
Chris@14 3 * This file is part of the exporter package.
Chris@0 4 *
Chris@0 5 * (c) Sebastian Bergmann <sebastian@phpunit.de>
Chris@0 6 *
Chris@0 7 * For the full copyright and license information, please view the LICENSE
Chris@0 8 * file that was distributed with this source code.
Chris@0 9 */
Chris@0 10
Chris@0 11 namespace SebastianBergmann\Exporter;
Chris@0 12
Chris@0 13 use SebastianBergmann\RecursionContext\Context;
Chris@0 14
Chris@0 15 /**
Chris@0 16 * A nifty utility for visualizing PHP variables.
Chris@0 17 *
Chris@0 18 * <code>
Chris@0 19 * <?php
Chris@0 20 * use SebastianBergmann\Exporter\Exporter;
Chris@0 21 *
Chris@0 22 * $exporter = new Exporter;
Chris@0 23 * print $exporter->export(new Exception);
Chris@0 24 * </code>
Chris@0 25 */
Chris@0 26 class Exporter
Chris@0 27 {
Chris@0 28 /**
Chris@0 29 * Exports a value as a string
Chris@0 30 *
Chris@0 31 * The output of this method is similar to the output of print_r(), but
Chris@0 32 * improved in various aspects:
Chris@0 33 *
Chris@0 34 * - NULL is rendered as "null" (instead of "")
Chris@0 35 * - TRUE is rendered as "true" (instead of "1")
Chris@0 36 * - FALSE is rendered as "false" (instead of "")
Chris@0 37 * - Strings are always quoted with single quotes
Chris@0 38 * - Carriage returns and newlines are normalized to \n
Chris@0 39 * - Recursion and repeated rendering is treated properly
Chris@0 40 *
Chris@14 41 * @param mixed $value
Chris@14 42 * @param int $indentation The indentation level of the 2nd+ line
Chris@14 43 *
Chris@0 44 * @return string
Chris@0 45 */
Chris@0 46 public function export($value, $indentation = 0)
Chris@0 47 {
Chris@0 48 return $this->recursiveExport($value, $indentation);
Chris@0 49 }
Chris@0 50
Chris@0 51 /**
Chris@14 52 * @param mixed $data
Chris@14 53 * @param Context $context
Chris@14 54 *
Chris@0 55 * @return string
Chris@0 56 */
Chris@0 57 public function shortenedRecursiveExport(&$data, Context $context = null)
Chris@0 58 {
Chris@14 59 $result = [];
Chris@0 60 $exporter = new self();
Chris@0 61
Chris@0 62 if (!$context) {
Chris@0 63 $context = new Context;
Chris@0 64 }
Chris@0 65
Chris@14 66 $array = $data;
Chris@0 67 $context->add($data);
Chris@0 68
Chris@14 69 foreach ($array as $key => $value) {
Chris@0 70 if (is_array($value)) {
Chris@0 71 if ($context->contains($data[$key]) !== false) {
Chris@0 72 $result[] = '*RECURSION*';
Chris@14 73 } else {
Chris@0 74 $result[] = sprintf(
Chris@0 75 'array(%s)',
Chris@0 76 $this->shortenedRecursiveExport($data[$key], $context)
Chris@0 77 );
Chris@0 78 }
Chris@14 79 } else {
Chris@0 80 $result[] = $exporter->shortenedExport($value);
Chris@0 81 }
Chris@0 82 }
Chris@0 83
Chris@0 84 return implode(', ', $result);
Chris@0 85 }
Chris@0 86
Chris@0 87 /**
Chris@0 88 * Exports a value into a single-line string
Chris@0 89 *
Chris@0 90 * The output of this method is similar to the output of
Chris@0 91 * SebastianBergmann\Exporter\Exporter::export().
Chris@0 92 *
Chris@0 93 * Newlines are replaced by the visible string '\n'.
Chris@0 94 * Contents of arrays and objects (if any) are replaced by '...'.
Chris@0 95 *
Chris@14 96 * @param mixed $value
Chris@14 97 *
Chris@0 98 * @return string
Chris@14 99 *
Chris@0 100 * @see SebastianBergmann\Exporter\Exporter::export
Chris@0 101 */
Chris@0 102 public function shortenedExport($value)
Chris@0 103 {
Chris@0 104 if (is_string($value)) {
Chris@14 105 $string = str_replace("\n", '', $this->export($value));
Chris@0 106
Chris@0 107 if (function_exists('mb_strlen')) {
Chris@0 108 if (mb_strlen($string) > 40) {
Chris@0 109 $string = mb_substr($string, 0, 30) . '...' . mb_substr($string, -7);
Chris@0 110 }
Chris@0 111 } else {
Chris@0 112 if (strlen($string) > 40) {
Chris@0 113 $string = substr($string, 0, 30) . '...' . substr($string, -7);
Chris@0 114 }
Chris@0 115 }
Chris@0 116
Chris@14 117 return $string;
Chris@0 118 }
Chris@0 119
Chris@0 120 if (is_object($value)) {
Chris@0 121 return sprintf(
Chris@0 122 '%s Object (%s)',
Chris@0 123 get_class($value),
Chris@0 124 count($this->toArray($value)) > 0 ? '...' : ''
Chris@0 125 );
Chris@0 126 }
Chris@0 127
Chris@0 128 if (is_array($value)) {
Chris@0 129 return sprintf(
Chris@0 130 'Array (%s)',
Chris@0 131 count($value) > 0 ? '...' : ''
Chris@0 132 );
Chris@0 133 }
Chris@0 134
Chris@0 135 return $this->export($value);
Chris@0 136 }
Chris@0 137
Chris@0 138 /**
Chris@0 139 * Converts an object to an array containing all of its private, protected
Chris@0 140 * and public properties.
Chris@0 141 *
Chris@14 142 * @param mixed $value
Chris@14 143 *
Chris@0 144 * @return array
Chris@0 145 */
Chris@0 146 public function toArray($value)
Chris@0 147 {
Chris@0 148 if (!is_object($value)) {
Chris@0 149 return (array) $value;
Chris@0 150 }
Chris@0 151
Chris@14 152 $array = [];
Chris@0 153
Chris@0 154 foreach ((array) $value as $key => $val) {
Chris@0 155 // properties are transformed to keys in the following way:
Chris@0 156 // private $property => "\0Classname\0property"
Chris@0 157 // protected $property => "\0*\0property"
Chris@0 158 // public $property => "property"
Chris@0 159 if (preg_match('/^\0.+\0(.+)$/', $key, $matches)) {
Chris@0 160 $key = $matches[1];
Chris@0 161 }
Chris@0 162
Chris@0 163 // See https://github.com/php/php-src/commit/5721132
Chris@0 164 if ($key === "\0gcdata") {
Chris@0 165 continue;
Chris@0 166 }
Chris@0 167
Chris@0 168 $array[$key] = $val;
Chris@0 169 }
Chris@0 170
Chris@0 171 // Some internal classes like SplObjectStorage don't work with the
Chris@0 172 // above (fast) mechanism nor with reflection in Zend.
Chris@0 173 // Format the output similarly to print_r() in this case
Chris@0 174 if ($value instanceof \SplObjectStorage) {
Chris@0 175 // However, the fast method does work in HHVM, and exposes the
Chris@0 176 // internal implementation. Hide it again.
Chris@0 177 if (property_exists('\SplObjectStorage', '__storage')) {
Chris@0 178 unset($array['__storage']);
Chris@0 179 } elseif (property_exists('\SplObjectStorage', 'storage')) {
Chris@0 180 unset($array['storage']);
Chris@0 181 }
Chris@0 182
Chris@0 183 if (property_exists('\SplObjectStorage', '__key')) {
Chris@0 184 unset($array['__key']);
Chris@0 185 }
Chris@0 186
Chris@0 187 foreach ($value as $key => $val) {
Chris@14 188 $array[spl_object_hash($val)] = [
Chris@0 189 'obj' => $val,
Chris@0 190 'inf' => $value->getInfo(),
Chris@14 191 ];
Chris@0 192 }
Chris@0 193 }
Chris@0 194
Chris@0 195 return $array;
Chris@0 196 }
Chris@0 197
Chris@0 198 /**
Chris@0 199 * Recursive implementation of export
Chris@0 200 *
Chris@14 201 * @param mixed $value The value to export
Chris@14 202 * @param int $indentation The indentation level of the 2nd+ line
Chris@14 203 * @param \SebastianBergmann\RecursionContext\Context $processed Previously processed objects
Chris@14 204 *
Chris@0 205 * @return string
Chris@14 206 *
Chris@0 207 * @see SebastianBergmann\Exporter\Exporter::export
Chris@0 208 */
Chris@0 209 protected function recursiveExport(&$value, $indentation, $processed = null)
Chris@0 210 {
Chris@0 211 if ($value === null) {
Chris@0 212 return 'null';
Chris@0 213 }
Chris@0 214
Chris@0 215 if ($value === true) {
Chris@0 216 return 'true';
Chris@0 217 }
Chris@0 218
Chris@0 219 if ($value === false) {
Chris@0 220 return 'false';
Chris@0 221 }
Chris@0 222
Chris@0 223 if (is_float($value) && floatval(intval($value)) === $value) {
Chris@0 224 return "$value.0";
Chris@0 225 }
Chris@0 226
Chris@0 227 if (is_resource($value)) {
Chris@0 228 return sprintf(
Chris@0 229 'resource(%d) of type (%s)',
Chris@0 230 $value,
Chris@0 231 get_resource_type($value)
Chris@0 232 );
Chris@0 233 }
Chris@0 234
Chris@0 235 if (is_string($value)) {
Chris@0 236 // Match for most non printable chars somewhat taking multibyte chars into account
Chris@0 237 if (preg_match('/[^\x09-\x0d\x1b\x20-\xff]/', $value)) {
Chris@0 238 return 'Binary String: 0x' . bin2hex($value);
Chris@0 239 }
Chris@0 240
Chris@0 241 return "'" .
Chris@14 242 str_replace('<lf>', "\n",
Chris@14 243 str_replace(
Chris@14 244 ["\r\n", "\n\r", "\r", "\n"],
Chris@14 245 ['\r\n<lf>', '\n\r<lf>', '\r<lf>', '\n<lf>'],
Chris@14 246 $value
Chris@14 247 )
Chris@14 248 ) .
Chris@0 249 "'";
Chris@0 250 }
Chris@0 251
Chris@0 252 $whitespace = str_repeat(' ', 4 * $indentation);
Chris@0 253
Chris@0 254 if (!$processed) {
Chris@0 255 $processed = new Context;
Chris@0 256 }
Chris@0 257
Chris@0 258 if (is_array($value)) {
Chris@0 259 if (($key = $processed->contains($value)) !== false) {
Chris@0 260 return 'Array &' . $key;
Chris@0 261 }
Chris@0 262
Chris@14 263 $array = $value;
Chris@0 264 $key = $processed->add($value);
Chris@0 265 $values = '';
Chris@0 266
Chris@14 267 if (count($array) > 0) {
Chris@14 268 foreach ($array as $k => $v) {
Chris@0 269 $values .= sprintf(
Chris@0 270 '%s %s => %s' . "\n",
Chris@0 271 $whitespace,
Chris@0 272 $this->recursiveExport($k, $indentation),
Chris@0 273 $this->recursiveExport($value[$k], $indentation + 1, $processed)
Chris@0 274 );
Chris@0 275 }
Chris@0 276
Chris@0 277 $values = "\n" . $values . $whitespace;
Chris@0 278 }
Chris@0 279
Chris@0 280 return sprintf('Array &%s (%s)', $key, $values);
Chris@0 281 }
Chris@0 282
Chris@0 283 if (is_object($value)) {
Chris@0 284 $class = get_class($value);
Chris@0 285
Chris@0 286 if ($hash = $processed->contains($value)) {
Chris@0 287 return sprintf('%s Object &%s', $class, $hash);
Chris@0 288 }
Chris@0 289
Chris@0 290 $hash = $processed->add($value);
Chris@0 291 $values = '';
Chris@0 292 $array = $this->toArray($value);
Chris@0 293
Chris@0 294 if (count($array) > 0) {
Chris@0 295 foreach ($array as $k => $v) {
Chris@0 296 $values .= sprintf(
Chris@0 297 '%s %s => %s' . "\n",
Chris@0 298 $whitespace,
Chris@0 299 $this->recursiveExport($k, $indentation),
Chris@0 300 $this->recursiveExport($v, $indentation + 1, $processed)
Chris@0 301 );
Chris@0 302 }
Chris@0 303
Chris@0 304 $values = "\n" . $values . $whitespace;
Chris@0 305 }
Chris@0 306
Chris@0 307 return sprintf('%s Object &%s (%s)', $class, $hash, $values);
Chris@0 308 }
Chris@0 309
Chris@0 310 return var_export($value, true);
Chris@0 311 }
Chris@0 312 }