Chris@0: Chris@0: * Chris@0: * For the full copyright and license information, please view the LICENSE Chris@0: * file that was distributed with this source code. Chris@0: */ Chris@0: Chris@0: namespace SebastianBergmann\GlobalState; Chris@0: Chris@0: use ReflectionClass; Chris@0: use Serializable; Chris@0: Chris@0: /** Chris@0: * A snapshot of global state. Chris@0: */ Chris@0: class Snapshot Chris@0: { Chris@0: /** Chris@0: * @var Blacklist Chris@0: */ Chris@0: private $blacklist; Chris@0: Chris@0: /** Chris@0: * @var array Chris@0: */ Chris@0: private $globalVariables = array(); Chris@0: Chris@0: /** Chris@0: * @var array Chris@0: */ Chris@0: private $superGlobalArrays = array(); Chris@0: Chris@0: /** Chris@0: * @var array Chris@0: */ Chris@0: private $superGlobalVariables = array(); Chris@0: Chris@0: /** Chris@0: * @var array Chris@0: */ Chris@0: private $staticAttributes = array(); Chris@0: Chris@0: /** Chris@0: * @var array Chris@0: */ Chris@0: private $iniSettings = array(); Chris@0: Chris@0: /** Chris@0: * @var array Chris@0: */ Chris@0: private $includedFiles = array(); Chris@0: Chris@0: /** Chris@0: * @var array Chris@0: */ Chris@0: private $constants = array(); Chris@0: Chris@0: /** Chris@0: * @var array Chris@0: */ Chris@0: private $functions = array(); Chris@0: Chris@0: /** Chris@0: * @var array Chris@0: */ Chris@0: private $interfaces = array(); Chris@0: Chris@0: /** Chris@0: * @var array Chris@0: */ Chris@0: private $classes = array(); Chris@0: Chris@0: /** Chris@0: * @var array Chris@0: */ Chris@0: private $traits = array(); Chris@0: Chris@0: /** Chris@0: * Creates a snapshot of the current global state. Chris@0: * Chris@0: * @param Blacklist $blacklist Chris@0: * @param bool $includeGlobalVariables Chris@0: * @param bool $includeStaticAttributes Chris@0: * @param bool $includeConstants Chris@0: * @param bool $includeFunctions Chris@0: * @param bool $includeClasses Chris@0: * @param bool $includeInterfaces Chris@0: * @param bool $includeTraits Chris@0: * @param bool $includeIniSettings Chris@0: * @param bool $includeIncludedFiles Chris@0: */ Chris@0: 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: { Chris@0: if ($blacklist === null) { Chris@0: $blacklist = new Blacklist; Chris@0: } Chris@0: Chris@0: $this->blacklist = $blacklist; Chris@0: Chris@0: if ($includeConstants) { Chris@0: $this->snapshotConstants(); Chris@0: } Chris@0: Chris@0: if ($includeFunctions) { Chris@0: $this->snapshotFunctions(); Chris@0: } Chris@0: Chris@0: if ($includeClasses || $includeStaticAttributes) { Chris@0: $this->snapshotClasses(); Chris@0: } Chris@0: Chris@0: if ($includeInterfaces) { Chris@0: $this->snapshotInterfaces(); Chris@0: } Chris@0: Chris@0: if ($includeGlobalVariables) { Chris@0: $this->setupSuperGlobalArrays(); Chris@0: $this->snapshotGlobals(); Chris@0: } Chris@0: Chris@0: if ($includeStaticAttributes) { Chris@0: $this->snapshotStaticAttributes(); Chris@0: } Chris@0: Chris@0: if ($includeIniSettings) { Chris@0: $this->iniSettings = ini_get_all(null, false); Chris@0: } Chris@0: Chris@0: if ($includeIncludedFiles) { Chris@0: $this->includedFiles = get_included_files(); Chris@0: } Chris@0: Chris@0: if (function_exists('get_declared_traits')) { Chris@0: $this->traits = get_declared_traits(); Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * @return Blacklist Chris@0: */ Chris@0: public function blacklist() Chris@0: { Chris@0: return $this->blacklist; Chris@0: } Chris@0: Chris@0: /** Chris@0: * @return array Chris@0: */ Chris@0: public function globalVariables() Chris@0: { Chris@0: return $this->globalVariables; Chris@0: } Chris@0: Chris@0: /** Chris@0: * @return array Chris@0: */ Chris@0: public function superGlobalVariables() Chris@0: { Chris@0: return $this->superGlobalVariables; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Returns a list of all super-global variable arrays. Chris@0: * Chris@0: * @return array Chris@0: */ Chris@0: public function superGlobalArrays() Chris@0: { Chris@0: return $this->superGlobalArrays; Chris@0: } Chris@0: Chris@0: /** Chris@0: * @return array Chris@0: */ Chris@0: public function staticAttributes() Chris@0: { Chris@0: return $this->staticAttributes; Chris@0: } Chris@0: Chris@0: /** Chris@0: * @return array Chris@0: */ Chris@0: public function iniSettings() Chris@0: { Chris@0: return $this->iniSettings; Chris@0: } Chris@0: Chris@0: /** Chris@0: * @return array Chris@0: */ Chris@0: public function includedFiles() Chris@0: { Chris@0: return $this->includedFiles; Chris@0: } Chris@0: Chris@0: /** Chris@0: * @return array Chris@0: */ Chris@0: public function constants() Chris@0: { Chris@0: return $this->constants; Chris@0: } Chris@0: Chris@0: /** Chris@0: * @return array Chris@0: */ Chris@0: public function functions() Chris@0: { Chris@0: return $this->functions; Chris@0: } Chris@0: Chris@0: /** Chris@0: * @return array Chris@0: */ Chris@0: public function interfaces() Chris@0: { Chris@0: return $this->interfaces; Chris@0: } Chris@0: Chris@0: /** Chris@0: * @return array Chris@0: */ Chris@0: public function classes() Chris@0: { Chris@0: return $this->classes; Chris@0: } Chris@0: Chris@0: /** Chris@0: * @return array Chris@0: */ Chris@0: public function traits() Chris@0: { Chris@0: return $this->traits; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Creates a snapshot user-defined constants. Chris@0: */ Chris@0: private function snapshotConstants() Chris@0: { Chris@0: $constants = get_defined_constants(true); Chris@0: Chris@0: if (isset($constants['user'])) { Chris@0: $this->constants = $constants['user']; Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Creates a snapshot user-defined functions. Chris@0: */ Chris@0: private function snapshotFunctions() Chris@0: { Chris@0: $functions = get_defined_functions(); Chris@0: Chris@0: $this->functions = $functions['user']; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Creates a snapshot user-defined classes. Chris@0: */ Chris@0: private function snapshotClasses() Chris@0: { Chris@0: foreach (array_reverse(get_declared_classes()) as $className) { Chris@0: $class = new ReflectionClass($className); Chris@0: Chris@0: if (!$class->isUserDefined()) { Chris@0: break; Chris@0: } Chris@0: Chris@0: $this->classes[] = $className; Chris@0: } Chris@0: Chris@0: $this->classes = array_reverse($this->classes); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Creates a snapshot user-defined interfaces. Chris@0: */ Chris@0: private function snapshotInterfaces() Chris@0: { Chris@0: foreach (array_reverse(get_declared_interfaces()) as $interfaceName) { Chris@0: $class = new ReflectionClass($interfaceName); Chris@0: Chris@0: if (!$class->isUserDefined()) { Chris@0: break; Chris@0: } Chris@0: Chris@0: $this->interfaces[] = $interfaceName; Chris@0: } Chris@0: Chris@0: $this->interfaces = array_reverse($this->interfaces); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Creates a snapshot of all global and super-global variables. Chris@0: */ Chris@0: private function snapshotGlobals() Chris@0: { Chris@0: $superGlobalArrays = $this->superGlobalArrays(); Chris@0: Chris@0: foreach ($superGlobalArrays as $superGlobalArray) { Chris@0: $this->snapshotSuperGlobalArray($superGlobalArray); Chris@0: } Chris@0: Chris@0: foreach (array_keys($GLOBALS) as $key) { Chris@0: if ($key != 'GLOBALS' && Chris@0: !in_array($key, $superGlobalArrays) && Chris@0: $this->canBeSerialized($GLOBALS[$key]) && Chris@0: !$this->blacklist->isGlobalVariableBlacklisted($key)) { Chris@0: $this->globalVariables[$key] = unserialize(serialize($GLOBALS[$key])); Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Creates a snapshot a super-global variable array. Chris@0: * Chris@0: * @param $superGlobalArray Chris@0: */ Chris@0: private function snapshotSuperGlobalArray($superGlobalArray) Chris@0: { Chris@0: $this->superGlobalVariables[$superGlobalArray] = array(); Chris@0: Chris@0: if (isset($GLOBALS[$superGlobalArray]) && is_array($GLOBALS[$superGlobalArray])) { Chris@0: foreach ($GLOBALS[$superGlobalArray] as $key => $value) { Chris@0: $this->superGlobalVariables[$superGlobalArray][$key] = unserialize(serialize($value)); Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Creates a snapshot of all static attributes in user-defined classes. Chris@0: */ Chris@0: private function snapshotStaticAttributes() Chris@0: { Chris@0: foreach ($this->classes as $className) { Chris@0: $class = new ReflectionClass($className); Chris@0: $snapshot = array(); Chris@0: Chris@0: foreach ($class->getProperties() as $attribute) { Chris@0: if ($attribute->isStatic()) { Chris@0: $name = $attribute->getName(); Chris@0: Chris@0: if ($this->blacklist->isStaticAttributeBlacklisted($className, $name)) { Chris@0: continue; Chris@0: } Chris@0: Chris@0: $attribute->setAccessible(true); Chris@0: $value = $attribute->getValue(); Chris@0: Chris@0: if ($this->canBeSerialized($value)) { Chris@0: $snapshot[$name] = unserialize(serialize($value)); Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: if (!empty($snapshot)) { Chris@0: $this->staticAttributes[$className] = $snapshot; Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Returns a list of all super-global variable arrays. Chris@0: * Chris@0: * @return array Chris@0: */ Chris@0: private function setupSuperGlobalArrays() Chris@0: { Chris@0: $this->superGlobalArrays = array( Chris@0: '_ENV', Chris@0: '_POST', Chris@0: '_GET', Chris@0: '_COOKIE', Chris@0: '_SERVER', Chris@0: '_FILES', Chris@0: '_REQUEST' Chris@0: ); Chris@0: Chris@0: if (ini_get('register_long_arrays') == '1') { Chris@0: $this->superGlobalArrays = array_merge( Chris@0: $this->superGlobalArrays, Chris@0: array( Chris@0: 'HTTP_ENV_VARS', Chris@0: 'HTTP_POST_VARS', Chris@0: 'HTTP_GET_VARS', Chris@0: 'HTTP_COOKIE_VARS', Chris@0: 'HTTP_SERVER_VARS', Chris@0: 'HTTP_POST_FILES' Chris@0: ) Chris@0: ); Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * @param mixed $variable Chris@0: * @return bool Chris@0: * @todo Implement this properly Chris@0: */ Chris@0: private function canBeSerialized($variable) Chris@0: { Chris@0: if (!is_object($variable)) { Chris@0: return !is_resource($variable); Chris@0: } Chris@0: Chris@0: if ($variable instanceof \stdClass) { Chris@0: return true; Chris@0: } Chris@0: Chris@0: $class = new ReflectionClass($variable); Chris@0: Chris@0: do { Chris@0: if ($class->isInternal()) { Chris@0: return $variable instanceof Serializable; Chris@0: } Chris@0: } while ($class = $class->getParentClass()); Chris@0: Chris@0: return true; Chris@0: } Chris@0: }