DepthGuard.php 1.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * This file is part of Hyperf.
  5. *
  6. * @link https://www.hyperf.io
  7. * @document https://hyperf.wiki
  8. * @contact group@hyperf.io
  9. * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  10. */
  11. namespace Hyperf\Di\Resolver;
  12. use Hyperf\Context\Context;
  13. use Hyperf\Di\Exception\CircularDependencyException;
  14. /**
  15. * Class DepthGuard aborts the resolver after
  16. * reaching a predefined depth limit. This is
  17. * useful to detect circular dependencies.
  18. */
  19. class DepthGuard
  20. {
  21. protected int $depthLimit = 500;
  22. private static ?DepthGuard $instance = null;
  23. public static function getInstance(): self
  24. {
  25. if (! isset(self::$instance)) {
  26. self::$instance = new static();
  27. }
  28. return self::$instance;
  29. }
  30. /**
  31. * Allows user to adjust depth limit.
  32. * Should call it before di container bootstraps.
  33. */
  34. public function setDepthLimit(int $depthLimit)
  35. {
  36. $this->depthLimit = $depthLimit;
  37. }
  38. public function increment()
  39. {
  40. Context::override('di.depth', function ($depth) {
  41. $depth = $depth ?? 0;
  42. if (++$depth > $this->depthLimit) {
  43. throw new CircularDependencyException();
  44. }
  45. return $depth;
  46. });
  47. }
  48. public function decrement()
  49. {
  50. Context::override('di.depth', function ($depth) {
  51. return --$depth;
  52. });
  53. }
  54. public function call(string $name, callable $callable)
  55. {
  56. try {
  57. $this->increment();
  58. return $callable();
  59. } catch (CircularDependencyException $exception) {
  60. $exception->addDefinitionName($name);
  61. throw $exception;
  62. } finally {
  63. $this->decrement();
  64. }
  65. }
  66. }