Native.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506
  1. <?php
  2. namespace Laravel\SerializableClosure\Serializers;
  3. use Closure;
  4. use Laravel\SerializableClosure\Contracts\Serializable;
  5. use Laravel\SerializableClosure\SerializableClosure;
  6. use Laravel\SerializableClosure\Support\ClosureScope;
  7. use Laravel\SerializableClosure\Support\ClosureStream;
  8. use Laravel\SerializableClosure\Support\ReflectionClosure;
  9. use Laravel\SerializableClosure\Support\SelfReference;
  10. use ReflectionObject;
  11. use UnitEnum;
  12. class Native implements Serializable
  13. {
  14. /**
  15. * Transform the use variables before serialization.
  16. *
  17. * @var \Closure|null
  18. */
  19. public static $transformUseVariables;
  20. /**
  21. * Resolve the use variables after unserialization.
  22. *
  23. * @var \Closure|null
  24. */
  25. public static $resolveUseVariables;
  26. /**
  27. * The closure to be serialized/unserialized.
  28. *
  29. * @var \Closure
  30. */
  31. protected $closure;
  32. /**
  33. * The closure's reflection.
  34. *
  35. * @var \Laravel\SerializableClosure\Support\ReflectionClosure|null
  36. */
  37. protected $reflector;
  38. /**
  39. * The closure's code.
  40. *
  41. * @var array|null
  42. */
  43. protected $code;
  44. /**
  45. * The closure's reference.
  46. *
  47. * @var string
  48. */
  49. protected $reference;
  50. /**
  51. * The closure's scope.
  52. *
  53. * @var \Laravel\SerializableClosure\Support\ClosureScope|null
  54. */
  55. protected $scope;
  56. /**
  57. * The "key" that marks an array as recursive.
  58. */
  59. const ARRAY_RECURSIVE_KEY = 'LARAVEL_SERIALIZABLE_RECURSIVE_KEY';
  60. /**
  61. * Creates a new serializable closure instance.
  62. *
  63. * @param \Closure $closure
  64. * @return void
  65. */
  66. public function __construct(Closure $closure)
  67. {
  68. $this->closure = $closure;
  69. }
  70. /**
  71. * Resolve the closure with the given arguments.
  72. *
  73. * @return mixed
  74. */
  75. public function __invoke()
  76. {
  77. return call_user_func_array($this->closure, func_get_args());
  78. }
  79. /**
  80. * Gets the closure.
  81. *
  82. * @return \Closure
  83. */
  84. public function getClosure()
  85. {
  86. return $this->closure;
  87. }
  88. /**
  89. * Get the serializable representation of the closure.
  90. *
  91. * @return array
  92. */
  93. public function __serialize()
  94. {
  95. if ($this->scope === null) {
  96. $this->scope = new ClosureScope();
  97. $this->scope->toSerialize++;
  98. }
  99. $this->scope->serializations++;
  100. $scope = $object = null;
  101. $reflector = $this->getReflector();
  102. if ($reflector->isBindingRequired()) {
  103. $object = $reflector->getClosureThis();
  104. static::wrapClosures($object, $this->scope);
  105. }
  106. if ($scope = $reflector->getClosureScopeClass()) {
  107. $scope = $scope->name;
  108. }
  109. $this->reference = spl_object_hash($this->closure);
  110. $this->scope[$this->closure] = $this;
  111. $use = $reflector->getUseVariables();
  112. if (static::$transformUseVariables) {
  113. $use = call_user_func(static::$transformUseVariables, $reflector->getUseVariables());
  114. }
  115. $code = $reflector->getCode();
  116. $this->mapByReference($use);
  117. $data = [
  118. 'use' => $use,
  119. 'function' => $code,
  120. 'scope' => $scope,
  121. 'this' => $object,
  122. 'self' => $this->reference,
  123. ];
  124. if (! --$this->scope->serializations && ! --$this->scope->toSerialize) {
  125. $this->scope = null;
  126. }
  127. return $data;
  128. }
  129. /**
  130. * Restore the closure after serialization.
  131. *
  132. * @param array $data
  133. * @return void
  134. */
  135. public function __unserialize($data)
  136. {
  137. ClosureStream::register();
  138. $this->code = $data;
  139. unset($data);
  140. $this->code['objects'] = [];
  141. if ($this->code['use']) {
  142. $this->scope = new ClosureScope();
  143. if (static::$resolveUseVariables) {
  144. $this->code['use'] = call_user_func(static::$resolveUseVariables, $this->code['use']);
  145. }
  146. $this->mapPointers($this->code['use']);
  147. extract($this->code['use'], EXTR_OVERWRITE | EXTR_REFS);
  148. $this->scope = null;
  149. }
  150. $this->closure = include ClosureStream::STREAM_PROTO.'://'.$this->code['function'];
  151. if ($this->code['this'] === $this) {
  152. $this->code['this'] = null;
  153. }
  154. $this->closure = $this->closure->bindTo($this->code['this'], $this->code['scope']);
  155. if (! empty($this->code['objects'])) {
  156. foreach ($this->code['objects'] as $item) {
  157. $item['property']->setValue($item['instance'], $item['object']->getClosure());
  158. }
  159. }
  160. $this->code = $this->code['function'];
  161. }
  162. /**
  163. * Ensures the given closures are serializable.
  164. *
  165. * @param mixed $data
  166. * @param \Laravel\SerializableClosure\Support\ClosureScope $storage
  167. * @return void
  168. */
  169. public static function wrapClosures(&$data, $storage)
  170. {
  171. if ($data instanceof Closure) {
  172. $data = new static($data);
  173. } elseif (is_array($data)) {
  174. if (isset($data[self::ARRAY_RECURSIVE_KEY])) {
  175. return;
  176. }
  177. $data[self::ARRAY_RECURSIVE_KEY] = true;
  178. foreach ($data as $key => &$value) {
  179. if ($key === self::ARRAY_RECURSIVE_KEY) {
  180. continue;
  181. }
  182. static::wrapClosures($value, $storage);
  183. }
  184. unset($value);
  185. unset($data[self::ARRAY_RECURSIVE_KEY]);
  186. } elseif ($data instanceof \stdClass) {
  187. if (isset($storage[$data])) {
  188. $data = $storage[$data];
  189. return;
  190. }
  191. $data = $storage[$data] = clone $data;
  192. foreach ($data as &$value) {
  193. static::wrapClosures($value, $storage);
  194. }
  195. unset($value);
  196. } elseif (is_object($data) && ! $data instanceof static && ! $data instanceof UnitEnum) {
  197. if (isset($storage[$data])) {
  198. $data = $storage[$data];
  199. return;
  200. }
  201. $instance = $data;
  202. $reflection = new ReflectionObject($instance);
  203. if (! $reflection->isUserDefined()) {
  204. $storage[$instance] = $data;
  205. return;
  206. }
  207. $storage[$instance] = $data = $reflection->newInstanceWithoutConstructor();
  208. do {
  209. if (! $reflection->isUserDefined()) {
  210. break;
  211. }
  212. foreach ($reflection->getProperties() as $property) {
  213. if ($property->isStatic() || ! $property->getDeclaringClass()->isUserDefined()) {
  214. continue;
  215. }
  216. $property->setAccessible(true);
  217. if (PHP_VERSION >= 7.4 && ! $property->isInitialized($instance)) {
  218. continue;
  219. }
  220. $value = $property->getValue($instance);
  221. if (is_array($value) || is_object($value)) {
  222. static::wrapClosures($value, $storage);
  223. }
  224. $property->setValue($data, $value);
  225. }
  226. } while ($reflection = $reflection->getParentClass());
  227. }
  228. }
  229. /**
  230. * Gets the closure's reflector.
  231. *
  232. * @return \Laravel\SerializableClosure\Support\ReflectionClosure
  233. */
  234. public function getReflector()
  235. {
  236. if ($this->reflector === null) {
  237. $this->code = null;
  238. $this->reflector = new ReflectionClosure($this->closure);
  239. }
  240. return $this->reflector;
  241. }
  242. /**
  243. * Internal method used to map closure pointers.
  244. *
  245. * @param mixed $data
  246. * @return void
  247. */
  248. protected function mapPointers(&$data)
  249. {
  250. $scope = $this->scope;
  251. if ($data instanceof static) {
  252. $data = &$data->closure;
  253. } elseif (is_array($data)) {
  254. if (isset($data[self::ARRAY_RECURSIVE_KEY])) {
  255. return;
  256. }
  257. $data[self::ARRAY_RECURSIVE_KEY] = true;
  258. foreach ($data as $key => &$value) {
  259. if ($key === self::ARRAY_RECURSIVE_KEY) {
  260. continue;
  261. } elseif ($value instanceof static) {
  262. $data[$key] = &$value->closure;
  263. } elseif ($value instanceof SelfReference && $value->hash === $this->code['self']) {
  264. $data[$key] = &$this->closure;
  265. } else {
  266. $this->mapPointers($value);
  267. }
  268. }
  269. unset($value);
  270. unset($data[self::ARRAY_RECURSIVE_KEY]);
  271. } elseif ($data instanceof \stdClass) {
  272. if (isset($scope[$data])) {
  273. return;
  274. }
  275. $scope[$data] = true;
  276. foreach ($data as $key => &$value) {
  277. if ($value instanceof SelfReference && $value->hash === $this->code['self']) {
  278. $data->{$key} = &$this->closure;
  279. } elseif (is_array($value) || is_object($value)) {
  280. $this->mapPointers($value);
  281. }
  282. }
  283. unset($value);
  284. } elseif (is_object($data) && ! ($data instanceof Closure)) {
  285. if (isset($scope[$data])) {
  286. return;
  287. }
  288. $scope[$data] = true;
  289. $reflection = new ReflectionObject($data);
  290. do {
  291. if (! $reflection->isUserDefined()) {
  292. break;
  293. }
  294. foreach ($reflection->getProperties() as $property) {
  295. if ($property->isStatic() || ! $property->getDeclaringClass()->isUserDefined()) {
  296. continue;
  297. }
  298. $property->setAccessible(true);
  299. if (PHP_VERSION >= 7.4 && ! $property->isInitialized($data)) {
  300. continue;
  301. }
  302. $item = $property->getValue($data);
  303. if ($item instanceof SerializableClosure || ($item instanceof SelfReference && $item->hash === $this->code['self'])) {
  304. $this->code['objects'][] = [
  305. 'instance' => $data,
  306. 'property' => $property,
  307. 'object' => $item instanceof SelfReference ? $this : $item,
  308. ];
  309. } elseif (is_array($item) || is_object($item)) {
  310. $this->mapPointers($item);
  311. $property->setValue($data, $item);
  312. }
  313. }
  314. } while ($reflection = $reflection->getParentClass());
  315. }
  316. }
  317. /**
  318. * Internal method used to map closures by reference.
  319. *
  320. * @param mixed $data
  321. * @return void
  322. */
  323. protected function mapByReference(&$data)
  324. {
  325. if ($data instanceof Closure) {
  326. if ($data === $this->closure) {
  327. $data = new SelfReference($this->reference);
  328. return;
  329. }
  330. if (isset($this->scope[$data])) {
  331. $data = $this->scope[$data];
  332. return;
  333. }
  334. $instance = new static($data);
  335. $instance->scope = $this->scope;
  336. $data = $this->scope[$data] = $instance;
  337. } elseif (is_array($data)) {
  338. if (isset($data[self::ARRAY_RECURSIVE_KEY])) {
  339. return;
  340. }
  341. $data[self::ARRAY_RECURSIVE_KEY] = true;
  342. foreach ($data as $key => &$value) {
  343. if ($key === self::ARRAY_RECURSIVE_KEY) {
  344. continue;
  345. }
  346. $this->mapByReference($value);
  347. }
  348. unset($value);
  349. unset($data[self::ARRAY_RECURSIVE_KEY]);
  350. } elseif ($data instanceof \stdClass) {
  351. if (isset($this->scope[$data])) {
  352. $data = $this->scope[$data];
  353. return;
  354. }
  355. $instance = $data;
  356. $this->scope[$instance] = $data = clone $data;
  357. foreach ($data as &$value) {
  358. $this->mapByReference($value);
  359. }
  360. unset($value);
  361. } elseif (is_object($data) && ! $data instanceof SerializableClosure) {
  362. if (isset($this->scope[$data])) {
  363. $data = $this->scope[$data];
  364. return;
  365. }
  366. $instance = $data;
  367. if ($data instanceof UnitEnum) {
  368. $this->scope[$instance] = $data;
  369. return;
  370. }
  371. $reflection = new ReflectionObject($data);
  372. if (! $reflection->isUserDefined()) {
  373. $this->scope[$instance] = $data;
  374. return;
  375. }
  376. $this->scope[$instance] = $data = $reflection->newInstanceWithoutConstructor();
  377. do {
  378. if (! $reflection->isUserDefined()) {
  379. break;
  380. }
  381. foreach ($reflection->getProperties() as $property) {
  382. if ($property->isStatic() || ! $property->getDeclaringClass()->isUserDefined()) {
  383. continue;
  384. }
  385. $property->setAccessible(true);
  386. if (PHP_VERSION >= 7.4 && ! $property->isInitialized($instance)) {
  387. continue;
  388. }
  389. $value = $property->getValue($instance);
  390. if (is_array($value) || is_object($value)) {
  391. $this->mapByReference($value);
  392. }
  393. $property->setValue($data, $value);
  394. }
  395. } while ($reflection = $reflection->getParentClass());
  396. }
  397. }
  398. }