Passed
Push — master ( 38ea17...fe26dd )
by Damien
04:28
created

AuditTrait::value()   C

Complexity

Conditions 14
Paths 14

Size

Total Lines 46
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 31
dl 0
loc 46
rs 6.2666
c 0
b 0
f 0
cc 14
nc 14
nop 3

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace DH\Auditor\Provider\Doctrine\Auditing\Transaction;
4
5
use DH\Auditor\Provider\Doctrine\Persistence\Helper\DoctrineHelper;
6
use DH\Auditor\User\UserInterface;
7
use Doctrine\DBAL\Types\Type;
8
use Doctrine\ORM\EntityManagerInterface;
9
10
trait AuditTrait
11
{
12
    /**
13
     * Returns the primary key value of an entity.
14
     *
15
     * @param mixed $entity
16
     *
17
     * @return mixed
18
     */
19
    private function id(EntityManagerInterface $entityManager, $entity)
20
    {
21
        $meta = $entityManager->getClassMetadata(DoctrineHelper::getRealClassName($entity));
22
        $pk = $meta->getSingleIdentifierFieldName();
23
24
        if (isset($meta->fieldMappings[$pk])) {
25
            $type = Type::getType($meta->fieldMappings[$pk]['type']);
26
27
            return $this->value($entityManager, $type, $meta->getReflectionProperty($pk)->getValue($entity));
28
        }
29
30
        /**
31
         * Primary key is not part of fieldMapping.
32
         *
33
         * @see https://github.com/DamienHarper/Auditor\Provider\Doctrine/issues/40
34
         * @see https://www.doctrine-project.org/projects/doctrine-orm/en/latest/tutorials/composite-primary-keys.html#identity-through-foreign-entities
35
         * We try to get it from associationMapping (will throw a MappingException if not available)
36
         */
37
        $targetEntity = $meta->getReflectionProperty($pk)->getValue($entity);
38
39
        $mapping = $meta->getAssociationMapping($pk);
40
41
        $meta = $entityManager->getClassMetadata($mapping['targetEntity']);
42
        $pk = $meta->getSingleIdentifierFieldName();
43
        $type = Type::getType($meta->fieldMappings[$pk]['type']);
44
45
        return $this->value($entityManager, $type, $meta->getReflectionProperty($pk)->getValue($targetEntity));
46
    }
47
48
    /**
49
     * Type converts the input value and returns it.
50
     *
51
     * @param mixed $value
52
     *
53
     * @return mixed
54
     */
55
    private function value(EntityManagerInterface $entityManager, Type $type, $value)
56
    {
57
        if (null === $value) {
58
            return;
59
        }
60
61
        $platform = $entityManager->getConnection()->getDatabasePlatform();
62
63
        switch ($type->getName()) {
64
            case DoctrineHelper::getDoctrineType('BIGINT'):
65
                $convertedValue = (string) $value;
66
67
                break;
68
            case DoctrineHelper::getDoctrineType('INTEGER'):
69
            case DoctrineHelper::getDoctrineType('SMALLINT'):
70
                $convertedValue = (int) $value;
71
72
                break;
73
            case DoctrineHelper::getDoctrineType('DECIMAL'):
74
            case DoctrineHelper::getDoctrineType('FLOAT'):
75
            case DoctrineHelper::getDoctrineType('BOOLEAN'):
76
                $convertedValue = $type->convertToPHPValue($value, $platform);
77
78
                break;
79
            case 'uuid_binary':
80
            case 'uuid_binary_ordered_time':
81
            case 'uuid':
82
            case 'ulid':
83
                // Ramsey UUID / Symfony UID (UUID/ULID)
84
                $convertedValue = (string) $value;
85
86
                break;
87
            case DoctrineHelper::getDoctrineType('BINARY'):
88
                if (\is_resource($value)) {
89
                    // let's replace resources with a "simple" representation: resourceType#resourceId
90
                    $convertedValue = get_resource_type($value).'#'.get_resource_id($value);
91
                } else {
92
                    $convertedValue = $type->convertToDatabaseValue($value, $platform);
93
                }
94
95
                break;
96
            default:
97
                $convertedValue = $type->convertToDatabaseValue($value, $platform);
98
        }
99
100
        return $convertedValue;
101
    }
102
103
    /**
104
     * Computes a usable diff.
105
     *
106
     * @param mixed $entity
107
     */
108
    private function diff(EntityManagerInterface $entityManager, $entity, array $changeset): array
109
    {
110
        $meta = $entityManager->getClassMetadata(DoctrineHelper::getRealClassName($entity));
111
        $diff = [];
112
113
        foreach ($changeset as $fieldName => [$old, $new]) {
114
            $o = null;
115
            $n = null;
116
117
            if (
118
                !isset($meta->embeddedClasses[$fieldName])
119
                && $meta->hasField($fieldName)
120
                && $this->provider->isAuditedField($entity, $fieldName)
121
            ) {
122
                $mapping = $meta->fieldMappings[$fieldName];
123
                $type = Type::getType($mapping['type']);
124
                $o = $this->value($entityManager, $type, $old);
125
                $n = $this->value($entityManager, $type, $new);
126
            } elseif (
127
                $meta->hasAssociation($fieldName)
128
                && $meta->isSingleValuedAssociation($fieldName)
129
                && $this->provider->isAuditedField($entity, $fieldName)
130
            ) {
131
                $o = $this->summarize($entityManager, $old);
132
                $n = $this->summarize($entityManager, $new);
133
            }
134
135
            if ($o !== $n) {
136
                $diff[$fieldName] = [
137
                    'new' => $n,
138
                    'old' => $o,
139
                ];
140
            }
141
        }
142
143
        return $diff;
144
    }
145
146
    /**
147
     * Returns an array describing an entity.
148
     *
149
     * @param null|mixed $entity
150
     * @param null|mixed $id
151
     */
152
    private function summarize(EntityManagerInterface $entityManager, $entity = null, $id = null): ?array
153
    {
154
        if (null === $entity) {
155
            return null;
156
        }
157
158
        $entityManager->getUnitOfWork()->initializeObject($entity); // ensure that proxies are initialized
159
        $meta = $entityManager->getClassMetadata(DoctrineHelper::getRealClassName($entity));
160
        $pkName = $meta->getSingleIdentifierFieldName();
161
        $pkValue = $id ?? $this->id($entityManager, $entity);
162
        // An added guard for proxies that fail to initialize.
163
        if (null === $pkValue) {
164
            return null;
165
        }
166
167
        if (method_exists($entity, '__toString')) {
168
            $label = (string) $entity;
169
        } else {
170
            $label = DoctrineHelper::getRealClassName($entity).'#'.$pkValue;
171
        }
172
173
        return [
174
            $pkName => $pkValue,
175
            'class' => $meta->name,
176
            'label' => $label,
177
            'table' => $meta->getTableName(),
178
        ];
179
    }
180
181
    /**
182
     * Blames an audit operation.
183
     */
184
    private function blame(): array
185
    {
186
        $user_id = null;
187
        $username = null;
188
        $client_ip = null;
189
        $user_fqdn = null;
190
        $user_firewall = null;
191
192
        $securityProvider = $this->provider->getAuditor()->getConfiguration()->getSecurityProvider();
193
        if (null !== $securityProvider) {
194
            [$client_ip, $user_firewall] = $securityProvider();
195
        }
196
197
        $userProvider = $this->provider->getAuditor()->getConfiguration()->getUserProvider();
198
        $user = null === $userProvider ? null : $userProvider();
199
        if ($user instanceof UserInterface) {
200
            $user_id = $user->getIdentifier();
201
            $username = $user->getUsername();
202
            $user_fqdn = DoctrineHelper::getRealClassName($user);
203
        }
204
205
        return [
206
            'client_ip' => $client_ip,
207
            'user_firewall' => $user_firewall,
208
            'user_fqdn' => $user_fqdn,
209
            'user_id' => $user_id,
210
            'username' => $username,
211
        ];
212
    }
213
}
214