Test Failed
Push — dev ( 6bf3a3...19aaea )
by Janko
09:03
created

SwitchableEntityManager::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 4
c 1
b 0
f 0
nc 1
nop 4
dl 0
loc 11
rs 10
1
<?php
2
3
namespace Stu\Orm\Transaction;
4
5
use Doctrine\Common\Collections\ArrayCollection;
6
use Doctrine\Common\Collections\Collection;
7
use Doctrine\DBAL\Connection;
8
use Doctrine\ORM\Configuration;
9
use Doctrine\ORM\Decorator\EntityManagerDecorator;
10
use Doctrine\ORM\EntityManagerInterface;
11
use Doctrine\ORM\EntityRepository;
12
use Override;
0 ignored issues
show
Bug introduced by
The type Override 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...
13
use RuntimeException;
14
use Stu\Module\Logging\LoggerTypeEnum;
0 ignored issues
show
Bug introduced by
The type Stu\Module\Logging\LoggerTypeEnum 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...
15
use Stu\Module\Logging\LoggerUtilFactoryInterface;
16
use Stu\Module\Logging\RotatingLoggerInterface;
0 ignored issues
show
Bug introduced by
The type Stu\Module\Logging\RotatingLoggerInterface 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...
17
use Stu\Orm\Transaction\EntityManagerFactoryInterface;
18
19
class SwitchableEntityManager extends EntityManagerDecorator implements SwitchableEntityManagerInterface
20
{
21
    /** @var ArrayCollection<string, EntityManagerInterface> */
22
    private Collection $entityManagers;
23
24
    private EntityManagerTypeEnum $currentType;
0 ignored issues
show
Bug introduced by
The type Stu\Orm\Transaction\EntityManagerTypeEnum 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...
25
    private EntityManagerTypeEnum $lastType;
26
    private RotatingLoggerInterface $logger;
27
28
    public function __construct(
29
        private EntityManagerFactoryInterface $entityManagerFactory,
30
        private Configuration $configuration,
31
        EntityManagerInterface $sessionEntityManager,
32
        LoggerUtilFactoryInterface $loggerUtilFactory
33
    ) {
34
        parent::__construct($sessionEntityManager);
35
36
        $this->entityManagers = new ArrayCollection([EntityManagerTypeEnum::SESSION->value => $sessionEntityManager]);
37
        $this->currentType = EntityManagerTypeEnum::SESSION;
38
        $this->logger = $loggerUtilFactory->getRotatingLogger(LoggerTypeEnum::EVENT_AND_ENTITY_LOCK);
0 ignored issues
show
Bug introduced by
The method getRotatingLogger() does not exist on Stu\Module\Logging\LoggerUtilFactoryInterface. ( Ignorable by Annotation )

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

38
        /** @scrutinizer ignore-call */ 
39
        $this->logger = $loggerUtilFactory->getRotatingLogger(LoggerTypeEnum::EVENT_AND_ENTITY_LOCK);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
39
    }
40
41
    public function switchType(EntityManagerTypeEnum $type): SwitchableEntityManager
42
    {
43
        if ($this->currentType === $type) {
44
            return $this;
45
        }
46
47
        $this->lastType = $this->currentType;
48
        $this->currentType =  $type;
49
50
        $key = $type->value;
51
        $entityManager = $this->entityManagers->get($key);
52
        if ($entityManager === null) {
53
            $entityManager = $this->setNewEntityManger($key);
54
        }
55
56
        $this->logger->log(sprintf(
57
            'SWITCH_TYPE: %s(%d)->%s(%d)',
58
            $this->lastType->value,
59
            $this->wrapped->getConnection()->getTransactionNestingLevel(),
60
            $key,
61
            $entityManager->getConnection()->getTransactionNestingLevel()
62
        ));
63
64
        $this->wrapped = $entityManager;
0 ignored issues
show
Documentation Bug introduced by
It seems like $entityManager can also be of type Doctrine\ORM\EntityManagerInterface. However, the property $wrapped is declared as type Doctrine\Persistence\TObjectManager. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
65
66
        return $this;
67
    }
68
69
    public function resetLastType(): SwitchableEntityManager
70
    {
71
        $this->switchType($this->lastType);
72
73
        return $this;
74
    }
75
76
    private function setNewEntityManger(string $key, ?Connection $connection = null): EntityManagerInterface
77
    {
78
        $newManager = $this->entityManagerFactory->createEntityManager($connection);
79
        $this->entityManagers->set($key, $newManager);
80
81
        return $newManager;
82
    }
83
84
    /**
85
     * Executes a function in a transaction.
86
     *
87
     * The function gets passed this EntityManager instance as an (optional) parameter.
88
     *
89
     * {@link flush} is invoked prior to transaction commit.
90
     *
91
     * If an exception occurs during execution of the function or flushing or transaction commit,
92
     * the transaction is rolled back, the EntityManager closed and the exception re-thrown.
93
     *
94
     * @psalm-param callable(self): T $func The function to execute transactionally.
95
     *
96
     * @return mixed The value returned from the closure.
97
     * @psalm-return T
98
     *
99
     * @template T
100
     */
101
    #[Override]
102
    public function wrapInTransaction(callable $func): mixed
103
    {
104
        if (!$this->wrapped->isOpen()) {
105
            $this->renewManager();
106
        }
107
108
        return $this->wrapped->wrapInTransaction(function (EntityManagerInterface $entityManager) use ($func) {
109
110
            return $func($entityManager);
111
        });
112
    }
113
114
    private function renewManager(EntityManagerInterface $entityManager = null): SwitchableEntityManager
115
    {
116
        // remove old manager
117
        $oldManager = $entityManager ?? $this->wrapped;
118
        /** @var string|false */
119
        $key = $this->entityManagers->indexOf($oldManager);
120
        if (!$key) {
121
            throw new RuntimeException(sprintf('no entity manager of type "%s" registered', $key));
0 ignored issues
show
Bug introduced by
It seems like $key can also be of type false; however, parameter $values of sprintf() does only seem to accept double|integer|string, 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

121
            throw new RuntimeException(sprintf('no entity manager of type "%s" registered', /** @scrutinizer ignore-type */ $key));
Loading history...
122
        }
123
        $this->entityManagers->remove($key);
0 ignored issues
show
Bug introduced by
It seems like $key can also be of type true; however, parameter $key of Doctrine\Common\Collections\Collection::remove() does only seem to accept integer|string, 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

123
        $this->entityManagers->remove(/** @scrutinizer ignore-type */ $key);
Loading history...
124
125
        // create and register new manager
126
        $this->wrapped = $this->setNewEntityManger($key, $oldManager->getConnection());
0 ignored issues
show
Bug introduced by
It seems like $key can also be of type true; however, parameter $key of Stu\Orm\Transaction\Swit...r::setNewEntityManger() does only seem to accept string, 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

126
        $this->wrapped = $this->setNewEntityManger(/** @scrutinizer ignore-type */ $key, $oldManager->getConnection());
Loading history...
Documentation Bug introduced by
It seems like $this->setNewEntityMange...nager->getConnection()) of type Doctrine\ORM\EntityManagerInterface is incompatible with the declared type Doctrine\Persistence\TObjectManager of property $wrapped.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
127
128
        return $this;
129
    }
130
131
    public function isUninitializedObject(mixed $obj): bool
132
    {
133
        return $this->wrapped->isUninitializedObject($obj);
134
    }
135
136
    #[Override]
137
    public function getRepository(string $className): EntityRepository
138
    {
139
        return $this
140
            ->configuration
141
            ->getRepositoryFactory()
142
            ->getRepository($this, $className);
143
    }
144
145
    public function beginTransaction(): void
146
    {
147
        $this->logger->logf('%s::BEGIN_TRANSACTION', $this->currentType->value);
148
        $this->wrapped->beginTransaction();
149
    }
150
151
    public function flush(): void
152
    {
153
        if ($this->wrapped->isOpen()) {
154
            $this->wrapped->flush();
155
        } else {
156
            throw new TransactionException(sprintf('entity manager is closed. wrong type? (%s)', $this->currentType->value));
157
        }
158
    }
159
160
    #[Override]
161
    public function commit(): void
162
    {
163
        $this->logger->logf('%s::COMMIT_TRANSACTION', $this->currentType->value);
164
165
        if ($this->wrapped->getConnection()->isTransactionActive()) {
166
            $this->wrapped->commit();
167
        } else {
168
            throw new TransactionException(sprintf('no active transaction. wrong type? (%s)', $this->currentType->value));
169
        }
170
    }
171
}
172