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