Test Failed
Push — main ( a2096a...c7561b )
by Bingo
15:21
created

DbEntityCache::putInternal()   C

Complexity

Conditions 14
Paths 20

Size

Total Lines 74
Code Lines 41

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 41
dl 0
loc 74
rs 6.2666
c 0
b 0
f 0
cc 14
nc 20
nop 1

How to fix   Long Method    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 Jabe\Engine\Impl\Db\EntityManager\Cache;
4
5
use Jabe\Engine\ProcessEngineException;
6
use Jabe\Engine\Impl\ProcessEngineLogger;
7
use Jabe\Engine\Impl\Db\{
8
    DbEntityInterface,
9
    EnginePersistenceLogger
10
};
11
12
class DbEntityCache
13
{
14
    //protected static final EnginePersistenceLogger LOG = ProcessEngineLogger.PERSISTENCE_LOGGER;
15
16
    /**
17
     * The cache itself: maps entity types (classes) to maps indexed by id (primary key).
18
     *
19
     * The motivation for indexing by type (class) is
20
     *
21
     * a) multiple entities of different types could have the same value as primary key. In the
22
     *    process engine, TaskEntity and HistoricTaskEntity have the same id value.
23
     *
24
     * b) performance (?)
25
     */
26
    protected $cachedEntites = [];
27
28
    protected $cacheKeyMapping;
29
30
    public function __construct(DbEntityCacheKeyMapping $cacheKeyMapping = null)
31
    {
32
        if ($cacheKeyMapping == null) {
33
            DbEntityCacheKeyMapping::emptyMapping();
34
        } else {
35
            $this->cacheKeyMapping = $cacheKeyMapping;
36
        }
37
    }
38
39
    /**
40
     * get an object from the cache
41
     *
42
     * @param type the type of the object
43
     * @param id the id of the object
44
     * @return the object or 'null' if the object is not in the cache
45
     * @throws ProcessEngineException if an object for the given id can be found but is of the wrong type.
46
     */
47
    public function get(string $type, string $id)
48
    {
49
        $cacheKey = $this->cacheKeyMapping->getEntityCacheKey($type);
50
        $cachedDbEntity = $this->getCachedEntity($cacheKey, $id);
51
        if ($cachedDbEntity != null) {
52
            $dbEntity = $cachedDbEntity->getEntity();
53
            if (!is_a($dbEntity, $type)) {
54
                //throw LOG.entityCacheLookupException(type, id, dbEntity.getClass(), null);
55
                throw new \Exception("entityCacheLookupException");
56
            }
57
            try {
58
                return $dbEntity;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $dbEntity also could return the type Jabe\Engine\Impl\Db\DbEntityInterface which is incompatible with the documented return type Jabe\Engine\Impl\Db\EntityManager\Cache\the.
Loading history...
59
            } catch (\Exception $e) {
0 ignored issues
show
Unused Code introduced by
catch (\Exception $e) is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
60
                //throw LOG.entityCacheLookupException(type, id, dbEntity.getClass(), e);
61
                throw new \Exception("entityCacheLookupException");
62
            }
63
        } else {
64
            return null;
65
        }
66
    }
67
68
    public function getEntitiesByType(string $type): array
69
    {
70
        $cacheKey = $cacheKeyMapping->getEntityCacheKey($type);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $cacheKeyMapping does not exist. Did you maybe mean $cacheKey?
Loading history...
71
        if (array_key_exists($cacheKey, $this->cachedEntites)) {
72
            $entities = $this->cachedEntites[$cacheKey];
73
        } else {
74
            $entities = null;
75
        }
76
        $result = [];
77
        if ($entities == null) {
78
            return [];
79
        } else {
80
            foreach (array_values($entities) as $cachedEntity) {
81
                if ($type != $cacheKey) {
82
                    // if the cacheKey of this type differs from the actual type,
83
                    // not all cached entities with the key should be returned.
84
                    // Then we only add those entities whose type matches the argument type.
85
                    if (is_a($cachedEntity, $type)) {
86
                        $result[] = $cachedEntity->getEntity();
87
                    }
88
                } else {
89
                    $result[] = $cachedEntity->getEntity();
90
                }
91
            }
92
            return $result;
93
        }
94
    }
95
96
    /**
97
     * Looks up an entity in the cache.
98
     *
99
     * @param type the type of the object
100
     * @param id the id of the CachedEntity to lookup
101
     * @return the cached entity or null if the entity does not exist.
102
     */
103
    public function getCachedEntity($typeOrEntity, string $id = null): ?CachedDbEntity
104
    {
105
        if (is_string($typeOrEntity)) {
106
            $cacheKey = $cacheKeyMapping->getEntityCacheKey($typeOrEntity);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $cacheKeyMapping does not exist. Did you maybe mean $cacheKey?
Loading history...
107
            if (array_key_exists($cacheKey, $this->cachedEntites)) {
108
                $entitiesByType = $this->cachedEntites[$cacheKey];
109
                return $entitiesByType[$id];
110
            } else {
111
                return null;
112
            }
113
        } elseif ($typeOrEntity instanceof DbEntityInterface) {
114
            return $this->getCachedEntity(get_class($dbEntity), $dbEntity->getId());
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getCachedE...y), $dbEntity->getId()) also could return the type Jabe\Engine\Impl\Db\Enti...er\Cache\CachedDbEntity which is incompatible with the documented return type Jabe\Engine\Impl\Db\EntityManager\Cache\the.
Loading history...
Comprehensibility Best Practice introduced by
The variable $dbEntity seems to be never defined.
Loading history...
115
        }
116
    }
117
118
    /**
119
     * Put a new, {@link DbEntityState#TRANSIENT} object into the cache.
120
     *
121
     * @param e the object to put into the cache
122
     */
123
    public function putTransient(DbEntityInterface $e): void
124
    {
125
        $cachedDbEntity = new CachedDbEntity();
126
        $cachedDbEntity->setEntity($e);
127
        $cachedDbEntity->setEntityState(DbEntityState::TRANSIENT);
128
        $this->putInternal($cachedDbEntity);
129
    }
130
131
    /**
132
     * Put a {@link DbEntityState#PERSISTENT} object into the cache.
133
     *
134
     * @param e the object to put into the cache
135
     */
136
    public function putPersistent(DbEntityInterface $e): void
137
    {
138
        $cachedDbEntity = new CachedDbEntity();
139
        $cachedDbEntity->setEntity($e);
140
        $cachedDbEntity->setEntityState(DbEntityState::PERSISTENT);
141
        $cachedDbEntity->determineEntityReferences();
142
        $cachedDbEntity->makeCopy();
143
        $this->putInternal($cachedDbEntity);
144
    }
145
146
    /**
147
     * Put a {@link DbEntityState#MERGED} object into the cache.
148
     *
149
     * @param e the object to put into the cache
150
     */
151
    public function putMerged(DbEntityInterface $e): void
152
    {
153
        $cachedDbEntity = new CachedDbEntity();
154
        $cachedDbEntity->setEntity($e);
155
        $cachedDbEntity->setEntityState(DbEntityState::MERGED);
156
        $cachedDbEntity->determineEntityReferences();
157
        // no copy required
158
        $this->putInternal($cachedDbEntity);
159
    }
160
161
    protected function putInternal(CachedDbEntity $entityToAdd): void
162
    {
163
        $type = get_class($entityToAdd->getEntity());
0 ignored issues
show
Bug introduced by
It seems like $entityToAdd->getEntity() can also be of type null; however, parameter $object of get_class() does only seem to accept object, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

163
        $type = get_class(/** @scrutinizer ignore-type */ $entityToAdd->getEntity());
Loading history...
164
        $cacheKey = $this->cacheKeyMapping->getEntityCacheKey($type);
165
166
        if (array_key_exists($cacheKey, $this->cachedEntites)) {
167
            $map = $this->cachedEntites[$cacheKey];
168
        } else {
169
            $map = [];
170
            $this->cachedEntites[$cacheKey] = $map;
171
        }
172
173
        // check whether this object is already present in the cache
174
        $id = $entityToAdd->getEntity()->getId();
175
        if (!array_key_exists($id, $map)) {
176
            // no such entity exists -> put it into the cache
177
            $this->cachedEntites[$cacheKey][$id] = $entityToAdd;
178
        } else {
179
            $existingCachedEntity = $map[$id];
180
            // the same entity is already cached
181
            switch ($entityToAdd->getEntityState()) {
182
                case DbEntityState::TRANSIENT:
183
                    // cannot put TRANSIENT entity if entity with same id already exists in cache.
184
                    if ($existingCachedEntity->getEntityState() == DbEntityState::TRANSIENT) {
185
                        //throw LOG.entityCacheDuplicateEntryException("TRANSIENT", entityToAdd.getEntity().getId(),
186
                        //entityToAdd.getEntity().getClass(), existingCachedEntity.getEntityState());
187
                        throw new \Exception("entityCacheDuplicateEntryException");
188
                    } else {
189
                        //throw LOG.alreadyMarkedEntityInEntityCacheException(entityToAdd.getEntity().getId(),
190
                        //entityToAdd.getEntity().getClass(), existingCachedEntity.getEntityState());
191
                        throw new \Exception("alreadyMarkedEntityInEntityCacheException");
192
                    }
193
                case DbEntityState::PERSISTENT:
194
                    if ($existingCachedEntity->getEntityState() == DbEntityState::PERSISTENT) {
195
                        // use new entity state, replacing the existing one.
196
                        $this->cachedEntites[$cacheKey][$id] = $entityToAdd;
197
                        break;
198
                    }
199
                    if (
200
                        $existingCachedEntity->getEntityState() == DbEntityState::DELETED_PERSISTENT ||
201
                        $existingCachedEntity->getEntityState() == DbEntityState::DELETED_MERGED
202
                    ) {
203
                        // ignore put -> this is already marked to be deleted
204
                        break;
205
                    }
206
                    // otherwise fail:
207
                    //throw LOG.entityCacheDuplicateEntryException("PERSISTENT", entityToAdd.getEntity().getId(),
208
                    //    entityToAdd.getEntity().getClass(), existingCachedEntity.getEntityState());
209
                    throw new \Exception("entityCacheDuplicateEntryException");
210
                case DbEntityState::MERGED:
211
                    if (
212
                        $existingCachedEntity->getEntityState() == DbEntityState::PERSISTENT ||
213
                        $existingCachedEntity->getEntityState() == DbEntityState::MERGED
214
                    ) {
215
                        // use new entity state, replacing the existing one.
216
                        $this->cachedEntites[$cacheKey][$id] = $entityToAdd;
217
                        break;
218
                    }
219
                    if (
220
                        $existingCachedEntity->getEntityState() == DbEntityState::DELETED_PERSISTENT ||
221
                        $existingCachedEntity->getEntityState() == DbEntityState::DELETED_MERGED
222
                    ) {
223
                        // ignore put -> this is already marked to be deleted
224
                        break;
225
                    }
226
227
                    // otherwise fail:
228
                    //throw LOG.entityCacheDuplicateEntryException("MERGED", entityToAdd.getEntity().getId(),
229
                    //    entityToAdd.getEntity().getClass(), existingCachedEntity.getEntityState());
230
                    throw new \Exception("entityCacheDuplicateEntryException");
231
                default:
232
                    // deletes are always added
233
                    $this->cachedEntites[$cacheKey][$id] = $entityToAdd;
234
                    break;
235
            }
236
        }
237
    }
238
239
    /**
240
     * Remove an entity from the cache
241
     * @param e the entity to remove
242
     * @return
243
     */
244
    public function remove(DbEntityInterface $e)
245
    {
246
        if ($e instanceof DbEntityInterface) {
0 ignored issues
show
introduced by
$e is always a sub-type of Jabe\Engine\Impl\Db\DbEntityInterface.
Loading history...
247
            $cacheKey = $cacheKeyMapping->getEntityCacheKey(get_class($e));
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $cacheKeyMapping does not exist. Did you maybe mean $cacheKey?
Loading history...
248
            if (array_key_exists($cacheKey, $this->cachedEntites)) {
249
                $typeMap = $this->cachedEntites[$cacheKey];
250
                if (array_key_exists($e->getId(), $typeMap)) {
251
                    unset($this->cachedEntites[$cacheKey][$e->getId()]);
252
                    return true;
253
                }
254
                return false;
255
            } else {
256
                return false;
257
            }
258
        } elseif ($e instanceof CachedDbEntity) {
259
            return $this->remove($e->getEntity());
260
        }
261
        return false;
262
    }
263
264
    /**
265
     * Allows checking whether the provided entity is present in the cache
266
     *
267
     * @param dbEntity the entity to check
268
     * @return true if the the provided entity is present in the cache
269
     */
270
    public function contains(DbEntityInterface $dbEntity): bool
271
    {
272
        return $this->getCachedEntity($dbEntity) != null;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getCachedEntity($dbEntity) != null returns the type boolean which is incompatible with the documented return type true.
Loading history...
273
    }
274
275
    /**
276
     * Allows checking whether the provided entity is present in the cache
277
     * and is {@link DbEntityState#PERSISTENT}.
278
     *
279
     * @param dbEntity the entity to check
280
     * @return true if the provided entity is present in the cache and is
281
     * {@link DbEntityState#PERSISTENT}.
282
     */
283
    public function isPersistent(DbEntityInterface $dbEntity): bool
284
    {
285
        $cachedDbEntity = $this->getCachedEntity($dbEntity);
286
        if ($cachedDbEntity == null) {
287
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type true.
Loading history...
288
        } else {
289
            return $cachedDbEntity->getEntityState() == DbEntityState::PERSISTENT;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $cachedDbEntity->...EntityState::PERSISTENT returns the type boolean which is incompatible with the documented return type true.
Loading history...
290
        }
291
    }
292
293
    /**
294
     * Allows checking whether the provided entity is present in the cache
295
     * and is marked to be deleted.
296
     *
297
     * @param dbEntity the entity to check
298
     * @return true if the provided entity is present in the cache and is
299
     * marked to be deleted
300
     */
301
    public function isDeleted(DbEntityInterface $dbEntity): bool
302
    {
303
        $cachedDbEntity = $this->getCachedEntity($dbEntity);
304
        if ($cachedDbEntity == null) {
305
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type true.
Loading history...
306
        } else {
307
            return $cachedDbEntity->getEntityState() == DbEntityState::DELETED_MERGED
0 ignored issues
show
Bug Best Practice introduced by
The expression return $cachedDbEntity->...tate::DELETED_TRANSIENT returns the type boolean which is incompatible with the documented return type true.
Loading history...
308
                || $cachedDbEntity->getEntityState() == DbEntityState::DELETED_PERSISTENT
309
                || $cachedDbEntity->getEntityState() == DbEntityState::DELETED_TRANSIENT;
310
        }
311
    }
312
313
    /**
314
     * Allows checking whether the provided entity is present in the cache
315
     * and is {@link DbEntityState#TRANSIENT}.
316
     *
317
     * @param dbEntity the entity to check
318
     * @return true if the provided entity is present in the cache and is
319
     * {@link DbEntityState#TRANSIENT}.
320
     */
321
    public function isTransient(DbEntityInterface $dbEntity): bool
322
    {
323
        $cachedDbEntity = $this->getCachedEntity($dbEntity);
324
        if ($cachedDbEntity == null) {
325
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type true.
Loading history...
326
        } else {
327
            return $cachedDbEntity->getEntityState() == DbEntityState::TRANSIENT;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $cachedDbEntity->...bEntityState::TRANSIENT returns the type boolean which is incompatible with the documented return type true.
Loading history...
328
        }
329
    }
330
331
    public function getCachedEntities(): array
332
    {
333
        $result = [];
334
        foreach (array_values($this->cachedEntites) as $typeCache) {
335
            $result = array_merge($result, array_values($typeCache));
336
        }
337
        return $result;
338
    }
339
340
    /**
341
     * Sets an object to a deleted state. It will not be removed from the cache but
342
     * transition to one of the DELETED states, depending on it's current state.
343
     *
344
     * @param dbEntity the object to mark deleted.
345
     */
346
    public function setDeleted(DbEntityInterface $dbEntity): void
347
    {
348
        $cachedEntity = $this->getCachedEntity($dbEntity);
349
        if ($cachedEntity != null) {
350
            if ($cachedEntity->getEntityState() == DbEntityState::TRANSIENT) {
351
                $cachedEntity->setEntityState(DbEntityState::DELETED_TRANSIENT);
352
            } elseif ($cachedEntity->getEntityState() == DbEntityState::PERSISTENT) {
353
                $cachedEntity->setEntityState(DbEntityState::DELETED_PERSISTENT);
354
            } elseif ($cachedEntity->getEntityState() == DbEntityState::MERGED) {
355
                $cachedEntity->setEntityState(DbEntityState::DELETED_MERGED);
356
            }
357
        } else {
358
            // put a deleted merged into the cache
359
            $cachedDbEntity = new CachedDbEntity();
360
            $cachedDbEntity->setEntity($dbEntity);
361
            $cachedDbEntity->setEntityState(DbEntityState::DELETED_MERGED);
362
            $this->putInternal($cachedDbEntity);
363
        }
364
    }
365
366
    public function undoDelete(DbEntityInterface $dbEntity): void
367
    {
368
        $cachedEntity = $this->getCachedEntity($dbEntity);
369
        if ($cachedEntity->getEntityState() == DbEntityState::DELETED_TRANSIENT) {
370
            $cachedEntity->setEntityState(DbEntityState::TRANSIENT);
371
        } else {
372
            $cachedEntity->setEntityState(DbEntityState::MERGED);
373
        }
374
    }
375
}
376