helpers.php 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. <?php
  2. use Illuminate\Support\Traits\ReflectsClosures;
  3. if (! function_exists('lazy')) {
  4. /**
  5. * Create a lazy instance.
  6. *
  7. * @template TValue of object
  8. *
  9. * @param class-string<TValue>|(\Closure(TValue): mixed) $class
  10. * @param (\Closure(TValue): mixed)|int $callback
  11. * @param int $options
  12. * @param array<string, mixed> $eager
  13. * @return TValue
  14. */
  15. function lazy($class, $callback = 0, $options = 0, $eager = [])
  16. {
  17. static $closureReflector;
  18. $closureReflector ??= new class
  19. {
  20. use ReflectsClosures;
  21. public function typeFromParameter($callback)
  22. {
  23. return $this->firstClosureParameterType($callback);
  24. }
  25. };
  26. [$class, $callback, $options] = is_string($class)
  27. ? [$class, $callback, $options]
  28. : [$closureReflector->typeFromParameter($class), $class, $callback ?: $options];
  29. $reflectionClass = new ReflectionClass($class);
  30. $instance = $reflectionClass->newLazyGhost(function ($instance) use ($callback) {
  31. $result = $callback($instance);
  32. if (is_array($result)) {
  33. $instance->__construct(...$result);
  34. }
  35. }, $options);
  36. foreach ($eager as $property => $value) {
  37. $reflectionClass->getProperty($property)->setRawValueWithoutLazyInitialization($instance, $value);
  38. }
  39. return $instance;
  40. }
  41. }
  42. if (! function_exists('proxy')) {
  43. /**
  44. * Create a lazy proxy instance.
  45. *
  46. * @template TValue of object
  47. *
  48. * @param class-string<TValue>|(\Closure(TValue): TValue) $class
  49. * @param (\Closure(TValue): TValue)|int $callback
  50. * @param int $options
  51. * @param array<string, mixed> $eager
  52. * @return TValue
  53. */
  54. function proxy($class, $callback = 0, $options = 0, $eager = [])
  55. {
  56. static $closureReflector;
  57. $closureReflector = new class
  58. {
  59. use ReflectsClosures;
  60. public function get($callback)
  61. {
  62. return $this->closureReturnTypes($callback)[0] ?? $this->firstClosureParameterType($callback);
  63. }
  64. };
  65. [$class, $callback, $options] = is_string($class)
  66. ? [$class, $callback, $options]
  67. : [$closureReflector->get($class), $class, $callback ?: $options];
  68. $reflectionClass = new ReflectionClass($class);
  69. $proxy = $reflectionClass->newLazyProxy(function () use ($callback, $eager, &$proxy) {
  70. $instance = $callback($proxy, $eager);
  71. return $instance;
  72. }, $options);
  73. foreach ($eager as $property => $value) {
  74. $reflectionClass->getProperty($property)->setRawValueWithoutLazyInitialization($proxy, $value);
  75. }
  76. return $proxy;
  77. }
  78. }