Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
83.33% |
60 / 72 |
|
42.86% |
3 / 7 |
CRAP | |
0.00% |
0 / 1 |
| EntityUtils | |
83.33% |
60 / 72 |
|
42.86% |
3 / 7 |
38.04 | |
0.00% |
0 / 1 |
| __construct | n/a |
0 / 0 |
n/a |
0 / 0 |
1 | |||||
| isEntity | |
66.67% |
4 / 6 |
|
0.00% |
0 / 1 |
4.59 | |||
| isNonPolymorphicEntity | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
30 | |||
| isPolymorphicEntity | |
66.67% |
4 / 6 |
|
0.00% |
0 / 1 |
4.59 | |||
| getDiscriminatorValues | |
100.00% |
19 / 19 |
|
100.00% |
1 / 1 |
7 | |||
| getDiscriminatorClasses | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
3 | |||
| getContextParameters | |
100.00% |
12 / 12 |
|
100.00% |
1 / 1 |
5 | |||
| findClass | |
92.86% |
13 / 14 |
|
0.00% |
0 / 1 |
4.01 | |||
| 1 | <?php |
| 2 | namespace Apie\Core\Utils; |
| 3 | |
| 4 | use Apie\Core\Attributes\Context; |
| 5 | use Apie\Core\Entities\EntityInterface; |
| 6 | use Apie\Core\Entities\PolymorphicEntityInterface; |
| 7 | use Apie\Core\Exceptions\IndexNotFoundException; |
| 8 | use Apie\Core\Lists\ReflectionClassList; |
| 9 | use Apie\Core\Other\DiscriminatorMapping; |
| 10 | use ReflectionClass; |
| 11 | use ReflectionException; |
| 12 | use ReflectionMethod; |
| 13 | use ReflectionParameter; |
| 14 | use ReflectionProperty; |
| 15 | use ReflectionType; |
| 16 | |
| 17 | final class EntityUtils |
| 18 | { |
| 19 | /** |
| 20 | * @codeCoverageIgnore |
| 21 | */ |
| 22 | private function __construct() |
| 23 | { |
| 24 | } |
| 25 | |
| 26 | /** |
| 27 | * @param string|ReflectionClass<object>|ReflectionProperty|ReflectionType|ReflectionMethod $input |
| 28 | */ |
| 29 | public static function isEntity(string|ReflectionClass|ReflectionProperty|ReflectionType|ReflectionMethod $input): bool |
| 30 | { |
| 31 | try { |
| 32 | $class = ConverterUtils::toReflectionClass($input); |
| 33 | } catch (ReflectionException) { |
| 34 | return false; |
| 35 | } |
| 36 | return $class !== null |
| 37 | && $class->implementsInterface(EntityInterface::class) |
| 38 | && !$class->isInterface(); |
| 39 | } |
| 40 | |
| 41 | /** |
| 42 | * @param string|ReflectionClass<object>|ReflectionProperty|ReflectionType|ReflectionMethod $input |
| 43 | */ |
| 44 | public static function isNonPolymorphicEntity(string|ReflectionClass|ReflectionProperty|ReflectionType|ReflectionMethod $input): bool |
| 45 | { |
| 46 | try { |
| 47 | $class = ConverterUtils::toReflectionClass($input); |
| 48 | } catch (ReflectionException) { |
| 49 | return false; |
| 50 | } |
| 51 | return $class !== null |
| 52 | && !$class->implementsInterface(PolymorphicEntityInterface::class) |
| 53 | && $class->implementsInterface(EntityInterface::class) |
| 54 | && !$class->isInterface(); |
| 55 | } |
| 56 | |
| 57 | /** |
| 58 | * @param string|ReflectionClass<object>|ReflectionProperty|ReflectionType|ReflectionMethod $input |
| 59 | */ |
| 60 | public static function isPolymorphicEntity(string|ReflectionClass|ReflectionProperty|ReflectionType|ReflectionMethod $input): bool |
| 61 | { |
| 62 | try { |
| 63 | $class = ConverterUtils::toReflectionClass($input); |
| 64 | } catch (ReflectionException) { |
| 65 | return false; |
| 66 | } |
| 67 | return $class !== null |
| 68 | && $class->implementsInterface(PolymorphicEntityInterface::class) |
| 69 | && !$class->isInterface(); |
| 70 | } |
| 71 | |
| 72 | /** |
| 73 | * @param PolymorphicEntityInterface|ReflectionClass<PolymorphicEntityInterface> $entity |
| 74 | * @param ReflectionClass<PolymorphicEntityInterface>|null $base |
| 75 | * @return array<string, string> |
| 76 | */ |
| 77 | public static function getDiscriminatorValues(PolymorphicEntityInterface|ReflectionClass $entity, ?ReflectionClass $base = null): array |
| 78 | { |
| 79 | $currentRefl = $entity instanceof ReflectionClass ? $entity : new ReflectionClass($entity); |
| 80 | if (!$base) { |
| 81 | $refl = $currentRefl; |
| 82 | while ($refl) { |
| 83 | if ($refl->getMethod('getDiscriminatorMapping')->getDeclaringClass()->name === $refl->name) { |
| 84 | $base = $refl; |
| 85 | } |
| 86 | $refl = $refl->getParentClass(); |
| 87 | } |
| 88 | } |
| 89 | assert($base !== null); |
| 90 | $entityClass = $currentRefl->name; |
| 91 | $result = []; |
| 92 | $current = $base; |
| 93 | $last = null; |
| 94 | while ($current->getMethod('getDiscriminatorMapping')->getDeclaringClass()->name !== $last && $current->name !== $entityClass) { |
| 95 | /** @var DiscriminatorMapping $mapping */ |
| 96 | $mapping = $current->getMethod('getDiscriminatorMapping')->invoke(null); |
| 97 | $config = $mapping->getConfigForClass($entity); |
| 98 | $result[$mapping->getPropertyName()] = $config->getDiscriminator(); |
| 99 | $last = $current->getMethod('getDiscriminatorMapping')->getDeclaringClass()->name; |
| 100 | $current = new ReflectionClass($config->getClassName()); |
| 101 | } |
| 102 | return $result; |
| 103 | } |
| 104 | |
| 105 | /** |
| 106 | * @param ReflectionClass<PolymorphicEntityInterface> $base |
| 107 | */ |
| 108 | public static function getDiscriminatorClasses(ReflectionClass $base): ReflectionClassList |
| 109 | { |
| 110 | $list = []; |
| 111 | /** @var DiscriminatorMapping $mapping */ |
| 112 | $mapping = $base->getMethod('getDiscriminatorMapping')->invoke(null); |
| 113 | foreach ($mapping->getConfigs() as $config) { |
| 114 | $refl = new ReflectionClass($config->getClassName()); |
| 115 | if ($refl->isInstantiable()) { |
| 116 | $list[] = $refl; |
| 117 | } else { |
| 118 | $list = [...$list, ...self::getDiscriminatorClasses($refl)]; |
| 119 | } |
| 120 | } |
| 121 | return new ReflectionClassList($list); |
| 122 | } |
| 123 | |
| 124 | /** |
| 125 | * Returns context related parameters of a method. This depends on the method type. |
| 126 | * A context related parameter gets his value not from user input, but from a |
| 127 | * Apie context variable. |
| 128 | * |
| 129 | * @return ReflectionParameter[] |
| 130 | */ |
| 131 | public static function getContextParameters(ReflectionMethod $method): array |
| 132 | { |
| 133 | // getters: all arguments are context related |
| 134 | if (preg_match('/^(get|is|has).+/i', $method->name)) { |
| 135 | return $method->getParameters(); |
| 136 | } |
| 137 | // setters: all arguments, except the last one |
| 138 | if (preg_match('/^set.+/i', $method->name)) { |
| 139 | $parameters = $method->getParameters(); |
| 140 | array_pop($parameters); |
| 141 | return $parameters; |
| 142 | } |
| 143 | // other methods: constructor, actions. Only with #[Context] attributes. |
| 144 | $parameters = []; |
| 145 | foreach ($method->getParameters() as $parameter) { |
| 146 | $attributes = $parameter->getAttributes(Context::class); |
| 147 | if (!empty($attributes)) { |
| 148 | $parameters[] = $parameter; |
| 149 | } |
| 150 | } |
| 151 | return $parameters; |
| 152 | } |
| 153 | |
| 154 | /** |
| 155 | * Converts discriminator mappings from an array into the linked class. |
| 156 | * |
| 157 | * @template T of PolymorphicEntityInterface |
| 158 | * @param array<string, string> $discriminators |
| 159 | * @param ReflectionClass<T> $base |
| 160 | * @return ReflectionClass<T> |
| 161 | */ |
| 162 | public static function findClass(array $discriminators, ReflectionClass $base): ReflectionClass |
| 163 | { |
| 164 | /** @var DiscriminatorMapping $mapping */ |
| 165 | $mapping = $base->getMethod('getDiscriminatorMapping')->invoke(null); |
| 166 | $value = $discriminators[$mapping->getPropertyName()] ?? null; |
| 167 | if (!isset($value)) { |
| 168 | throw new IndexNotFoundException($mapping->getPropertyName()); |
| 169 | } |
| 170 | $current = new ReflectionClass($mapping->getClassNameFromDiscriminator($value)); |
| 171 | $last = $base->name; |
| 172 | while ($current->getMethod('getDiscriminatorMapping')->getDeclaringClass()->name !== $last) { |
| 173 | $mapping = $current->getMethod('getDiscriminatorMapping')->invoke(null); |
| 174 | $value = $discriminators[$mapping->getPropertyName()] ?? null; |
| 175 | if (!isset($value)) { |
| 176 | throw new IndexNotFoundException($mapping->getPropertyName()); |
| 177 | } |
| 178 | $last = $current->getMethod('getDiscriminatorMapping')->getDeclaringClass()->name; |
| 179 | $current = new ReflectionClass($mapping->getClassNameFromDiscriminator($value)); |
| 180 | } |
| 181 | |
| 182 | return $current; |
| 183 | } |
| 184 | } |