Completed
Push — master ( e75413...39c194 )
by Roni
14:44 queued 13:17
created

DoctrineSubscriber::setAnnotationReader()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 4
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\EventSubscriber;
15
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
16
use Doctrine\Common\Util\ClassUtils;
17
use Symfony\Component\EventDispatcher\EventDispatcher;
18
use Xiidea\EasyAuditBundle\Annotation\SubscribeDoctrineEvents;
19
use Xiidea\EasyAuditBundle\Events\DoctrineObjectEvent;
20
use Xiidea\EasyAuditBundle\Events\DoctrineEvents;
21
22
class DoctrineSubscriber implements EventSubscriber
23
{
24
    /** @var \Doctrine\Common\Annotations\Reader */
25
    private $annotationReader;
26
27
    private $toBeDeleted = [];
28
29
    /**
30
     * @var EventDispatcher
31
     */
32
    private $dispatcher;
33
34
    /**
35
     * @var array
36
     */
37
    private $entities;
38
39
    public function __construct($entities = array())
40
    {
41
        $this->entities = $entities;
42
    }
43
44
    public function getSubscribedEvents()
45
    {
46
        return array(
47
            'postPersist',
48
            'postUpdate',
49
            'preRemove',
50
            'postRemove',
51
        );
52
    }
53
54
    public function postPersist(LifecycleEventArgs $args)
55
    {
56
        $this->handleEvent(DoctrineEvents::ENTITY_CREATED, $args);
57
    }
58
59
    public function postUpdate(LifecycleEventArgs $args)
60
    {
61
        $this->handleEvent(DoctrineEvents::ENTITY_UPDATED, $args);
62
    }
63
64
    public function preRemove(LifecycleEventArgs $args)
65
    {
66
        if (false === $this->isConfiguredToTrack($args->getObject(), DoctrineEvents::ENTITY_DELETED)) {
67
            return;
68
        }
69
70
        $className = ClassUtils::getClass($args->getObject());
71
72
        if (!isset($this->toBeDeleted[$className])) {
73
            $this->toBeDeleted[$className] = [];
74
        }
75
76
        $this->toBeDeleted[$className][spl_object_hash($args->getObject())] = $this->getIdentity($args, $className);
77
    }
78
79
    public function postRemove(LifecycleEventArgs $args)
80
    {
81
        $identity = $this->getToBeDeletedId($args->getObject());
82
83
        if (null !== $identity) {
84
            $this->dispatcher->dispatch(DoctrineEvents::ENTITY_DELETED,
85
                new DoctrineObjectEvent($args, $identity)
86
            );
87
        }
88
    }
89
90
    private function getToBeDeletedId($entity)
91
    {
92
        if ($this->isScheduledForDelete($entity)) {
93
            return $this->toBeDeleted[ClassUtils::getClass($entity)][spl_object_hash($entity)];
94
        }
95
96
        return null;
97
    }
98
99
    /**
100
     * @param string             $eventName
101
     * @param LifecycleEventArgs $args
102
     */
103
    private function handleEvent($eventName, LifecycleEventArgs $args)
104
    {
105
        if (true === $this->isConfiguredToTrack($args->getObject(), $eventName)) {
106
            $this->dispatcher->dispatch($eventName,
107
                new DoctrineObjectEvent($args, $this->getIdentity($args, ClassUtils::getClass($args->getObject())))
108
            );
109
        }
110
    }
111
112
    /**
113
     * @param $entity
114
     * @param string $eventName
115
     *
116
     * @return bool
117
     */
118
    private function isConfiguredToTrack($entity, $eventName = '')
119
    {
120
        $class = ClassUtils::getClass($entity);
121
        $eventType = DoctrineEvents::getShortEventType($eventName);
122
123
        if (null !== $track = $this->isAnnotatedEvent($entity, $eventType)) {
124
            return $track;
125
        }
126
127
        if (!isset($this->entities[$class])) {
128
            return false;
129
        }
130
131
        if ($this->shouldTrackAllEventType($class)) {
132
            return true;
133
        }
134
135
        return $this->shouldTrackEventType($eventType, $class);
136
    }
137
138
    /**
139
     * @param $entity
140
     * @param string $eventType
141
     *
142
     * @return bool|null
143
     */
144
    protected function isAnnotatedEvent($entity, $eventType)
145
    {
146
        $metaData = $this->hasAnnotation($entity);
147
148
        if (!$metaData) {
149
            return null;
150
        }
151
152
        return empty($metaData->events) || in_array($eventType, $metaData->events);
153
    }
154
155
    /**
156
     * @param $entity
157
     *
158
     * @return null|object
159
     */
160
    protected function hasAnnotation($entity)
161
    {
162
        $reflection = $this->getReflectionClassFromObject($entity);
163
164
        return $this
165
            ->getAnnotationReader()
166
            ->getClassAnnotation($reflection, SubscribeDoctrineEvents::class);
167
    }
168
169
    /**
170
     * @return \Doctrine\Common\Annotations\Reader
171
     */
172
    protected function getAnnotationReader()
173
    {
174
        return $this->annotationReader;
175
    }
176
177
    /**
178
     * @param $object
179
     *
180
     * @return \ReflectionClass
181
     */
182
    protected function getReflectionClassFromObject($object)
183
    {
184
        $class = ClassUtils::getClass($object);
185
186
        return new \ReflectionClass($class);
187
    }
188
189
    /**
190
     * @param string $eventType
191
     * @param string $class
192
     *
193
     * @return bool
194
     */
195
    private function shouldTrackEventType($eventType, $class)
196
    {
197
        return is_array($this->entities[$class]) && in_array($eventType, $this->entities[$class]);
198
    }
199
200
    /**
201
     * @param string $class
202
     *
203
     * @return bool
204
     */
205
    private function shouldTrackAllEventType($class)
206
    {
207
        return empty($this->entities[$class]);
208
    }
209
210
    /**
211
     * @param \Doctrine\Common\Annotations\Reader $annotationReader
212
     */
213
    public function setAnnotationReader($annotationReader = null)
214
    {
215
        $this->annotationReader = $annotationReader;
216
    }
217
218
    /**
219
     * @param LifecycleEventArgs $args
220
     * @param $className
221
     *
222
     * @return array
223
     */
224
    protected function getIdentity(LifecycleEventArgs $args, $className)
225
    {
226
        return $args->getObjectManager()->getClassMetadata($className)->getIdentifierValues($args->getObject());
227
    }
228
229
    /**
230
     * @param $entity
231
     *
232
     * @return bool
233
     */
234
    private function isScheduledForDelete($entity)
235
    {
236
        $originalClassName = ClassUtils::getClass($entity);
237
238
        return isset($this->toBeDeleted[$originalClassName]) && isset($this->toBeDeleted[$originalClassName][spl_object_hash($entity)]);
239
    }
240
241
    /**
242
     * @param EventDispatcher $dispatcher
243
     */
244
    public function setDispatcher($dispatcher)
245
    {
246
        $this->dispatcher = $dispatcher;
247
    }
248
}
249