SerializerTest.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646
  1. <?php
  2. use Carbon\Carbon;
  3. use Carbon\CarbonImmutable;
  4. use Laravel\SerializableClosure\SerializableClosure;
  5. use Laravel\SerializableClosure\Serializers\Signed;
  6. use Laravel\SerializableClosure\Support\ReflectionClosure;
  7. use Laravel\SerializableClosure\UnsignedSerializableClosure;
  8. use Tests\Fixtures\Model;
  9. test('closure with simple const', function () {
  10. $c = function () {
  11. return ObjWithConst::FOO;
  12. };
  13. $u = s($c)();
  14. expect($u)->toBe('bar');
  15. })->with('serializers');
  16. test('closure use return value', function () {
  17. $a = 100;
  18. $c = function () use ($a) {
  19. return $a;
  20. };
  21. $u = s($c);
  22. expect($a)->toEqual($u());
  23. })->with('serializers');
  24. test('closure use transformation with Native', function () {
  25. $a = 100;
  26. SerializableClosure::transformUseVariablesUsing(function ($data) {
  27. foreach ($data as $key => $value) {
  28. $data[$key] = $value * 2;
  29. }
  30. return $data;
  31. });
  32. SerializableClosure::resolveUseVariablesUsing(function ($data) {
  33. foreach ($data as $key => $value) {
  34. $data[$key] = $value / 4;
  35. }
  36. return $data;
  37. });
  38. if ($this->serializer == UnsignedSerializableClosure::class) {
  39. $c = unserialize(serialize(SerializableClosure::unsigned(function () use ($a) {
  40. return $a;
  41. })));
  42. } else {
  43. $c = unserialize(serialize(new SerializableClosure(function () use ($a) {
  44. return $a;
  45. })));
  46. }
  47. expect($c())->toEqual(50);
  48. })->with('serializers')->skip((float) phpversion() < '7.4');
  49. test('closure use transformation with Signed', function () {
  50. $a = 100;
  51. SerializableClosure::setSecretKey('secret');
  52. SerializableClosure::transformUseVariablesUsing(function ($data) {
  53. foreach ($data as $key => $value) {
  54. $data[$key] = $value * 2;
  55. }
  56. return $data;
  57. });
  58. SerializableClosure::resolveUseVariablesUsing(function ($data) {
  59. foreach ($data as $key => $value) {
  60. $data[$key] = $value / 4;
  61. }
  62. return $data;
  63. });
  64. if ($this->serializer == UnsignedSerializableClosure::class) {
  65. $c = unserialize(serialize(SerializableClosure::unsigned(function () use ($a) {
  66. return $a;
  67. })));
  68. } else {
  69. $c = unserialize(serialize(new SerializableClosure(function () use ($a) {
  70. return $a;
  71. })));
  72. }
  73. expect($c())->toEqual(50);
  74. })->with('serializers')->skip((float) phpversion() < '7.4');
  75. test('closure use return closure', function () {
  76. $a = function ($p) {
  77. return $p + 1;
  78. };
  79. $b = function ($p) use ($a) {
  80. return $a($p);
  81. };
  82. $v = 1;
  83. $u = s($b);
  84. expect($u(1))->toEqual($v + 1);
  85. })->with('serializers');
  86. test('closure use return closure by ref', function () {
  87. $a = function ($p) {
  88. return $p + 1;
  89. };
  90. $b = function ($p) use (&$a) {
  91. return $a($p);
  92. };
  93. $v = 1;
  94. $u = s($b);
  95. expect($u(1))->toEqual($v + 1);
  96. })->with('serializers');
  97. test('closure use self', function () {
  98. $a = function () use (&$a) {
  99. return $a;
  100. };
  101. $u = s($a);
  102. expect($u())->toEqual($u);
  103. })->with('serializers');
  104. test('closure use self in array', function () {
  105. $a = [];
  106. $b = function () use (&$a) {
  107. return $a[0];
  108. };
  109. $a[] = $b;
  110. $u = s($b);
  111. expect($u())->toEqual($u);
  112. })->with('serializers');
  113. test('closure use self in object', function () {
  114. $a = new stdClass();
  115. $b = function () use (&$a) {
  116. return $a->me;
  117. };
  118. $a->me = $b;
  119. $u = s($b);
  120. expect($u())->toEqual($u);
  121. })->with('serializers');
  122. test('closure use self in multi array', function () {
  123. $a = [];
  124. $x = null;
  125. $b = function () use (&$x) {
  126. return $x;
  127. };
  128. $c = function ($i) use (&$a) {
  129. $f = $a[$i];
  130. return $f();
  131. };
  132. $a[] = $b;
  133. $a[] = $c;
  134. $x = $c;
  135. $u = s($c);
  136. expect($u(0))->toEqual($u);
  137. })->with('serializers');
  138. test('closure use self in instance', function () {
  139. $i = new ObjSelf();
  140. $c = function ($c) use ($i) {
  141. return $c === $i->o;
  142. };
  143. $i->o = $c;
  144. $u = s($c);
  145. expect($u($u))->toBeTrue();
  146. })->with('serializers');
  147. test('closure use self in instance2', function () {
  148. $i = new ObjSelf();
  149. $c = function () use (&$c, $i) {
  150. return $c == $i->o;
  151. };
  152. $i->o = &$c;
  153. $u = s($c);
  154. expect($u())->toBeTrue();
  155. })->with('serializers');
  156. test('closure serialization twice', function () {
  157. $a = function ($p) {
  158. return $p;
  159. };
  160. $b = function ($p) use ($a) {
  161. return $a($p);
  162. };
  163. $u = s(s($b));
  164. expect($u('ok'))->toEqual('ok');
  165. })->with('serializers');
  166. test('closure real serialization', function () {
  167. $f = function ($a, $b) {
  168. return $a + $b;
  169. };
  170. $u = s(s($f));
  171. expect($u(2, 3))->toEqual(5);
  172. })->with('serializers');
  173. test('closure nested', function () {
  174. $o = function ($a) {
  175. // this should never happen
  176. if ($a === false) {
  177. return false;
  178. }
  179. $n = function ($b) {
  180. return ! $b;
  181. };
  182. $ns = s($n);
  183. return $ns(false);
  184. };
  185. $os = s($o);
  186. expect($os(true))->toEqual(true);
  187. })->with('serializers');
  188. test('closure curly syntax', function () {
  189. $f = function () {
  190. $x = (object) ['a' => 1, 'b' => 3];
  191. $b = 'b';
  192. return $x->{'a'} + $x->{$b};
  193. };
  194. $f = s($f);
  195. expect($f())->toEqual(4);
  196. })->with('serializers');
  197. test('closure bind to object', function () {
  198. $a = new A();
  199. $b = function () {
  200. return $this->aPublic();
  201. };
  202. $b = $b->bindTo($a, __NAMESPACE__.'\\A');
  203. $u = s($b);
  204. expect($u())->toEqual('public called');
  205. })->with('serializers');
  206. test('closure bind to object scope', function () {
  207. $a = new A();
  208. $b = function () {
  209. return $this->aProtected();
  210. };
  211. $b = $b->bindTo($a, __NAMESPACE__.'\\A');
  212. $u = s($b);
  213. expect($u())->toEqual('protected called');
  214. })->with('serializers');
  215. test('closure bind to object static scope', function () {
  216. $a = new A();
  217. $b = function () {
  218. return static::aStaticProtected();
  219. };
  220. $b = $b->bindTo(null, __NAMESPACE__.'\\A');
  221. $u = s($b);
  222. expect($u())->toEqual('static protected called');
  223. })->with('serializers');
  224. test('closure static', function () {
  225. $f = static function () {
  226. };
  227. $rc = new ReflectionClosure($f);
  228. expect($rc->isStatic())->toBeTrue();
  229. })->with('serializers');
  230. test('closure static fail', function () {
  231. $f = static // This will not work
  232. function () {
  233. };
  234. $rc = new ReflectionClosure($f);
  235. expect($rc->isStatic())->toBeFalse();
  236. })->with('serializers');
  237. test('closure scope remains the same', function () {
  238. $f = function () {
  239. static $i = 0;
  240. };
  241. $o = s($f);
  242. $rf = new ReflectionClosure($f);
  243. $ro = new ReflectionClosure($o);
  244. test()->assertNotNull($rf->getClosureScopeClass());
  245. test()->assertNotNull($ro->getClosureScopeClass());
  246. expect($ro->getClosureScopeClass()->name)->toEqual($rf->getClosureScopeClass()->name);
  247. expect($ro->getClosureScopeClass()->name)->toEqual($rf->getClosureScopeClass()->name);
  248. $f = $f->bindTo(null, null);
  249. $o = s($f);
  250. $rf = new ReflectionClosure($f);
  251. $ro = new ReflectionClosure($o);
  252. expect($rf->getClosureScopeClass())->toBeNull();
  253. expect($ro->getClosureScopeClass())->toBeNull();
  254. })->with('serializers');
  255. test('mixed encodings', function () {
  256. $a = iconv('utf-8', 'utf-16', 'Düsseldorf');
  257. $b = mb_convert_encoding('Düsseldorf', 'ISO-8859-1', 'UTF-8');
  258. $closure = function () use ($a, $b) {
  259. return [$a, $b];
  260. };
  261. $u = s($closure);
  262. $r = $u();
  263. expect($r[0])->toEqual($a);
  264. expect($r[1])->toEqual($b);
  265. })->with('serializers');
  266. test('serializable closure serialization string content dont change', function () {
  267. $a = 100;
  268. SerializableClosure::setSecretKey('foo');
  269. $c = new SerializableClosure(function () use ($a) {
  270. return $a;
  271. });
  272. $actual = explode('s:32:', serialize($c))[0];
  273. expect($actual)->toBe(<<<OEF
  274. O:47:"Laravel\SerializableClosure\SerializableClosure":1:{s:12:"serializable";O:46:"Laravel\SerializableClosure\Serializers\Signed":2:{s:12:"serializable";s:264:"O:46:"Laravel\SerializableClosure\Serializers\Native":5:{s:3:"use";a:1:{s:1:"a";i:100;}s:8:"function";s:47:"function () use (\$a) {
  275. return \$a;
  276. }";s:5:"scope";s:22:"P\Tests\SerializerTest";s:4:"this";N;s:4:"self";
  277. OEF
  278. );
  279. });
  280. test('unsigned serializable closure serialization string content dont change', function () {
  281. $a = 100;
  282. SerializableClosure::setSecretKey('foo');
  283. $c = SerializableClosure::unsigned(function () use ($a) {
  284. return $a;
  285. });
  286. $actual = explode('s:32:', serialize($c))[0];
  287. expect($actual)->toBe(<<<OEF
  288. O:55:"Laravel\SerializableClosure\UnsignedSerializableClosure":1:{s:12:"serializable";O:46:"Laravel\SerializableClosure\Serializers\Native":5:{s:3:"use";a:1:{s:1:"a";i:100;}s:8:"function";s:47:"function () use (\$a) {
  289. return \$a;
  290. }";s:5:"scope";s:22:"P\Tests\SerializerTest";s:4:"this";N;s:4:"self";
  291. OEF
  292. );
  293. });
  294. test('use objects with serializable closures properties', function () {
  295. $a = new stdClass();
  296. if ($this->serializer == Signed::class) {
  297. SerializableClosure::setSecretKey('secret');
  298. }
  299. if ($this->serializer == UnsignedSerializableClosure::class) {
  300. $a->b = SerializableClosure::unsigned(function () {
  301. return 'Hi';
  302. });
  303. } else {
  304. $a->b = new SerializableClosure(function () {
  305. return 'Hi';
  306. });
  307. }
  308. $closure = function () use ($a) {
  309. return ($a->b)();
  310. };
  311. $u = s($closure);
  312. $r = $u();
  313. expect($r)->toEqual('Hi');
  314. })->with('serializers');
  315. test('rebound closure', function () {
  316. $closure = Closure::bind(
  317. function () {
  318. return $this->hello();
  319. },
  320. new A3(function () {
  321. return 'Hi';
  322. }),
  323. A3::class
  324. );
  325. $u = s($closure);
  326. $r = $u();
  327. expect($r)->toEqual('Hi');
  328. })->with('serializers');
  329. test('from callable namespaces', function () {
  330. $f = Closure::fromCallable([new Model, 'make']);
  331. $f = s($f);
  332. expect($f(new Model))->toBeInstanceOf(Model::class);
  333. })->with('serializers');
  334. test('from static callable namespaces', function () {
  335. $f = Closure::fromCallable([new Model, 'staticMake']);
  336. $f = s($f);
  337. expect($f(new Model))->toBeInstanceOf(Model::class);
  338. })->with('serializers');
  339. test('serializes used dates', function ($_, $date) {
  340. $closure = function () use ($date) {
  341. return $date;
  342. };
  343. $u = s($closure);
  344. $r = $u();
  345. expect($r)->toEqual($date);
  346. })->with('serializers')->with([
  347. new DateTime,
  348. new DateTimeImmutable,
  349. new Carbon,
  350. new CarbonImmutable,
  351. ]);
  352. test('serializes with used object date properties', function ($_, $date) {
  353. $obj = new ObjSelf;
  354. $obj->o = $date;
  355. $closure = function () use ($obj) {
  356. return $obj;
  357. };
  358. $u = s($closure);
  359. $r = $u();
  360. expect($r->o)->toEqual($date);
  361. })->with('serializers')->with([
  362. new DateTime,
  363. new DateTimeImmutable,
  364. new Carbon,
  365. new CarbonImmutable,
  366. ]);
  367. function serializer_php_74_switch_statement_test_is_two($a)
  368. {
  369. return $a === 2;
  370. }
  371. class SerializerPhp74SwitchStatementClass
  372. {
  373. public static function isThree($a)
  374. {
  375. return $a === 3;
  376. }
  377. public function isFour($a)
  378. {
  379. return $a === 4;
  380. }
  381. }
  382. class SerializerPhp74Class
  383. {
  384. }
  385. test('instanceof', function () {
  386. $closure = function ($a) {
  387. $b = $a instanceof DateTime || $a instanceof SerializerPhp74Class || $a instanceof Model;
  388. return [
  389. $b,
  390. $a instanceof DateTime || $a instanceof SerializerPhp74Class || $a instanceof Model,
  391. (function ($a) {
  392. return ($a instanceof DateTime || $a instanceof SerializerPhp74Class || $a instanceof Model) === true;
  393. })($a),
  394. ];
  395. };
  396. $u = s($closure);
  397. expect($u(new DateTime))->toEqual([true, true, true])
  398. ->and($u(new SerializerPhp74Class))->toEqual([true, true, true])
  399. ->and($u(new Model))->toEqual([true, true, true])
  400. ->and($u(new stdClass))->toEqual([false, false, false]);
  401. })->with('serializers');
  402. test('switch statement', function () {
  403. $closure = function ($a) {
  404. switch (true) {
  405. case $a === 1:
  406. return 'one';
  407. case serializer_php_74_switch_statement_test_is_two($a):
  408. return 'two';
  409. case SerializerPhp74SwitchStatementClass::isThree($a):
  410. return 'three';
  411. case (new SerializerPhp74SwitchStatementClass)->isFour($a):
  412. return 'four';
  413. case $a instanceof SerializerPhp74SwitchStatementClass:
  414. return 'five';
  415. case $a instanceof DateTime:
  416. return 'six';
  417. case $a instanceof Model:
  418. return 'seven';
  419. default:
  420. return 'other';
  421. }
  422. };
  423. $u = s($closure);
  424. expect($u(1))->toEqual('one')
  425. ->and($u(2))->toEqual('two')
  426. ->and($u(3))->toEqual('three')
  427. ->and($u(4))->toEqual('four')
  428. ->and($u(new SerializerPhp74SwitchStatementClass))->toEqual('five')
  429. ->and($u(new DateTime))->toEqual('six')
  430. ->and($u(new Model()))->toEqual('seven')
  431. ->and($u(999))->toEqual('other');
  432. })->with('serializers');
  433. class A
  434. {
  435. protected static function aStaticProtected()
  436. {
  437. return 'static protected called';
  438. }
  439. protected function aProtected()
  440. {
  441. return 'protected called';
  442. }
  443. public function aPublic()
  444. {
  445. return 'public called';
  446. }
  447. }
  448. class A2
  449. {
  450. private $phrase = 'Hello, World!';
  451. private $closure1;
  452. private $closure2;
  453. private $closure3;
  454. public function __construct()
  455. {
  456. $this->closure1 = function () {
  457. return $this->phrase;
  458. };
  459. $this->closure2 = function () {
  460. return $this;
  461. };
  462. $this->closure3 = function () {
  463. $c = $this->closure2;
  464. return $this === $c();
  465. };
  466. }
  467. public function getPhrase()
  468. {
  469. $c = $this->closure1;
  470. return $c();
  471. }
  472. public function getEquality()
  473. {
  474. $c = $this->closure3;
  475. return $c();
  476. }
  477. }
  478. class A3
  479. {
  480. private $closure;
  481. public function __construct($closure)
  482. {
  483. $this->closure = $closure;
  484. }
  485. public function hello()
  486. {
  487. return ($this->closure)();
  488. }
  489. }
  490. class ObjSelf
  491. {
  492. public $o;
  493. }
  494. class ObjWithConst
  495. {
  496. const FOO = 'bar';
  497. }