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