Onceable.php 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. <?php
  2. namespace Illuminate\Support;
  3. use Closure;
  4. use Laravel\SerializableClosure\Support\ReflectionClosure;
  5. class Onceable
  6. {
  7. /**
  8. * Create a new onceable instance.
  9. *
  10. * @param string $hash
  11. * @param object|null $object
  12. * @param callable $callable
  13. * @return void
  14. */
  15. public function __construct(
  16. public string $hash,
  17. public object|null $object,
  18. public $callable
  19. ) {
  20. //
  21. }
  22. /**
  23. * Tries to create a new onceable instance from the given trace.
  24. *
  25. * @param array<int, array<string, mixed>> $trace
  26. * @return static|null
  27. */
  28. public static function tryFromTrace(array $trace, callable $callable)
  29. {
  30. if (! is_null($hash = static::hashFromTrace($trace, $callable))) {
  31. $object = static::objectFromTrace($trace);
  32. return new static($hash, $object, $callable);
  33. }
  34. }
  35. /**
  36. * Computes the object of the onceable from the given trace, if any.
  37. *
  38. * @param array<int, array<string, mixed>> $trace
  39. * @return object|null
  40. */
  41. protected static function objectFromTrace(array $trace)
  42. {
  43. return $trace[1]['object'] ?? null;
  44. }
  45. /**
  46. * Computes the hash of the onceable from the given trace.
  47. *
  48. * @param array<int, array<string, mixed>> $trace
  49. * @return string|null
  50. */
  51. protected static function hashFromTrace(array $trace, callable $callable)
  52. {
  53. if (str_contains($trace[0]['file'] ?? '', 'eval()\'d code')) {
  54. return null;
  55. }
  56. $uses = array_map(
  57. fn (mixed $argument) => is_object($argument) ? spl_object_hash($argument) : $argument,
  58. $callable instanceof Closure ? (new ReflectionClosure($callable))->getClosureUsedVariables() : [],
  59. );
  60. return md5(sprintf(
  61. '%s@%s%s:%s (%s)',
  62. $trace[0]['file'],
  63. isset($trace[1]['class']) ? ($trace[1]['class'].'@') : '',
  64. $trace[1]['function'],
  65. $trace[0]['line'],
  66. serialize($uses),
  67. ));
  68. }
  69. }