Chris@0
|
1 <?php
|
Chris@0
|
2 /*
|
Chris@0
|
3 * This file is part of the GlobalState 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\GlobalState;
|
Chris@0
|
12
|
Chris@0
|
13 use ReflectionClass;
|
Chris@0
|
14 use Serializable;
|
Chris@0
|
15
|
Chris@0
|
16 /**
|
Chris@0
|
17 * A snapshot of global state.
|
Chris@0
|
18 */
|
Chris@0
|
19 class Snapshot
|
Chris@0
|
20 {
|
Chris@0
|
21 /**
|
Chris@0
|
22 * @var Blacklist
|
Chris@0
|
23 */
|
Chris@0
|
24 private $blacklist;
|
Chris@0
|
25
|
Chris@0
|
26 /**
|
Chris@0
|
27 * @var array
|
Chris@0
|
28 */
|
Chris@0
|
29 private $globalVariables = array();
|
Chris@0
|
30
|
Chris@0
|
31 /**
|
Chris@0
|
32 * @var array
|
Chris@0
|
33 */
|
Chris@0
|
34 private $superGlobalArrays = array();
|
Chris@0
|
35
|
Chris@0
|
36 /**
|
Chris@0
|
37 * @var array
|
Chris@0
|
38 */
|
Chris@0
|
39 private $superGlobalVariables = array();
|
Chris@0
|
40
|
Chris@0
|
41 /**
|
Chris@0
|
42 * @var array
|
Chris@0
|
43 */
|
Chris@0
|
44 private $staticAttributes = array();
|
Chris@0
|
45
|
Chris@0
|
46 /**
|
Chris@0
|
47 * @var array
|
Chris@0
|
48 */
|
Chris@0
|
49 private $iniSettings = array();
|
Chris@0
|
50
|
Chris@0
|
51 /**
|
Chris@0
|
52 * @var array
|
Chris@0
|
53 */
|
Chris@0
|
54 private $includedFiles = array();
|
Chris@0
|
55
|
Chris@0
|
56 /**
|
Chris@0
|
57 * @var array
|
Chris@0
|
58 */
|
Chris@0
|
59 private $constants = array();
|
Chris@0
|
60
|
Chris@0
|
61 /**
|
Chris@0
|
62 * @var array
|
Chris@0
|
63 */
|
Chris@0
|
64 private $functions = array();
|
Chris@0
|
65
|
Chris@0
|
66 /**
|
Chris@0
|
67 * @var array
|
Chris@0
|
68 */
|
Chris@0
|
69 private $interfaces = array();
|
Chris@0
|
70
|
Chris@0
|
71 /**
|
Chris@0
|
72 * @var array
|
Chris@0
|
73 */
|
Chris@0
|
74 private $classes = array();
|
Chris@0
|
75
|
Chris@0
|
76 /**
|
Chris@0
|
77 * @var array
|
Chris@0
|
78 */
|
Chris@0
|
79 private $traits = array();
|
Chris@0
|
80
|
Chris@0
|
81 /**
|
Chris@0
|
82 * Creates a snapshot of the current global state.
|
Chris@0
|
83 *
|
Chris@0
|
84 * @param Blacklist $blacklist
|
Chris@0
|
85 * @param bool $includeGlobalVariables
|
Chris@0
|
86 * @param bool $includeStaticAttributes
|
Chris@0
|
87 * @param bool $includeConstants
|
Chris@0
|
88 * @param bool $includeFunctions
|
Chris@0
|
89 * @param bool $includeClasses
|
Chris@0
|
90 * @param bool $includeInterfaces
|
Chris@0
|
91 * @param bool $includeTraits
|
Chris@0
|
92 * @param bool $includeIniSettings
|
Chris@0
|
93 * @param bool $includeIncludedFiles
|
Chris@0
|
94 */
|
Chris@0
|
95 public function __construct(Blacklist $blacklist = null, $includeGlobalVariables = true, $includeStaticAttributes = true, $includeConstants = true, $includeFunctions = true, $includeClasses = true, $includeInterfaces = true, $includeTraits = true, $includeIniSettings = true, $includeIncludedFiles = true)
|
Chris@0
|
96 {
|
Chris@0
|
97 if ($blacklist === null) {
|
Chris@0
|
98 $blacklist = new Blacklist;
|
Chris@0
|
99 }
|
Chris@0
|
100
|
Chris@0
|
101 $this->blacklist = $blacklist;
|
Chris@0
|
102
|
Chris@0
|
103 if ($includeConstants) {
|
Chris@0
|
104 $this->snapshotConstants();
|
Chris@0
|
105 }
|
Chris@0
|
106
|
Chris@0
|
107 if ($includeFunctions) {
|
Chris@0
|
108 $this->snapshotFunctions();
|
Chris@0
|
109 }
|
Chris@0
|
110
|
Chris@0
|
111 if ($includeClasses || $includeStaticAttributes) {
|
Chris@0
|
112 $this->snapshotClasses();
|
Chris@0
|
113 }
|
Chris@0
|
114
|
Chris@0
|
115 if ($includeInterfaces) {
|
Chris@0
|
116 $this->snapshotInterfaces();
|
Chris@0
|
117 }
|
Chris@0
|
118
|
Chris@0
|
119 if ($includeGlobalVariables) {
|
Chris@0
|
120 $this->setupSuperGlobalArrays();
|
Chris@0
|
121 $this->snapshotGlobals();
|
Chris@0
|
122 }
|
Chris@0
|
123
|
Chris@0
|
124 if ($includeStaticAttributes) {
|
Chris@0
|
125 $this->snapshotStaticAttributes();
|
Chris@0
|
126 }
|
Chris@0
|
127
|
Chris@0
|
128 if ($includeIniSettings) {
|
Chris@0
|
129 $this->iniSettings = ini_get_all(null, false);
|
Chris@0
|
130 }
|
Chris@0
|
131
|
Chris@0
|
132 if ($includeIncludedFiles) {
|
Chris@0
|
133 $this->includedFiles = get_included_files();
|
Chris@0
|
134 }
|
Chris@0
|
135
|
Chris@0
|
136 if (function_exists('get_declared_traits')) {
|
Chris@0
|
137 $this->traits = get_declared_traits();
|
Chris@0
|
138 }
|
Chris@0
|
139 }
|
Chris@0
|
140
|
Chris@0
|
141 /**
|
Chris@0
|
142 * @return Blacklist
|
Chris@0
|
143 */
|
Chris@0
|
144 public function blacklist()
|
Chris@0
|
145 {
|
Chris@0
|
146 return $this->blacklist;
|
Chris@0
|
147 }
|
Chris@0
|
148
|
Chris@0
|
149 /**
|
Chris@0
|
150 * @return array
|
Chris@0
|
151 */
|
Chris@0
|
152 public function globalVariables()
|
Chris@0
|
153 {
|
Chris@0
|
154 return $this->globalVariables;
|
Chris@0
|
155 }
|
Chris@0
|
156
|
Chris@0
|
157 /**
|
Chris@0
|
158 * @return array
|
Chris@0
|
159 */
|
Chris@0
|
160 public function superGlobalVariables()
|
Chris@0
|
161 {
|
Chris@0
|
162 return $this->superGlobalVariables;
|
Chris@0
|
163 }
|
Chris@0
|
164
|
Chris@0
|
165 /**
|
Chris@0
|
166 * Returns a list of all super-global variable arrays.
|
Chris@0
|
167 *
|
Chris@0
|
168 * @return array
|
Chris@0
|
169 */
|
Chris@0
|
170 public function superGlobalArrays()
|
Chris@0
|
171 {
|
Chris@0
|
172 return $this->superGlobalArrays;
|
Chris@0
|
173 }
|
Chris@0
|
174
|
Chris@0
|
175 /**
|
Chris@0
|
176 * @return array
|
Chris@0
|
177 */
|
Chris@0
|
178 public function staticAttributes()
|
Chris@0
|
179 {
|
Chris@0
|
180 return $this->staticAttributes;
|
Chris@0
|
181 }
|
Chris@0
|
182
|
Chris@0
|
183 /**
|
Chris@0
|
184 * @return array
|
Chris@0
|
185 */
|
Chris@0
|
186 public function iniSettings()
|
Chris@0
|
187 {
|
Chris@0
|
188 return $this->iniSettings;
|
Chris@0
|
189 }
|
Chris@0
|
190
|
Chris@0
|
191 /**
|
Chris@0
|
192 * @return array
|
Chris@0
|
193 */
|
Chris@0
|
194 public function includedFiles()
|
Chris@0
|
195 {
|
Chris@0
|
196 return $this->includedFiles;
|
Chris@0
|
197 }
|
Chris@0
|
198
|
Chris@0
|
199 /**
|
Chris@0
|
200 * @return array
|
Chris@0
|
201 */
|
Chris@0
|
202 public function constants()
|
Chris@0
|
203 {
|
Chris@0
|
204 return $this->constants;
|
Chris@0
|
205 }
|
Chris@0
|
206
|
Chris@0
|
207 /**
|
Chris@0
|
208 * @return array
|
Chris@0
|
209 */
|
Chris@0
|
210 public function functions()
|
Chris@0
|
211 {
|
Chris@0
|
212 return $this->functions;
|
Chris@0
|
213 }
|
Chris@0
|
214
|
Chris@0
|
215 /**
|
Chris@0
|
216 * @return array
|
Chris@0
|
217 */
|
Chris@0
|
218 public function interfaces()
|
Chris@0
|
219 {
|
Chris@0
|
220 return $this->interfaces;
|
Chris@0
|
221 }
|
Chris@0
|
222
|
Chris@0
|
223 /**
|
Chris@0
|
224 * @return array
|
Chris@0
|
225 */
|
Chris@0
|
226 public function classes()
|
Chris@0
|
227 {
|
Chris@0
|
228 return $this->classes;
|
Chris@0
|
229 }
|
Chris@0
|
230
|
Chris@0
|
231 /**
|
Chris@0
|
232 * @return array
|
Chris@0
|
233 */
|
Chris@0
|
234 public function traits()
|
Chris@0
|
235 {
|
Chris@0
|
236 return $this->traits;
|
Chris@0
|
237 }
|
Chris@0
|
238
|
Chris@0
|
239 /**
|
Chris@0
|
240 * Creates a snapshot user-defined constants.
|
Chris@0
|
241 */
|
Chris@0
|
242 private function snapshotConstants()
|
Chris@0
|
243 {
|
Chris@0
|
244 $constants = get_defined_constants(true);
|
Chris@0
|
245
|
Chris@0
|
246 if (isset($constants['user'])) {
|
Chris@0
|
247 $this->constants = $constants['user'];
|
Chris@0
|
248 }
|
Chris@0
|
249 }
|
Chris@0
|
250
|
Chris@0
|
251 /**
|
Chris@0
|
252 * Creates a snapshot user-defined functions.
|
Chris@0
|
253 */
|
Chris@0
|
254 private function snapshotFunctions()
|
Chris@0
|
255 {
|
Chris@0
|
256 $functions = get_defined_functions();
|
Chris@0
|
257
|
Chris@0
|
258 $this->functions = $functions['user'];
|
Chris@0
|
259 }
|
Chris@0
|
260
|
Chris@0
|
261 /**
|
Chris@0
|
262 * Creates a snapshot user-defined classes.
|
Chris@0
|
263 */
|
Chris@0
|
264 private function snapshotClasses()
|
Chris@0
|
265 {
|
Chris@0
|
266 foreach (array_reverse(get_declared_classes()) as $className) {
|
Chris@0
|
267 $class = new ReflectionClass($className);
|
Chris@0
|
268
|
Chris@0
|
269 if (!$class->isUserDefined()) {
|
Chris@0
|
270 break;
|
Chris@0
|
271 }
|
Chris@0
|
272
|
Chris@0
|
273 $this->classes[] = $className;
|
Chris@0
|
274 }
|
Chris@0
|
275
|
Chris@0
|
276 $this->classes = array_reverse($this->classes);
|
Chris@0
|
277 }
|
Chris@0
|
278
|
Chris@0
|
279 /**
|
Chris@0
|
280 * Creates a snapshot user-defined interfaces.
|
Chris@0
|
281 */
|
Chris@0
|
282 private function snapshotInterfaces()
|
Chris@0
|
283 {
|
Chris@0
|
284 foreach (array_reverse(get_declared_interfaces()) as $interfaceName) {
|
Chris@0
|
285 $class = new ReflectionClass($interfaceName);
|
Chris@0
|
286
|
Chris@0
|
287 if (!$class->isUserDefined()) {
|
Chris@0
|
288 break;
|
Chris@0
|
289 }
|
Chris@0
|
290
|
Chris@0
|
291 $this->interfaces[] = $interfaceName;
|
Chris@0
|
292 }
|
Chris@0
|
293
|
Chris@0
|
294 $this->interfaces = array_reverse($this->interfaces);
|
Chris@0
|
295 }
|
Chris@0
|
296
|
Chris@0
|
297 /**
|
Chris@0
|
298 * Creates a snapshot of all global and super-global variables.
|
Chris@0
|
299 */
|
Chris@0
|
300 private function snapshotGlobals()
|
Chris@0
|
301 {
|
Chris@0
|
302 $superGlobalArrays = $this->superGlobalArrays();
|
Chris@0
|
303
|
Chris@0
|
304 foreach ($superGlobalArrays as $superGlobalArray) {
|
Chris@0
|
305 $this->snapshotSuperGlobalArray($superGlobalArray);
|
Chris@0
|
306 }
|
Chris@0
|
307
|
Chris@0
|
308 foreach (array_keys($GLOBALS) as $key) {
|
Chris@0
|
309 if ($key != 'GLOBALS' &&
|
Chris@0
|
310 !in_array($key, $superGlobalArrays) &&
|
Chris@0
|
311 $this->canBeSerialized($GLOBALS[$key]) &&
|
Chris@0
|
312 !$this->blacklist->isGlobalVariableBlacklisted($key)) {
|
Chris@0
|
313 $this->globalVariables[$key] = unserialize(serialize($GLOBALS[$key]));
|
Chris@0
|
314 }
|
Chris@0
|
315 }
|
Chris@0
|
316 }
|
Chris@0
|
317
|
Chris@0
|
318 /**
|
Chris@0
|
319 * Creates a snapshot a super-global variable array.
|
Chris@0
|
320 *
|
Chris@0
|
321 * @param $superGlobalArray
|
Chris@0
|
322 */
|
Chris@0
|
323 private function snapshotSuperGlobalArray($superGlobalArray)
|
Chris@0
|
324 {
|
Chris@0
|
325 $this->superGlobalVariables[$superGlobalArray] = array();
|
Chris@0
|
326
|
Chris@0
|
327 if (isset($GLOBALS[$superGlobalArray]) && is_array($GLOBALS[$superGlobalArray])) {
|
Chris@0
|
328 foreach ($GLOBALS[$superGlobalArray] as $key => $value) {
|
Chris@0
|
329 $this->superGlobalVariables[$superGlobalArray][$key] = unserialize(serialize($value));
|
Chris@0
|
330 }
|
Chris@0
|
331 }
|
Chris@0
|
332 }
|
Chris@0
|
333
|
Chris@0
|
334 /**
|
Chris@0
|
335 * Creates a snapshot of all static attributes in user-defined classes.
|
Chris@0
|
336 */
|
Chris@0
|
337 private function snapshotStaticAttributes()
|
Chris@0
|
338 {
|
Chris@0
|
339 foreach ($this->classes as $className) {
|
Chris@0
|
340 $class = new ReflectionClass($className);
|
Chris@0
|
341 $snapshot = array();
|
Chris@0
|
342
|
Chris@0
|
343 foreach ($class->getProperties() as $attribute) {
|
Chris@0
|
344 if ($attribute->isStatic()) {
|
Chris@0
|
345 $name = $attribute->getName();
|
Chris@0
|
346
|
Chris@0
|
347 if ($this->blacklist->isStaticAttributeBlacklisted($className, $name)) {
|
Chris@0
|
348 continue;
|
Chris@0
|
349 }
|
Chris@0
|
350
|
Chris@0
|
351 $attribute->setAccessible(true);
|
Chris@0
|
352 $value = $attribute->getValue();
|
Chris@0
|
353
|
Chris@0
|
354 if ($this->canBeSerialized($value)) {
|
Chris@0
|
355 $snapshot[$name] = unserialize(serialize($value));
|
Chris@0
|
356 }
|
Chris@0
|
357 }
|
Chris@0
|
358 }
|
Chris@0
|
359
|
Chris@0
|
360 if (!empty($snapshot)) {
|
Chris@0
|
361 $this->staticAttributes[$className] = $snapshot;
|
Chris@0
|
362 }
|
Chris@0
|
363 }
|
Chris@0
|
364 }
|
Chris@0
|
365
|
Chris@0
|
366 /**
|
Chris@0
|
367 * Returns a list of all super-global variable arrays.
|
Chris@0
|
368 *
|
Chris@0
|
369 * @return array
|
Chris@0
|
370 */
|
Chris@0
|
371 private function setupSuperGlobalArrays()
|
Chris@0
|
372 {
|
Chris@0
|
373 $this->superGlobalArrays = array(
|
Chris@0
|
374 '_ENV',
|
Chris@0
|
375 '_POST',
|
Chris@0
|
376 '_GET',
|
Chris@0
|
377 '_COOKIE',
|
Chris@0
|
378 '_SERVER',
|
Chris@0
|
379 '_FILES',
|
Chris@0
|
380 '_REQUEST'
|
Chris@0
|
381 );
|
Chris@0
|
382
|
Chris@0
|
383 if (ini_get('register_long_arrays') == '1') {
|
Chris@0
|
384 $this->superGlobalArrays = array_merge(
|
Chris@0
|
385 $this->superGlobalArrays,
|
Chris@0
|
386 array(
|
Chris@0
|
387 'HTTP_ENV_VARS',
|
Chris@0
|
388 'HTTP_POST_VARS',
|
Chris@0
|
389 'HTTP_GET_VARS',
|
Chris@0
|
390 'HTTP_COOKIE_VARS',
|
Chris@0
|
391 'HTTP_SERVER_VARS',
|
Chris@0
|
392 'HTTP_POST_FILES'
|
Chris@0
|
393 )
|
Chris@0
|
394 );
|
Chris@0
|
395 }
|
Chris@0
|
396 }
|
Chris@0
|
397
|
Chris@0
|
398 /**
|
Chris@0
|
399 * @param mixed $variable
|
Chris@0
|
400 * @return bool
|
Chris@0
|
401 * @todo Implement this properly
|
Chris@0
|
402 */
|
Chris@0
|
403 private function canBeSerialized($variable)
|
Chris@0
|
404 {
|
Chris@0
|
405 if (!is_object($variable)) {
|
Chris@0
|
406 return !is_resource($variable);
|
Chris@0
|
407 }
|
Chris@0
|
408
|
Chris@0
|
409 if ($variable instanceof \stdClass) {
|
Chris@0
|
410 return true;
|
Chris@0
|
411 }
|
Chris@0
|
412
|
Chris@0
|
413 $class = new ReflectionClass($variable);
|
Chris@0
|
414
|
Chris@0
|
415 do {
|
Chris@0
|
416 if ($class->isInternal()) {
|
Chris@0
|
417 return $variable instanceof Serializable;
|
Chris@0
|
418 }
|
Chris@0
|
419 } while ($class = $class->getParentClass());
|
Chris@0
|
420
|
Chris@0
|
421 return true;
|
Chris@0
|
422 }
|
Chris@0
|
423 }
|