Onceable.php 2.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. <?php
  2. namespace Illuminate\Support;
  3. use Closure;
  4. use Illuminate\Contracts\Support\HasOnceHash;
  5. use Laravel\SerializableClosure\Support\ReflectionClosure;
  6. class Onceable
  7. {
  8. /**
  9. * Create a new onceable instance.
  10. *
  11. * @param string $hash
  12. * @param object|null $object
  13. * @param callable $callable
  14. */
  15. public function __construct(
  16. public string $hash,
  17. public ?object $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. static function (mixed $argument) {
  58. if ($argument instanceof HasOnceHash) {
  59. return $argument->onceHash();
  60. }
  61. if (is_object($argument)) {
  62. return spl_object_hash($argument);
  63. }
  64. return $argument;
  65. },
  66. $callable instanceof Closure ? (new ReflectionClosure($callable))->getClosureUsedVariables() : [],
  67. );
  68. $class = $callable instanceof Closure ? (new ReflectionClosure($callable))->getClosureCalledClass()?->getName() : null;
  69. $class ??= isset($trace[1]['class']) ? $trace[1]['class'] : null;
  70. return hash('xxh128', sprintf(
  71. '%s@%s%s:%s (%s)',
  72. $trace[0]['file'],
  73. $class ? $class.'@' : '',
  74. $trace[1]['function'],
  75. $trace[0]['line'],
  76. serialize($uses),
  77. ));
  78. }
  79. }