pax_global_header00006660000000000000000000000064134247545310014522gustar00rootroot0000000000000052 comment=edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4 global-state-3.0.0/000077500000000000000000000000001342475453100141005ustar00rootroot00000000000000global-state-3.0.0/.github/000077500000000000000000000000001342475453100154405ustar00rootroot00000000000000global-state-3.0.0/.github/stale.yml000066400000000000000000000026771342475453100173070ustar00rootroot00000000000000# Configuration for probot-stale - https://github.com/probot/stale # Number of days of inactivity before an Issue or Pull Request becomes stale daysUntilStale: 60 # Number of days of inactivity before a stale Issue or Pull Request is closed. # Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. daysUntilClose: 7 # Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable exemptLabels: - enhancement # Set to true to ignore issues in a project (defaults to false) exemptProjects: false # Set to true to ignore issues in a milestone (defaults to false) exemptMilestones: false # Label to use when marking as stale staleLabel: stale # Comment to post when marking as stale. Set to `false` to disable markComment: > This issue has been automatically marked as stale because it has not had activity within the last 60 days. It will be closed after 7 days if no further activity occurs. Thank you for your contributions. # Comment to post when removing the stale label. # unmarkComment: > # Your comment here. # Comment to post when closing a stale Issue or Pull Request. closeComment: > This issue has been automatically closed because it has not had activity since it was marked as stale. Thank you for your contributions. # Limit the number of actions per hour, from 1-30. Default is 30 limitPerRun: 30 # Limit to only `issues` or `pulls` only: issues global-state-3.0.0/.gitignore000066400000000000000000000001151342475453100160650ustar00rootroot00000000000000/.idea /.php_cs /.php_cs.cache /.phpunit.result.cache /composer.lock /vendor global-state-3.0.0/.php_cs.dist000066400000000000000000000174761342475453100163360ustar00rootroot00000000000000 For the full copyright and license information, please view the LICENSE file that was distributed with this source code. EOF; return PhpCsFixer\Config::create() ->setRiskyAllowed(true) ->setRules( [ 'align_multiline_comment' => true, 'array_indentation' => true, 'array_syntax' => ['syntax' => 'short'], 'binary_operator_spaces' => [ 'operators' => [ '=' => 'align', '=>' => 'align', ], ], 'blank_line_after_namespace' => true, 'blank_line_before_statement' => [ 'statements' => [ 'break', 'continue', 'declare', 'do', 'for', 'foreach', 'if', 'include', 'include_once', 'require', 'require_once', 'return', 'switch', 'throw', 'try', 'while', 'yield', ], ], 'braces' => true, 'cast_spaces' => true, 'class_attributes_separation' => ['elements' => ['const', 'method', 'property']], 'combine_consecutive_issets' => true, 'combine_consecutive_unsets' => true, 'compact_nullable_typehint' => true, 'concat_space' => ['spacing' => 'one'], 'declare_equal_normalize' => ['space' => 'none'], 'declare_strict_types' => true, 'dir_constant' => true, 'elseif' => true, 'encoding' => true, 'full_opening_tag' => true, 'function_declaration' => true, 'header_comment' => ['header' => $header, 'separate' => 'none'], 'indentation_type' => true, 'is_null' => true, 'line_ending' => true, 'list_syntax' => ['syntax' => 'short'], 'logical_operators' => true, 'lowercase_cast' => true, 'lowercase_constants' => true, 'lowercase_keywords' => true, 'lowercase_static_reference' => true, 'magic_constant_casing' => true, 'method_argument_space' => ['ensure_fully_multiline' => true], 'modernize_types_casting' => true, 'multiline_comment_opening_closing' => true, 'multiline_whitespace_before_semicolons' => true, 'native_constant_invocation' => true, 'native_function_casing' => true, 'native_function_invocation' => true, 'new_with_braces' => false, 'no_alias_functions' => true, 'no_alternative_syntax' => true, 'no_blank_lines_after_class_opening' => true, 'no_blank_lines_after_phpdoc' => true, 'no_blank_lines_before_namespace' => true, 'no_closing_tag' => true, 'no_empty_comment' => true, 'no_empty_phpdoc' => true, 'no_empty_statement' => true, 'no_extra_blank_lines' => true, 'no_homoglyph_names' => true, 'no_leading_import_slash' => true, 'no_leading_namespace_whitespace' => true, 'no_mixed_echo_print' => ['use' => 'print'], 'no_multiline_whitespace_around_double_arrow' => true, 'no_null_property_initialization' => true, 'no_php4_constructor' => true, 'no_short_bool_cast' => true, 'no_short_echo_tag' => true, 'no_singleline_whitespace_before_semicolons' => true, 'no_spaces_after_function_name' => true, 'no_spaces_inside_parenthesis' => true, 'no_superfluous_elseif' => true, 'no_superfluous_phpdoc_tags' => true, 'no_trailing_comma_in_list_call' => true, 'no_trailing_comma_in_singleline_array' => true, 'no_trailing_whitespace' => true, 'no_trailing_whitespace_in_comment' => true, 'no_unneeded_control_parentheses' => true, 'no_unneeded_curly_braces' => true, 'no_unneeded_final_method' => true, 'no_unreachable_default_argument_value' => true, 'no_unset_on_property' => true, 'no_unused_imports' => true, 'no_useless_else' => true, 'no_useless_return' => true, 'no_whitespace_before_comma_in_array' => true, 'no_whitespace_in_blank_line' => true, 'non_printable_character' => true, 'normalize_index_brace' => true, 'object_operator_without_whitespace' => true, 'ordered_class_elements' => [ 'order' => [ 'use_trait', 'constant_public', 'constant_protected', 'constant_private', 'property_public_static', 'property_protected_static', 'property_private_static', 'property_public', 'property_protected', 'property_private', 'method_public_static', 'construct', 'destruct', 'magic', 'phpunit', 'method_public', 'method_protected', 'method_private', 'method_protected_static', 'method_private_static', ], ], 'ordered_imports' => true, 'phpdoc_add_missing_param_annotation' => true, 'phpdoc_align' => true, 'phpdoc_annotation_without_dot' => true, 'phpdoc_indent' => true, 'phpdoc_no_access' => true, 'phpdoc_no_empty_return' => true, 'phpdoc_no_package' => true, 'phpdoc_order' => true, 'phpdoc_return_self_reference' => true, 'phpdoc_scalar' => true, 'phpdoc_separation' => true, 'phpdoc_single_line_var_spacing' => true, 'phpdoc_to_comment' => true, 'phpdoc_trim' => true, 'phpdoc_trim_consecutive_blank_line_separation' => true, 'phpdoc_types' => ['groups' => ['simple', 'meta']], 'phpdoc_types_order' => true, 'phpdoc_var_without_name' => true, 'pow_to_exponentiation' => true, 'protected_to_private' => true, 'return_assignment' => true, 'return_type_declaration' => ['space_before' => 'none'], 'self_accessor' => true, 'semicolon_after_instruction' => true, 'set_type_to_cast' => true, 'short_scalar_cast' => true, 'simplified_null_return' => true, 'single_blank_line_at_eof' => true, 'single_import_per_statement' => true, 'single_line_after_imports' => true, 'single_quote' => true, 'standardize_not_equals' => true, 'ternary_to_null_coalescing' => true, 'trailing_comma_in_multiline_array' => true, 'trim_array_spaces' => true, 'unary_operator_spaces' => true, 'visibility_required' => [ 'elements' => [ 'const', 'method', 'property', ], ], 'void_return' => true, 'whitespace_after_comma_in_array' => true, ] ) ->setFinder( PhpCsFixer\Finder::create() ->files() ->in(__DIR__ . '/src') ->in(__DIR__ . '/tests') ); global-state-3.0.0/.travis.yml000066400000000000000000000005741342475453100162170ustar00rootroot00000000000000language: php php: - 7.2 - 7.3 - master sudo: false before_install: - composer self-update - composer clear-cache install: - travis_retry composer update --no-interaction --no-ansi --no-progress --no-suggest script: - ./vendor/bin/phpunit --coverage-clover=coverage.xml after_success: - bash <(curl -s https://codecov.io/bash) notifications: email: false global-state-3.0.0/ChangeLog.md000066400000000000000000000007611342475453100162550ustar00rootroot00000000000000# Changes in sebastian/global-state All notable changes in `sebastian/global-state` are documented in this file using the [Keep a CHANGELOG](https://keepachangelog.com/) principles. ## [3.0.0] - 2019-02-01 ### Changed * `Snapshot::canBeSerialized()` now recursively checks arrays and object graphs for variables that cannot be serialized ### Removed * This component is no longer supported on PHP 7.0 and PHP 7.1 [3.0.0]: https://github.com/sebastianbergmann/phpunit/compare/2.0.0...3.0.0 global-state-3.0.0/LICENSE000066400000000000000000000030251342475453100151050ustar00rootroot00000000000000sebastian/global-state Copyright (c) 2001-2019, Sebastian Bergmann . All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Sebastian Bergmann nor the names of his contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. global-state-3.0.0/README.md000066400000000000000000000011731342475453100153610ustar00rootroot00000000000000# global-state Snapshotting of global state, factored out of PHPUnit into a stand-alone component. [![Build Status](https://travis-ci.org/sebastianbergmann/global-state.svg?branch=master)](https://travis-ci.org/sebastianbergmann/global-state) ## Installation You can add this library as a local, per-project dependency to your project using [Composer](https://getcomposer.org/): composer require sebastian/global-state If you only need this library during development, for instance to run your project's test suite, then you should add it as a development-time dependency: composer require --dev sebastian/global-state global-state-3.0.0/build.xml000066400000000000000000000012711342475453100157220ustar00rootroot00000000000000 global-state-3.0.0/composer.json000066400000000000000000000021051342475453100166200ustar00rootroot00000000000000{ "name": "sebastian/global-state", "description": "Snapshotting of global state", "keywords": ["global state"], "homepage": "http://www.github.com/sebastianbergmann/global-state", "license": "BSD-3-Clause", "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" } ], "prefer-stable": true, "config": { "optimize-autoloader": true, "sort-packages": true }, "require": { "php": "^7.2", "sebastian/object-reflector": "^1.1.1", "sebastian/recursion-context": "^3.0" }, "require-dev": { "ext-dom": "*", "phpunit/phpunit": "^8.0" }, "suggest": { "ext-uopz": "*" }, "autoload": { "classmap": [ "src/" ] }, "autoload-dev": { "classmap": [ "tests/_fixture/" ], "files": [ "tests/_fixture/SnapshotFunctions.php" ] }, "extra": { "branch-alias": { "dev-master": "3.0-dev" } } } global-state-3.0.0/phpunit.xml000066400000000000000000000016421342475453100163140ustar00rootroot00000000000000 tests src global-state-3.0.0/src/000077500000000000000000000000001342475453100146675ustar00rootroot00000000000000global-state-3.0.0/src/Blacklist.php000066400000000000000000000053011342475453100173070ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\GlobalState; /** * A blacklist for global state elements that should not be snapshotted. */ final class Blacklist { /** * @var array */ private $globalVariables = []; /** * @var string[] */ private $classes = []; /** * @var string[] */ private $classNamePrefixes = []; /** * @var string[] */ private $parentClasses = []; /** * @var string[] */ private $interfaces = []; /** * @var array */ private $staticAttributes = []; public function addGlobalVariable(string $variableName): void { $this->globalVariables[$variableName] = true; } public function addClass(string $className): void { $this->classes[] = $className; } public function addSubclassesOf(string $className): void { $this->parentClasses[] = $className; } public function addImplementorsOf(string $interfaceName): void { $this->interfaces[] = $interfaceName; } public function addClassNamePrefix(string $classNamePrefix): void { $this->classNamePrefixes[] = $classNamePrefix; } public function addStaticAttribute(string $className, string $attributeName): void { if (!isset($this->staticAttributes[$className])) { $this->staticAttributes[$className] = []; } $this->staticAttributes[$className][$attributeName] = true; } public function isGlobalVariableBlacklisted(string $variableName): bool { return isset($this->globalVariables[$variableName]); } public function isStaticAttributeBlacklisted(string $className, string $attributeName): bool { if (\in_array($className, $this->classes)) { return true; } foreach ($this->classNamePrefixes as $prefix) { if (\strpos($className, $prefix) === 0) { return true; } } $class = new \ReflectionClass($className); foreach ($this->parentClasses as $type) { if ($class->isSubclassOf($type)) { return true; } } foreach ($this->interfaces as $type) { if ($class->implementsInterface($type)) { return true; } } if (isset($this->staticAttributes[$className][$attributeName])) { return true; } return false; } } global-state-3.0.0/src/CodeExporter.php000066400000000000000000000045521342475453100200110ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\GlobalState; /** * Exports parts of a Snapshot as PHP code. */ final class CodeExporter { public function constants(Snapshot $snapshot): string { $result = ''; foreach ($snapshot->constants() as $name => $value) { $result .= \sprintf( 'if (!defined(\'%s\')) define(\'%s\', %s);' . "\n", $name, $name, $this->exportVariable($value) ); } return $result; } public function globalVariables(Snapshot $snapshot): string { $result = '$GLOBALS = [];' . \PHP_EOL; foreach ($snapshot->globalVariables() as $name => $value) { $result .= \sprintf( '$GLOBALS[%s] = %s;' . \PHP_EOL, $this->exportVariable($name), $this->exportVariable($value) ); } return $result; } public function iniSettings(Snapshot $snapshot): string { $result = ''; foreach ($snapshot->iniSettings() as $key => $value) { $result .= \sprintf( '@ini_set(%s, %s);' . "\n", $this->exportVariable($key), $this->exportVariable($value) ); } return $result; } private function exportVariable($variable): string { if (\is_scalar($variable) || null === $variable || (\is_array($variable) && $this->arrayOnlyContainsScalars($variable))) { return \var_export($variable, true); } return 'unserialize(' . \var_export(\serialize($variable), true) . ')'; } private function arrayOnlyContainsScalars(array $array): bool { $result = true; foreach ($array as $element) { if (\is_array($element)) { $result = $this->arrayOnlyContainsScalars($element); } elseif (!\is_scalar($element) && null !== $element) { $result = false; } if ($result === false) { break; } } return $result; } } global-state-3.0.0/src/Restorer.php000066400000000000000000000104361342475453100172110ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\GlobalState; /** * Restorer of snapshots of global state. */ class Restorer { /** * Deletes function definitions that are not defined in a snapshot. * * @throws RuntimeException when the uopz_delete() function is not available * * @see https://github.com/krakjoe/uopz */ public function restoreFunctions(Snapshot $snapshot): void { if (!\function_exists('uopz_delete')) { throw new RuntimeException('The uopz_delete() function is required for this operation'); } $functions = \get_defined_functions(); foreach (\array_diff($functions['user'], $snapshot->functions()) as $function) { uopz_delete($function); } } /** * Restores all global and super-global variables from a snapshot. */ public function restoreGlobalVariables(Snapshot $snapshot): void { $superGlobalArrays = $snapshot->superGlobalArrays(); foreach ($superGlobalArrays as $superGlobalArray) { $this->restoreSuperGlobalArray($snapshot, $superGlobalArray); } $globalVariables = $snapshot->globalVariables(); foreach (\array_keys($GLOBALS) as $key) { if ($key !== 'GLOBALS' && !\in_array($key, $superGlobalArrays) && !$snapshot->blacklist()->isGlobalVariableBlacklisted($key)) { if (\array_key_exists($key, $globalVariables)) { $GLOBALS[$key] = $globalVariables[$key]; } else { unset($GLOBALS[$key]); } } } } /** * Restores all static attributes in user-defined classes from this snapshot. */ public function restoreStaticAttributes(Snapshot $snapshot): void { $current = new Snapshot($snapshot->blacklist(), false, false, false, false, true, false, false, false, false); $newClasses = \array_diff($current->classes(), $snapshot->classes()); unset($current); foreach ($snapshot->staticAttributes() as $className => $staticAttributes) { foreach ($staticAttributes as $name => $value) { $reflector = new \ReflectionProperty($className, $name); $reflector->setAccessible(true); $reflector->setValue($value); } } foreach ($newClasses as $className) { $class = new \ReflectionClass($className); $defaults = $class->getDefaultProperties(); foreach ($class->getProperties() as $attribute) { if (!$attribute->isStatic()) { continue; } $name = $attribute->getName(); if ($snapshot->blacklist()->isStaticAttributeBlacklisted($className, $name)) { continue; } if (!isset($defaults[$name])) { continue; } $attribute->setAccessible(true); $attribute->setValue($defaults[$name]); } } } /** * Restores a super-global variable array from this snapshot. */ private function restoreSuperGlobalArray(Snapshot $snapshot, string $superGlobalArray): void { $superGlobalVariables = $snapshot->superGlobalVariables(); if (isset($GLOBALS[$superGlobalArray]) && \is_array($GLOBALS[$superGlobalArray]) && isset($superGlobalVariables[$superGlobalArray])) { $keys = \array_keys( \array_merge( $GLOBALS[$superGlobalArray], $superGlobalVariables[$superGlobalArray] ) ); foreach ($keys as $key) { if (isset($superGlobalVariables[$superGlobalArray][$key])) { $GLOBALS[$superGlobalArray][$key] = $superGlobalVariables[$superGlobalArray][$key]; } else { unset($GLOBALS[$superGlobalArray][$key]); } } } } } global-state-3.0.0/src/Snapshot.php000066400000000000000000000244761342475453100172140ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\GlobalState; use SebastianBergmann\ObjectReflector\ObjectReflector; use SebastianBergmann\RecursionContext\Context; /** * A snapshot of global state. */ class Snapshot { /** * @var Blacklist */ private $blacklist; /** * @var array */ private $globalVariables = []; /** * @var array */ private $superGlobalArrays = []; /** * @var array */ private $superGlobalVariables = []; /** * @var array */ private $staticAttributes = []; /** * @var array */ private $iniSettings = []; /** * @var array */ private $includedFiles = []; /** * @var array */ private $constants = []; /** * @var array */ private $functions = []; /** * @var array */ private $interfaces = []; /** * @var array */ private $classes = []; /** * @var array */ private $traits = []; /** * Creates a snapshot of the current global state. */ 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) { if ($blacklist === null) { $blacklist = new Blacklist; } $this->blacklist = $blacklist; if ($includeConstants) { $this->snapshotConstants(); } if ($includeFunctions) { $this->snapshotFunctions(); } if ($includeClasses || $includeStaticAttributes) { $this->snapshotClasses(); } if ($includeInterfaces) { $this->snapshotInterfaces(); } if ($includeGlobalVariables) { $this->setupSuperGlobalArrays(); $this->snapshotGlobals(); } if ($includeStaticAttributes) { $this->snapshotStaticAttributes(); } if ($includeIniSettings) { $this->iniSettings = \ini_get_all(null, false); } if ($includeIncludedFiles) { $this->includedFiles = \get_included_files(); } $this->traits = \get_declared_traits(); } public function blacklist(): Blacklist { return $this->blacklist; } public function globalVariables(): array { return $this->globalVariables; } public function superGlobalVariables(): array { return $this->superGlobalVariables; } public function superGlobalArrays(): array { return $this->superGlobalArrays; } public function staticAttributes(): array { return $this->staticAttributes; } public function iniSettings(): array { return $this->iniSettings; } public function includedFiles(): array { return $this->includedFiles; } public function constants(): array { return $this->constants; } public function functions(): array { return $this->functions; } public function interfaces(): array { return $this->interfaces; } public function classes(): array { return $this->classes; } public function traits(): array { return $this->traits; } /** * Creates a snapshot user-defined constants. */ private function snapshotConstants(): void { $constants = \get_defined_constants(true); if (isset($constants['user'])) { $this->constants = $constants['user']; } } /** * Creates a snapshot user-defined functions. */ private function snapshotFunctions(): void { $functions = \get_defined_functions(); $this->functions = $functions['user']; } /** * Creates a snapshot user-defined classes. */ private function snapshotClasses(): void { foreach (\array_reverse(\get_declared_classes()) as $className) { $class = new \ReflectionClass($className); if (!$class->isUserDefined()) { break; } $this->classes[] = $className; } $this->classes = \array_reverse($this->classes); } /** * Creates a snapshot user-defined interfaces. */ private function snapshotInterfaces(): void { foreach (\array_reverse(\get_declared_interfaces()) as $interfaceName) { $class = new \ReflectionClass($interfaceName); if (!$class->isUserDefined()) { break; } $this->interfaces[] = $interfaceName; } $this->interfaces = \array_reverse($this->interfaces); } /** * Creates a snapshot of all global and super-global variables. */ private function snapshotGlobals(): void { $superGlobalArrays = $this->superGlobalArrays(); foreach ($superGlobalArrays as $superGlobalArray) { $this->snapshotSuperGlobalArray($superGlobalArray); } foreach (\array_keys($GLOBALS) as $key) { if ($key !== 'GLOBALS' && !\in_array($key, $superGlobalArrays) && $this->canBeSerialized($GLOBALS[$key]) && !$this->blacklist->isGlobalVariableBlacklisted($key)) { /* @noinspection UnserializeExploitsInspection */ $this->globalVariables[$key] = \unserialize(\serialize($GLOBALS[$key])); } } } /** * Creates a snapshot a super-global variable array. */ private function snapshotSuperGlobalArray(string $superGlobalArray): void { $this->superGlobalVariables[$superGlobalArray] = []; if (isset($GLOBALS[$superGlobalArray]) && \is_array($GLOBALS[$superGlobalArray])) { foreach ($GLOBALS[$superGlobalArray] as $key => $value) { /* @noinspection UnserializeExploitsInspection */ $this->superGlobalVariables[$superGlobalArray][$key] = \unserialize(\serialize($value)); } } } /** * Creates a snapshot of all static attributes in user-defined classes. */ private function snapshotStaticAttributes(): void { foreach ($this->classes as $className) { $class = new \ReflectionClass($className); $snapshot = []; foreach ($class->getProperties() as $attribute) { if ($attribute->isStatic()) { $name = $attribute->getName(); if ($this->blacklist->isStaticAttributeBlacklisted($className, $name)) { continue; } $attribute->setAccessible(true); $value = $attribute->getValue(); if ($this->canBeSerialized($value)) { /* @noinspection UnserializeExploitsInspection */ $snapshot[$name] = \unserialize(\serialize($value)); } } } if (!empty($snapshot)) { $this->staticAttributes[$className] = $snapshot; } } } /** * Returns a list of all super-global variable arrays. */ private function setupSuperGlobalArrays(): void { $this->superGlobalArrays = [ '_ENV', '_POST', '_GET', '_COOKIE', '_SERVER', '_FILES', '_REQUEST', ]; } private function canBeSerialized($variable): bool { if (\is_scalar($variable) || $variable === null) { return true; } if (\is_resource($variable)) { return false; } foreach ($this->enumerateObjectsAndResources($variable) as $value) { if (\is_resource($value)) { return false; } if (\is_object($value)) { $class = new \ReflectionClass($value); if ($class->isAnonymous()) { return false; } try { @\serialize($value); } catch (\Throwable $t) { return false; } } } return true; } private function enumerateObjectsAndResources($variable): array { if (isset(\func_get_args()[1])) { $processed = \func_get_args()[1]; } else { $processed = new Context; } $result = []; if ($processed->contains($variable)) { return $result; } $array = $variable; $processed->add($variable); if (\is_array($variable)) { foreach ($array as $element) { if (!\is_array($element) && !\is_object($element) && !\is_resource($element)) { continue; } if (!\is_resource($element)) { /** @noinspection SlowArrayOperationsInLoopInspection */ $result = \array_merge( $result, $this->enumerateObjectsAndResources($element, $processed) ); } else { $result[] = $element; } } } else { $result[] = $variable; foreach ((new ObjectReflector)->getAttributes($variable) as $value) { if (!\is_array($value) && !\is_object($value) && !\is_resource($value)) { continue; } if (!\is_resource($value)) { /** @noinspection SlowArrayOperationsInLoopInspection */ $result = \array_merge( $result, $this->enumerateObjectsAndResources($value, $processed) ); } else { $result[] = $value; } } } return $result; } } global-state-3.0.0/src/exceptions/000077500000000000000000000000001342475453100170505ustar00rootroot00000000000000global-state-3.0.0/src/exceptions/Exception.php000066400000000000000000000005161342475453100215210ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\GlobalState; interface Exception { } global-state-3.0.0/src/exceptions/RuntimeException.php000066400000000000000000000006061342475453100230650ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\GlobalState; final class RuntimeException extends \RuntimeException implements Exception { } global-state-3.0.0/tests/000077500000000000000000000000001342475453100152425ustar00rootroot00000000000000global-state-3.0.0/tests/BlacklistTest.php000066400000000000000000000063401342475453100205260ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\GlobalState; use PHPUnit\Framework\TestCase; use SebastianBergmann\GlobalState\TestFixture\BlacklistedChildClass; use SebastianBergmann\GlobalState\TestFixture\BlacklistedClass; use SebastianBergmann\GlobalState\TestFixture\BlacklistedImplementor; use SebastianBergmann\GlobalState\TestFixture\BlacklistedInterface; /** * @covers \SebastianBergmann\GlobalState\Blacklist */ final class BlacklistTest extends TestCase { /** * @var \SebastianBergmann\GlobalState\Blacklist */ private $blacklist; protected function setUp(): void { $this->blacklist = new Blacklist; } public function testGlobalVariableThatIsNotBlacklistedIsNotTreatedAsBlacklisted(): void { $this->assertFalse($this->blacklist->isGlobalVariableBlacklisted('variable')); } public function testGlobalVariableCanBeBlacklisted(): void { $this->blacklist->addGlobalVariable('variable'); $this->assertTrue($this->blacklist->isGlobalVariableBlacklisted('variable')); } public function testStaticAttributeThatIsNotBlacklistedIsNotTreatedAsBlacklisted(): void { $this->assertFalse( $this->blacklist->isStaticAttributeBlacklisted( BlacklistedClass::class, 'attribute' ) ); } public function testClassCanBeBlacklisted(): void { $this->blacklist->addClass(BlacklistedClass::class); $this->assertTrue( $this->blacklist->isStaticAttributeBlacklisted( BlacklistedClass::class, 'attribute' ) ); } public function testSubclassesCanBeBlacklisted(): void { $this->blacklist->addSubclassesOf(BlacklistedClass::class); $this->assertTrue( $this->blacklist->isStaticAttributeBlacklisted( BlacklistedChildClass::class, 'attribute' ) ); } public function testImplementorsCanBeBlacklisted(): void { $this->blacklist->addImplementorsOf(BlacklistedInterface::class); $this->assertTrue( $this->blacklist->isStaticAttributeBlacklisted( BlacklistedImplementor::class, 'attribute' ) ); } public function testClassNamePrefixesCanBeBlacklisted(): void { $this->blacklist->addClassNamePrefix('SebastianBergmann\GlobalState'); $this->assertTrue( $this->blacklist->isStaticAttributeBlacklisted( BlacklistedClass::class, 'attribute' ) ); } public function testStaticAttributeCanBeBlacklisted(): void { $this->blacklist->addStaticAttribute( BlacklistedClass::class, 'attribute' ); $this->assertTrue( $this->blacklist->isStaticAttributeBlacklisted( BlacklistedClass::class, 'attribute' ) ); } } global-state-3.0.0/tests/CodeExporterTest.php000066400000000000000000000016431342475453100212220ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\GlobalState; use PHPUnit\Framework\TestCase; /** * @covers \SebastianBergmann\GlobalState\CodeExporter */ final class CodeExporterTest extends TestCase { /** * @runInSeparateProcess */ public function testCanExportGlobalVariablesToCode(): void { $GLOBALS = ['foo' => 'bar']; $snapshot = new Snapshot(null, true, false, false, false, false, false, false, false, false); $exporter = new CodeExporter; $this->assertEquals( '$GLOBALS = [];' . \PHP_EOL . '$GLOBALS[\'foo\'] = \'bar\';' . \PHP_EOL, $exporter->globalVariables($snapshot) ); } } global-state-3.0.0/tests/RestorerTest.php000066400000000000000000000042621342475453100204240ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\GlobalState; use PHPUnit\Framework\TestCase; /** * @covers \SebastianBergmann\GlobalState\Restorer * * @uses \SebastianBergmann\GlobalState\Blacklist * @uses \SebastianBergmann\GlobalState\Snapshot */ final class RestorerTest extends TestCase { public static function setUpBeforeClass(): void { $GLOBALS['varBool'] = false; $GLOBALS['varNull'] = null; $_GET['varGet'] = 0; } public function testRestorerGlobalVariable(): void { $snapshot = new Snapshot(null, true, false, false, false, false, false, false, false, false); $restorer = new Restorer; $restorer->restoreGlobalVariables($snapshot); $this->assertArrayHasKey('varBool', $GLOBALS); $this->assertEquals(false, $GLOBALS['varBool']); $this->assertArrayHasKey('varNull', $GLOBALS); $this->assertEquals(null, $GLOBALS['varNull']); $this->assertArrayHasKey('varGet', $_GET); $this->assertEquals(0, $_GET['varGet']); } /** * @backupGlobals enabled */ public function testIntegrationRestorerGlobalVariables(): void { $this->assertArrayHasKey('varBool', $GLOBALS); $this->assertEquals(false, $GLOBALS['varBool']); $this->assertArrayHasKey('varNull', $GLOBALS); $this->assertEquals(null, $GLOBALS['varNull']); $this->assertArrayHasKey('varGet', $_GET); $this->assertEquals(0, $_GET['varGet']); } /** * @depends testIntegrationRestorerGlobalVariables */ public function testIntegrationRestorerGlobalVariables2(): void { $this->assertArrayHasKey('varBool', $GLOBALS); $this->assertEquals(false, $GLOBALS['varBool']); $this->assertArrayHasKey('varNull', $GLOBALS); $this->assertEquals(null, $GLOBALS['varNull']); $this->assertArrayHasKey('varGet', $_GET); $this->assertEquals(0, $_GET['varGet']); } } global-state-3.0.0/tests/SnapshotTest.php000066400000000000000000000074761342475453100204300ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\GlobalState; use PHPUnit\Framework\TestCase; use SebastianBergmann\GlobalState\TestFixture\BlacklistedInterface; use SebastianBergmann\GlobalState\TestFixture\SnapshotClass; use SebastianBergmann\GlobalState\TestFixture\SnapshotTrait; /** * @covers \SebastianBergmann\GlobalState\Snapshot * * @uses \SebastianBergmann\GlobalState\Blacklist */ final class SnapshotTest extends TestCase { /** * @var Blacklist */ private $blacklist; protected function setUp(): void { $this->blacklist = new Blacklist; } public function testStaticAttributes(): void { SnapshotClass::init(); $this->blacklistAllLoadedClassesExceptSnapshotClass(); $snapshot = new Snapshot($this->blacklist, false, true, false, false, false, false, false, false, false); $expected = [ SnapshotClass::class => [ 'string' => 'string', 'objects' => [new \stdClass], ], ]; $this->assertEquals($expected, $snapshot->staticAttributes()); } public function testConstants(): void { $snapshot = new Snapshot($this->blacklist, false, false, true, false, false, false, false, false, false); $this->assertArrayHasKey('GLOBALSTATE_TESTSUITE', $snapshot->constants()); } public function testFunctions(): void { $snapshot = new Snapshot($this->blacklist, false, false, false, true, false, false, false, false, false); $functions = $snapshot->functions(); $this->assertContains('sebastianbergmann\globalstate\testfixture\snapshotfunction', $functions); $this->assertNotContains('assert', $functions); } public function testClasses(): void { $snapshot = new Snapshot($this->blacklist, false, false, false, false, true, false, false, false, false); $classes = $snapshot->classes(); $this->assertContains(TestCase::class, $classes); $this->assertNotContains(Exception::class, $classes); } public function testInterfaces(): void { $snapshot = new Snapshot($this->blacklist, false, false, false, false, false, true, false, false, false); $interfaces = $snapshot->interfaces(); $this->assertContains(BlacklistedInterface::class, $interfaces); $this->assertNotContains(\Countable::class, $interfaces); } public function testTraits(): void { \spl_autoload_call('SebastianBergmann\GlobalState\TestFixture\SnapshotTrait'); $snapshot = new Snapshot($this->blacklist, false, false, false, false, false, false, true, false, false); $this->assertContains(SnapshotTrait::class, $snapshot->traits()); } public function testIniSettings(): void { $snapshot = new Snapshot($this->blacklist, false, false, false, false, false, false, false, true, false); $iniSettings = $snapshot->iniSettings(); $this->assertArrayHasKey('date.timezone', $iniSettings); $this->assertEquals('Etc/UTC', $iniSettings['date.timezone']); } public function testIncludedFiles(): void { $snapshot = new Snapshot($this->blacklist, false, false, false, false, false, false, false, false, true); $this->assertContains(__FILE__, $snapshot->includedFiles()); } private function blacklistAllLoadedClassesExceptSnapshotClass(): void { foreach (\get_declared_classes() as $class) { if ($class === SnapshotClass::class) { continue; } $this->blacklist->addClass($class); } } } global-state-3.0.0/tests/_fixture/000077500000000000000000000000001342475453100170675ustar00rootroot00000000000000global-state-3.0.0/tests/_fixture/BlacklistedChildClass.php000066400000000000000000000005731342475453100237600ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\GlobalState\TestFixture; class BlacklistedChildClass extends BlacklistedClass { } global-state-3.0.0/tests/_fixture/BlacklistedClass.php000066400000000000000000000005741342475453100230150ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\GlobalState\TestFixture; class BlacklistedClass { private static $attribute; } global-state-3.0.0/tests/_fixture/BlacklistedImplementor.php000066400000000000000000000006421342475453100242370ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\GlobalState\TestFixture; class BlacklistedImplementor implements BlacklistedInterface { private static $attribute; } global-state-3.0.0/tests/_fixture/BlacklistedInterface.php000066400000000000000000000005451342475453100236460ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\GlobalState\TestFixture; interface BlacklistedInterface { } global-state-3.0.0/tests/_fixture/SnapshotClass.php000066400000000000000000000014321342475453100223650ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\GlobalState\TestFixture; class SnapshotClass { private static $string = 'string'; private static $closures = []; private static $files = []; private static $resources = []; private static $objects = []; public static function init(): void { self::$closures[] = function (): void { }; self::$files[] = new \SplFileInfo(__FILE__); self::$resources[] = \fopen('php://memory', 'r'); self::$objects[] = new \stdClass; } } global-state-3.0.0/tests/_fixture/SnapshotDomDocument.php000066400000000000000000000006061342475453100235400ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\GlobalState\TestFixture; use DomDocument; class SnapshotDomDocument extends DomDocument { } global-state-3.0.0/tests/_fixture/SnapshotFunctions.php000066400000000000000000000005501342475453100232700ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\GlobalState\TestFixture; function snapshotFunction(): void { } global-state-3.0.0/tests/_fixture/SnapshotTrait.php000066400000000000000000000005321342475453100224030ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\GlobalState\TestFixture; trait SnapshotTrait { }