PageRenderTime 65ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/vendor/sebastian/global-state/src/Snapshot.php

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