1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Prophecy\Util; |
4
|
|
|
|
5
|
|
|
use Prophecy\Prophecy\ProphecyInterface; |
6
|
|
|
use SebastianBergmann\RecursionContext\Context; |
7
|
|
|
|
8
|
|
|
/* |
9
|
|
|
* This file is part of the Prophecy. |
10
|
|
|
* (c) Konstantin Kudryashov <[email protected]> |
11
|
|
|
* Marcello Duarte <[email protected]> |
12
|
|
|
* |
13
|
|
|
* For the full copyright and license information, please view the LICENSE |
14
|
|
|
* file that was distributed with this source code. |
15
|
|
|
*/ |
16
|
|
|
|
17
|
|
|
/** |
18
|
|
|
* This class is a modification from sebastianbergmann/exporter |
19
|
|
|
* @see https://github.com/sebastianbergmann/exporter |
20
|
|
|
*/ |
21
|
|
|
class ExportUtil |
22
|
|
|
{ |
23
|
|
|
/** |
24
|
|
|
* Exports a value as a string |
25
|
|
|
* |
26
|
|
|
* The output of this method is similar to the output of print_r(), but |
27
|
|
|
* improved in various aspects: |
28
|
|
|
* |
29
|
|
|
* - NULL is rendered as "null" (instead of "") |
30
|
|
|
* - TRUE is rendered as "true" (instead of "1") |
31
|
|
|
* - FALSE is rendered as "false" (instead of "") |
32
|
|
|
* - Strings are always quoted with single quotes |
33
|
|
|
* - Carriage returns and newlines are normalized to \n |
34
|
|
|
* - Recursion and repeated rendering is treated properly |
35
|
|
|
* |
36
|
|
|
* @param mixed $value |
37
|
|
|
* @param int $indentation The indentation level of the 2nd+ line |
38
|
|
|
* @return string |
39
|
|
|
*/ |
40
|
|
|
public static function export($value, $indentation = 0) |
41
|
|
|
{ |
42
|
|
|
return self::recursiveExport($value, $indentation); |
43
|
|
|
} |
44
|
|
|
|
45
|
|
|
/** |
46
|
|
|
* Converts an object to an array containing all of its private, protected |
47
|
|
|
* and public properties. |
48
|
|
|
* |
49
|
|
|
* @param mixed $value |
50
|
|
|
* @return array |
51
|
|
|
*/ |
52
|
|
|
public static function toArray($value) |
53
|
|
|
{ |
54
|
|
|
if (!is_object($value)) { |
55
|
|
|
return (array) $value; |
56
|
|
|
} |
57
|
|
|
|
58
|
|
|
$array = array(); |
59
|
|
|
|
60
|
|
|
foreach ((array) $value as $key => $val) { |
61
|
|
|
// properties are transformed to keys in the following way: |
62
|
|
|
// private $property => "\0Classname\0property" |
63
|
|
|
// protected $property => "\0*\0property" |
64
|
|
|
// public $property => "property" |
65
|
|
|
if (preg_match('/^\0.+\0(.+)$/', $key, $matches)) { |
66
|
|
|
$key = $matches[1]; |
67
|
|
|
} |
68
|
|
|
|
69
|
|
|
// See https://github.com/php/php-src/commit/5721132 |
70
|
|
|
if ($key === "\0gcdata") { |
71
|
|
|
continue; |
72
|
|
|
} |
73
|
|
|
|
74
|
|
|
$array[$key] = $val; |
75
|
|
|
} |
76
|
|
|
|
77
|
|
|
// Some internal classes like SplObjectStorage don't work with the |
78
|
|
|
// above (fast) mechanism nor with reflection in Zend. |
79
|
|
|
// Format the output similarly to print_r() in this case |
80
|
|
|
if ($value instanceof \SplObjectStorage) { |
81
|
|
|
// However, the fast method does work in HHVM, and exposes the |
82
|
|
|
// internal implementation. Hide it again. |
83
|
|
|
if (property_exists('\SplObjectStorage', '__storage')) { |
84
|
|
|
unset($array['__storage']); |
85
|
|
|
} elseif (property_exists('\SplObjectStorage', 'storage')) { |
86
|
|
|
unset($array['storage']); |
87
|
|
|
} |
88
|
|
|
|
89
|
|
|
if (property_exists('\SplObjectStorage', '__key')) { |
90
|
|
|
unset($array['__key']); |
91
|
|
|
} |
92
|
|
|
|
93
|
|
|
foreach ($value as $key => $val) { |
94
|
|
|
$array[spl_object_hash($val)] = array( |
95
|
|
|
'obj' => $val, |
96
|
|
|
'inf' => $value->getInfo(), |
97
|
|
|
); |
98
|
|
|
} |
99
|
|
|
} |
100
|
|
|
|
101
|
|
|
return $array; |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
/** |
105
|
|
|
* Recursive implementation of export |
106
|
|
|
* |
107
|
|
|
* @param mixed $value The value to export |
108
|
|
|
* @param int $indentation The indentation level of the 2nd+ line |
109
|
|
|
* @param \SebastianBergmann\RecursionContext\Context $processed Previously processed objects |
110
|
|
|
* @return string |
111
|
|
|
* @see SebastianBergmann\Exporter\Exporter::export |
112
|
|
|
*/ |
113
|
|
|
protected static function recursiveExport(&$value, $indentation, $processed = null) |
114
|
|
|
{ |
115
|
|
|
if ($value === null) { |
116
|
|
|
return 'null'; |
117
|
|
|
} |
118
|
|
|
|
119
|
|
|
if ($value === true) { |
120
|
|
|
return 'true'; |
121
|
|
|
} |
122
|
|
|
|
123
|
|
|
if ($value === false) { |
124
|
|
|
return 'false'; |
125
|
|
|
} |
126
|
|
|
|
127
|
|
|
if (is_float($value) && floatval(intval($value)) === $value) { |
128
|
|
|
return "$value.0"; |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
if (is_resource($value)) { |
132
|
|
|
return sprintf( |
133
|
|
|
'resource(%d) of type (%s)', |
134
|
|
|
$value, |
135
|
|
|
get_resource_type($value) |
136
|
|
|
); |
137
|
|
|
} |
138
|
|
|
|
139
|
|
|
if (is_string($value)) { |
140
|
|
|
// Match for most non printable chars somewhat taking multibyte chars into account |
141
|
|
|
if (preg_match('/[^\x09-\x0d\x20-\xff]/', $value)) { |
142
|
|
|
return 'Binary String: 0x' . bin2hex($value); |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
return "'" . |
146
|
|
|
str_replace(array("\r\n", "\n\r", "\r"), array("\n", "\n", "\n"), $value) . |
147
|
|
|
"'"; |
148
|
|
|
} |
149
|
|
|
|
150
|
|
|
$whitespace = str_repeat(' ', 4 * $indentation); |
151
|
|
|
|
152
|
|
|
if (!$processed) { |
153
|
|
|
$processed = new Context; |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
if (is_array($value)) { |
157
|
|
|
if (($key = $processed->contains($value)) !== false) { |
158
|
|
|
return 'Array &' . $key; |
159
|
|
|
} |
160
|
|
|
|
161
|
|
|
$array = $value; |
162
|
|
|
$key = $processed->add($value); |
163
|
|
|
$values = ''; |
164
|
|
|
|
165
|
|
View Code Duplication |
if (count($array) > 0) { |
|
|
|
|
166
|
|
|
foreach ($array as $k => $v) { |
167
|
|
|
$values .= sprintf( |
168
|
|
|
'%s %s => %s' . "\n", |
169
|
|
|
$whitespace, |
170
|
|
|
self::recursiveExport($k, $indentation), |
171
|
|
|
self::recursiveExport($value[$k], $indentation + 1, $processed) |
172
|
|
|
); |
173
|
|
|
} |
174
|
|
|
|
175
|
|
|
$values = "\n" . $values . $whitespace; |
176
|
|
|
} |
177
|
|
|
|
178
|
|
|
return sprintf('Array &%s (%s)', $key, $values); |
179
|
|
|
} |
180
|
|
|
|
181
|
|
|
if (is_object($value)) { |
182
|
|
|
$class = get_class($value); |
183
|
|
|
|
184
|
|
|
if ($hash = $processed->contains($value)) { |
185
|
|
|
return sprintf('%s:%s Object', $class, $hash); |
186
|
|
|
} |
187
|
|
|
|
188
|
|
|
$hash = $processed->add($value); |
189
|
|
|
$values = ''; |
190
|
|
|
$array = self::toArray($value); |
191
|
|
|
|
192
|
|
View Code Duplication |
if (count($array) > 0) { |
|
|
|
|
193
|
|
|
foreach ($array as $k => $v) { |
194
|
|
|
$values .= sprintf( |
195
|
|
|
'%s %s => %s' . "\n", |
196
|
|
|
$whitespace, |
197
|
|
|
self::recursiveExport($k, $indentation), |
198
|
|
|
self::recursiveExport($v, $indentation + 1, $processed) |
199
|
|
|
); |
200
|
|
|
} |
201
|
|
|
|
202
|
|
|
$values = "\n" . $values . $whitespace; |
203
|
|
|
} |
204
|
|
|
|
205
|
|
|
return sprintf('%s:%s Object (%s)', $class, $hash, $values); |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
return var_export($value, true); |
209
|
|
|
} |
210
|
|
|
} |
211
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.