annotate vendor/sebastian/exporter/src/Exporter.php @ 9:1fc0ff908d1f

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