Aspect.php 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  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\Aop;
  12. use Hyperf\Di\Annotation\AnnotationCollector;
  13. use Hyperf\Di\Annotation\AspectCollector;
  14. use function Hyperf\Support\value;
  15. class Aspect
  16. {
  17. /**
  18. * Parse the aspects that point at the class.
  19. */
  20. public static function parse(string $class): RewriteCollection
  21. {
  22. $rewriteCollection = new RewriteCollection($class);
  23. $container = AspectCollector::list();
  24. foreach ($container as $type => $collection) {
  25. if ($type === 'classes') {
  26. static::parseClasses($collection, $class, $rewriteCollection);
  27. } elseif ($type === 'annotations') {
  28. static::parseAnnotations($collection, $class, $rewriteCollection);
  29. }
  30. }
  31. return $rewriteCollection;
  32. }
  33. /**
  34. * @return array [isMatch, $matchedMethods]
  35. */
  36. public static function isMatchClassRule(string $target, string $rule): array
  37. {
  38. /*
  39. * e.g. Foo/Bar
  40. * e.g. Foo/B*
  41. * e.g. F*o/Bar
  42. * e.g. Foo/Bar::method
  43. * e.g. Foo/Bar::met*
  44. */
  45. $ruleMethod = null;
  46. $ruleClass = $rule;
  47. $method = null;
  48. $class = $target;
  49. if (str_contains($rule, '::')) {
  50. [$ruleClass, $ruleMethod] = explode('::', $rule);
  51. }
  52. if (str_contains($target, '::')) {
  53. [$class, $method] = explode('::', $target);
  54. }
  55. if ($method == null) {
  56. if (! str_contains($ruleClass, '*')) {
  57. /*
  58. * Match [rule] Foo/Bar::ruleMethod [target] Foo/Bar [return] true,ruleMethod
  59. * Match [rule] Foo/Bar [target] Foo/Bar [return] true,null
  60. * Match [rule] FooBar::rule*Method [target] Foo/Bar [return] true,rule*Method
  61. */
  62. if ($ruleClass === $class) {
  63. return [true, $ruleMethod];
  64. }
  65. return [false, null];
  66. }
  67. /**
  68. * Match [rule] Foo*Bar::ruleMethod [target] Foo/Bar [return] true,ruleMethod
  69. * Match [rule] Foo*Bar [target] Foo/Bar [return] true,null.
  70. */
  71. $preg = str_replace(['*', '\\'], ['.*', '\\\\'], $ruleClass);
  72. $pattern = "#^{$preg}$#";
  73. if (preg_match($pattern, $class)) {
  74. return [true, $ruleMethod];
  75. }
  76. return [false, null];
  77. }
  78. if (! str_contains($rule, '*')) {
  79. /*
  80. * Match [rule] Foo/Bar::ruleMethod [target] Foo/Bar::ruleMethod [return] true,ruleMethod
  81. * Match [rule] Foo/Bar [target] Foo/Bar::ruleMethod [return] false,null
  82. */
  83. if ($ruleClass === $class && ($ruleMethod === null || $ruleMethod === $method)) {
  84. return [true, $method];
  85. }
  86. return [false, null];
  87. }
  88. /*
  89. * Match [rule] Foo*Bar::ruleMethod [target] Foo/Bar::ruleMethod [return] true,ruleMethod
  90. * Match [rule] FooBar::rule*Method [target] Foo/Bar::ruleMethod [return] true,rule*Method
  91. */
  92. if ($ruleMethod) {
  93. $preg = str_replace(['*', '\\'], ['.*', '\\\\'], $rule);
  94. $pattern = "#^{$preg}$#";
  95. if (preg_match($pattern, $target)) {
  96. return [true, $method];
  97. }
  98. } else {
  99. /**
  100. * Match [rule] Foo*Bar [target] Foo/Bar::ruleMethod [return] true,null.
  101. */
  102. $preg = str_replace(['*', '\\'], ['.*', '\\\\'], $rule);
  103. $pattern = "#^{$preg}$#";
  104. if (preg_match($pattern, $class)) {
  105. return [true, $method];
  106. }
  107. }
  108. return [false, null];
  109. }
  110. public static function isMatch(string $class, string $method, string $rule): bool
  111. {
  112. [$isMatch] = self::isMatchClassRule($class . '::' . $method, $rule);
  113. return $isMatch;
  114. }
  115. private static function parseAnnotations(array $collection, string $class, RewriteCollection $rewriteCollection): void
  116. {
  117. // Get the annotations of class and method.
  118. $annotations = AnnotationCollector::get($class);
  119. $classMapping = $annotations['_c'] ?? [];
  120. $methodMapping = value(function () use ($annotations) {
  121. $mapping = [];
  122. $methodAnnotations = $annotations['_m'] ?? [];
  123. foreach ($methodAnnotations as $method => $targetAnnotations) {
  124. $keys = array_keys($targetAnnotations);
  125. foreach ($keys as $key) {
  126. $mapping[$key][] = $method;
  127. }
  128. }
  129. return $mapping;
  130. });
  131. $aspects = array_keys($collection);
  132. foreach ($aspects as $aspect) {
  133. $rules = AspectCollector::getRule($aspect);
  134. foreach ($rules['annotations'] ?? [] as $rule) {
  135. // If exist class level annotation, then all methods should rewrite, so return an empty array directly.
  136. if (isset($classMapping[$rule])) {
  137. $rewriteCollection->setLevel(RewriteCollection::CLASS_LEVEL);
  138. return;
  139. }
  140. if (isset($methodMapping[$rule])) {
  141. $rewriteCollection->add($methodMapping[$rule]);
  142. }
  143. }
  144. }
  145. }
  146. private static function parseClasses(array $collection, string $class, RewriteCollection $rewriteCollection): void
  147. {
  148. $aspects = array_keys($collection);
  149. foreach ($aspects as $aspect) {
  150. $rules = AspectCollector::getRule($aspect);
  151. foreach ($rules['classes'] ?? [] as $rule) {
  152. [$isMatch, $method] = static::isMatchClassRule($class, $rule);
  153. if ($isMatch) {
  154. if ($method === null) {
  155. $rewriteCollection->setLevel(RewriteCollection::CLASS_LEVEL);
  156. return;
  157. }
  158. $rewriteCollection->add($method);
  159. }
  160. }
  161. }
  162. }
  163. }