EventLogger::log()   B
last analyzed

Complexity

Conditions 7
Paths 8

Size

Total Lines 24
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 12
c 0
b 0
f 0
nc 8
nop 1
dl 0
loc 24
rs 8.8333
1
<?php
2
/**
3
 * This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
4
 *
5
 * Copyright (C) 2019 - 2022 Jan Böhmer (https://github.com/jbtronics)
6
 *
7
 * This program is free software: you can redistribute it and/or modify
8
 * it under the terms of the GNU Affero General Public License as published
9
 * by the Free Software Foundation, either version 3 of the License, or
10
 * (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU Affero General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Affero General Public License
18
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19
 */
20
21
declare(strict_types=1);
22
23
namespace App\Services\LogSystem;
24
25
use App\Entity\LogSystem\AbstractLogEntry;
26
use App\Entity\UserSystem\User;
27
use Doctrine\ORM\EntityManagerInterface;
28
use Symfony\Component\Security\Core\Security;
29
30
class EventLogger
31
{
32
    protected int $minimum_log_level;
33
    protected array $blacklist;
34
    protected array $whitelist;
35
    protected EntityManagerInterface $em;
36
    protected Security $security;
37
38
    public function __construct(int $minimum_log_level, array $blacklist, array $whitelist, EntityManagerInterface $em, Security $security)
39
    {
40
        $this->minimum_log_level = $minimum_log_level;
41
        $this->blacklist = $blacklist;
42
        $this->whitelist = $whitelist;
43
        $this->em = $em;
44
        $this->security = $security;
45
    }
46
47
    /**
48
     * Adds the given log entry to the Log, if the entry fullfills the global configured criterias.
49
     * The change will not be flushed yet.
50
     *
51
     * @return bool returns true, if the event was added to log
52
     */
53
    public function log(AbstractLogEntry $logEntry): bool
54
    {
55
        $user = $this->security->getUser();
56
        //If the user is not specified explicitly, set it to the current user
57
        if ((null === $user || $user instanceof User) && null === $logEntry->getUser()) {
58
            if (null === $user) {
0 ignored issues
show
introduced by
The condition null === $user is always true.
Loading history...
59
                $repo = $this->em->getRepository(User::class);
60
                $user = $repo->getAnonymousUser();
61
            }
62
63
            //If no anonymous user is available skip the log (needed for data fixtures)
64
            if (null === $user) {
65
                return false;
66
            }
67
            $logEntry->setUser($user);
68
        }
69
70
        if ($this->shouldBeAdded($logEntry)) {
71
            $this->em->persist($logEntry);
72
73
            return true;
74
        }
75
76
        return false;
77
    }
78
79
    /**
80
     * Same as log(), but this function can be safely called from within the onFlush() doctrine event, as it
81
     * updated the changesets of the unit of work.
82
     * @param  AbstractLogEntry  $logEntry
83
     * @return bool
84
     */
85
    public function logFromOnFlush(AbstractLogEntry $logEntry): bool
86
    {
87
        if ($this->log($logEntry)) {
88
            $uow = $this->em->getUnitOfWork();
89
            //As we call it from onFlush, we have to recompute the changeset here, according to https://www.doctrine-project.org/projects/doctrine-orm/en/2.14/reference/events.html#reference-events-on-flush
90
            $uow->computeChangeSet($this->em->getClassMetadata(get_class($logEntry)), $logEntry);
91
92
            return true;
93
        }
94
95
        //If the normal log function does not added the log entry, we just do nothing
96
        return false;
97
    }
98
99
    /**
100
     * Adds the given log entry to the Log, if the entry fullfills the global configured criterias and flush afterwards.
101
     *
102
     * @return bool returns true, if the event was added to log
103
     */
104
    public function logAndFlush(AbstractLogEntry $logEntry): bool
105
    {
106
        $tmp = $this->log($logEntry);
107
        $this->em->flush();
108
109
        return $tmp;
110
    }
111
112
    public function shouldBeAdded(
113
        AbstractLogEntry $logEntry,
114
        ?int $minimum_log_level = null,
115
        ?array $blacklist = null,
116
        ?array $whitelist = null
117
    ): bool {
118
        //Apply the global settings, if nothing was specified
119
        $minimum_log_level = $minimum_log_level ?? $this->minimum_log_level;
120
        $blacklist = $blacklist ?? $this->blacklist;
121
        $whitelist = $whitelist ?? $this->whitelist;
122
123
        //Dont add the entry if it does not reach the minimum level
124
        if ($logEntry->getLevel() > $minimum_log_level) {
125
            return false;
126
        }
127
128
        //Check if the event type is black listed
129
        if (!empty($blacklist) && $this->isObjectClassInArray($logEntry, $blacklist)) {
130
            return false;
131
        }
132
133
        //Check for whitelisting
134
        if (!empty($whitelist) && !$this->isObjectClassInArray($logEntry, $whitelist)) {
135
            return false;
136
        }
137
138
        // By default all things should be added
139
        return true;
140
    }
141
142
    /**
143
     * Check if the object type is given in the classes array. This also works for inherited types.
144
     *
145
     * @param object   $object  The object which should be checked
146
     * @param string[] $classes the list of class names that should be used for checking
147
     */
148
    protected function isObjectClassInArray(object $object, array $classes): bool
149
    {
150
        //Check if the class is directly in the classes array
151
        if (in_array(get_class($object), $classes, true)) {
152
            return true;
153
        }
154
155
        //Iterate over all classes and check for inheritance
156
        foreach ($classes as $class) {
157
            if (is_a($object, $class)) {
158
                return true;
159
            }
160
        }
161
162
        return false;
163
    }
164
}
165