Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
74.36% covered (warning)
74.36%
29 / 39
28.57% covered (danger)
28.57%
2 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
IdentifierUtils
74.36% covered (warning)
74.36%
29 / 39
28.57% covered (danger)
28.57%
2 / 7
21.87
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 identifierToEntityClass
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 idStringToIdentifier
92.31% covered (success)
92.31%
12 / 13
0.00% covered (danger)
0.00%
0 / 1
6.02
 entityClassToIdentifier
66.67% covered (warning)
66.67%
4 / 6
0.00% covered (danger)
0.00%
0 / 1
3.33
 classNameToUnderscore
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 propertyToUnderscore
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 injectIdentifier
75.00% covered (warning)
75.00%
6 / 8
0.00% covered (danger)
0.00%
0 / 1
3.14
1<?php
2namespace Apie\Core;
3
4use Apie\Core\Context\ApieContext;
5use Apie\Core\Entities\EntityInterface;
6use Apie\Core\Exceptions\InvalidTypeException;
7use Apie\Core\Identifiers\IdentifierInterface;
8use Apie\Serializer\Serializer;
9use Exception;
10use LogicException;
11use ReflectionClass;
12use ReflectionNamedType;
13use ReflectionProperty;
14
15final class IdentifierUtils
16{
17    private function __construct()
18    {
19    }
20
21    /**
22     * @template T of EntityInterface
23     * @param ReflectionClass<IdentifierInterface<T>>|IdentifierInterface<T> $identifier
24     * @return ReflectionClass<T>
25     */
26    public static function identifierToEntityClass(ReflectionClass|IdentifierInterface $identifier): ReflectionClass
27    {
28        if ($identifier instanceof IdentifierInterface) {
29            $identifier = new ReflectionClass($identifier);
30        }
31        return $identifier->getMethod('getReferenceFor')->invoke(null);
32    }
33
34    /**
35     * @param bool $throwError
36     * @phpstan-return ($throwError is true ? IdentifierInterface<EntityInterface> : IdentifierInterface<EntityInterface>|null)
37     */
38    public static function idStringToIdentifier(
39        string $id,
40        ApieContext $context,
41        bool $throwError = true
42    ): ?IdentifierInterface {
43        $resourceName = $context->getContext(ContextConstants::RESOURCE_NAME, $throwError);
44        if (empty($resourceName) && !$throwError) {
45            return null;
46        }
47        try {
48            $resourceClass = new ReflectionClass($resourceName ?? '');
49            $idClass = self::entityClassToIdentifier($resourceClass);
50            /** @var IdentifierInterface<EntityInterface> $idObject */
51            $idObject = $context->hasContext(Serializer::class)
52                ? $context->getContext(Serializer::class)->denormalizeNewObject($id, $idClass->name, $context)
53                : $idClass->newInstance($id);
54            return $idObject;
55        } catch (Exception $exception) {
56            if ($throwError) {
57                throw $exception;
58            }
59            return null;
60        }
61    }
62
63    /**
64     * @template T of EntityInterface
65     * @param ReflectionClass<covariant T>|T $identifier
66     * @return ReflectionClass<covariant IdentifierInterface<T>>
67     */
68    public static function entityClassToIdentifier(ReflectionClass|EntityInterface $identifier): ReflectionClass
69    {
70        if ($identifier instanceof EntityInterface) {
71            $identifier = new ReflectionClass($identifier);
72        }
73        $returnType = $identifier->getMethod('getId')->getReturnType();
74        if (!($returnType instanceof ReflectionNamedType)) {
75            throw new InvalidTypeException($returnType, 'ReflectionNamedType');
76        }
77        return new ReflectionClass($returnType->getName());
78    }
79
80    /**
81     * @param ReflectionClass<covariant object> $class
82     */
83    public static function classNameToUnderscore(ReflectionClass $class): string
84    {
85        $str = $class->getShortName();
86        $str = lcfirst($str);
87        $str = preg_replace("/[A-Z]/", '_$0', $str);
88        return strtolower($str);
89    }
90
91    public static function propertyToUnderscore(ReflectionProperty $property): string
92    {
93        // TODO check visibility with private properties
94        $str = $property->name;
95        $str = lcfirst($str);
96        $str = preg_replace("/[A-Z]/", '_$0', $str);
97        return strtolower($str);
98    }
99
100    /**
101     * @template T of EntityInterface
102     * @param T $entity
103     * @param IdentifierInterface<T> $identifier
104     */
105    public static function injectIdentifier(EntityInterface $entity, IdentifierInterface $identifier): void
106    {
107        $refl = new ReflectionClass($entity);
108        while ($refl) {
109            if ($refl->hasProperty('id')) {
110                $prop = $refl->getProperty('id');
111                $prop->setValue($entity, $identifier);
112                return;
113            }
114            $refl = $refl->getParentClass();
115        }
116        throw new LogicException('I could not find an "id" property!');
117    }
118}