Passed
Pull Request — master (#58)
by
unknown
10:40
created

DoctrineSubscriber::getReflectionClassFromObject()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 2
dl 0
loc 5
c 0
b 0
f 0
rs 10
cc 1
nc 1
nop 1
1
<?php
2
3
/*
4
 * This file is part of the XiideaEasyAuditBundle package.
5
 *
6
 * (c) Xiidea <http://www.xiidea.net>
7
 *
8
 * This source file is subject to the MIT license that is bundled
9
 * with this source code in the file LICENSE.
10
 */
11
12
namespace Xiidea\EasyAuditBundle\Subscriber;
13
14
use Doctrine\Common\Util\ClassUtils;
15
use Doctrine\Persistence\Event\LifecycleEventArgs;
16
use Xiidea\EasyAuditBundle\Attribute\SubscribeDoctrineEvents;
17
use Xiidea\EasyAuditBundle\Events\DoctrineEvents;
18
use Xiidea\EasyAuditBundle\Events\DoctrineObjectEvent;
19
20
#[AsDoctrineListener]
21
class DoctrineSubscriber
22
{
23
    private array $toBeDeleted = [];
24
    private  $dispatcher = null;
25
26
    public function __construct(private array $entities = [])
27
    {
28
    }
29
30
    public function postPersist(LifecycleEventArgs $args)
31
    {
32
        $this->handleEvent(DoctrineEvents::ENTITY_CREATED, $args);
33
    }
34
35
    public function postUpdate(LifecycleEventArgs $args)
36
    {
37
        $this->handleEvent(DoctrineEvents::ENTITY_UPDATED, $args);
38
    }
39
40
    public function preRemove(LifecycleEventArgs $args)
41
    {
42
        if (false === $this->isConfiguredToTrack($args->getObject(), DoctrineEvents::ENTITY_DELETED)) {
43
            return;
44
        }
45
46
        $className = ClassUtils::getClass($args->getObject());
47
48
        if (!isset($this->toBeDeleted[$className])) {
49
            $this->toBeDeleted[$className] = [];
50
        }
51
52
        $this->toBeDeleted[$className][spl_object_hash($args->getObject())] = $this->getIdentity($args, $className);
53
    }
54
55
    public function postRemove(LifecycleEventArgs $args)
56
    {
57
        $identity = $this->getToBeDeletedId($args->getObject());
58
59
        if (null !== $identity) {
60
            $this->dispatcher->dispatch(new DoctrineObjectEvent($args, $identity), DoctrineEvents::ENTITY_DELETED);
61
        }
62
    }
63
64
    private function getToBeDeletedId($entity)
65
    {
66
        if ($this->isScheduledForDelete($entity)) {
67
            return $this->toBeDeleted[ClassUtils::getClass($entity)][spl_object_hash($entity)];
68
        }
69
70
        return null;
71
    }
72
73
    /**
74
     * @param  string  $eventName
75
     * @param  LifecycleEventArgs  $args
76
     */
77
    private function handleEvent($eventName, LifecycleEventArgs $args)
78
    {
79
        if (true === $this->isConfiguredToTrack($args->getObject(), $eventName)) {
80
            $this->dispatcher->dispatch(
81
                new DoctrineObjectEvent($args, $this->getIdentity($args, ClassUtils::getClass($args->getObject()))),
82
                $eventName
83
            );
84
        }
85
    }
86
87
    /**
88
     * @param $entity
89
     * @param  string  $eventName
90
     *
91
     * @return bool
92
     */
93
    private function isConfiguredToTrack($entity, $eventName = '')
94
    {
95
        $class = ClassUtils::getClass($entity);
96
        $eventType = DoctrineEvents::getShortEventType($eventName);
97
98
        if (null !== $track = $this->isAttributedEvent($entity, $eventType)) {
99
            return $track;
100
        }
101
102
        if (!isset($this->entities[$class])) {
103
            return false;
104
        }
105
106
        if ($this->shouldTrackAllEventType($class)) {
107
            return true;
108
        }
109
110
        return $this->shouldTrackEventType($eventType, $class);
111
    }
112
113
    /**
114
     * @param $entity
115
     * @param string $eventType
116
     * @return bool|null
117
     * @throws \ReflectionException
118
     */
119
    protected function isAttributedEvent($entity, $eventType)
120
    {
121
        $reflection = new \ReflectionClass($entity);
122
        $attribute = $reflection->getAttributes(SubscribeDoctrineEvents::class);
123
        if (!$attribute) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $attribute of type ReflectionAttribute[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
124
            return null;
125
        }
126
127
        $instance = $attribute[0]->newInstance();
128
129
        return empty($instance->events) || in_array($eventType, $instance->events);
130
    }
131
132
133
    /**
134
     * @param  string  $eventType
135
     * @param  string  $class
136
     *
137
     * @return bool
138
     */
139
    private function shouldTrackEventType($eventType, $class)
140
    {
141
        return is_array($this->entities[$class]) && in_array($eventType, $this->entities[$class]);
142
    }
143
144
    /**
145
     * @param  string  $class
146
     *
147
     * @return bool
148
     */
149
    private function shouldTrackAllEventType($class)
150
    {
151
        return empty($this->entities[$class]);
152
    }
153
154
    /**
155
     * @param  \Doctrine\Common\Annotations\Reader  $annotationReader
0 ignored issues
show
Bug introduced by
The type Doctrine\Common\Annotations\Reader was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
156
     */
157
    public function setAnnotationReader($annotationReader = null)
158
    {
159
        $this->annotationReader = $annotationReader;
0 ignored issues
show
Bug Best Practice introduced by
The property annotationReader does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
160
    }
161
162
    /**
163
     * @param  LifecycleEventArgs  $args
164
     * @param $className
165
     *
166
     * @return array
167
     */
168
    protected function getIdentity(LifecycleEventArgs $args, $className)
169
    {
170
        return $args->getObjectManager()->getClassMetadata($className)->getIdentifierValues($args->getObject());
171
    }
172
173
    /**
174
     * @param $entity
175
     *
176
     * @return bool
177
     */
178
    private function isScheduledForDelete($entity)
179
    {
180
        $originalClassName = ClassUtils::getClass($entity);
181
182
        return isset($this->toBeDeleted[$originalClassName]) && isset(
183
                $this->toBeDeleted[$originalClassName][spl_object_hash(
184
                    $entity
185
                )]
186
            );
187
    }
188
189
    /**
190
     * @param  EventDispatcherInterface  $dispatcher
0 ignored issues
show
Bug introduced by
The type Xiidea\EasyAuditBundle\S...ventDispatcherInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
191
     */
192
    public function setDispatcher($dispatcher)
193
    {
194
        $this->dispatcher = $dispatcher;
195
    }
196
}
197