GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Pull Request — master (#161)
by joseph
25:55
created

PingingAndReconnectingConnection::executeQuery()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 4
dl 0
loc 5
ccs 0
cts 4
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php declare(strict_types=1);
2
3
namespace EdmondsCommerce\DoctrineStaticMeta\EntityManager\RetryConnection;
4
5
use Doctrine\Common\EventManager;
6
use Doctrine\DBAL\Cache\QueryCacheProfile;
7
use Doctrine\DBAL\Configuration;
8
use Doctrine\DBAL\Connection;
9
use Doctrine\DBAL\DBALException;
10
use Doctrine\DBAL\Driver;
11
12
/**
13
 * This is a connection wrapper that enables some retry functionality should the connection to the DB be lost for any
14
 * reason. Especially useful on long running processes
15
 */
16
class PingingAndReconnectingConnection extends Connection
17
{
18
    /**
19
     * How many seconds between pings
20
     *
21
     * @var float
22
     */
23
    private const PING_INTERVAL_SECONDS = 1.0;
24
25
    private const PING_FAILURE_SLEEP_SECONDS = 10;
26
27
    /** @var \ReflectionProperty */
28
    private $selfReflectionNestingLevelProperty;
29
30
    /** @var float */
31
    private $pingTimer = 0;
32
33
    /**
34
     * RetryConnection constructor.
35
     *
36
     * @param array              $params
37
     * @param Driver             $driver
38
     * @param Configuration|null $config
39
     * @param EventManager|null  $eventManager
40
     *
41
     * @throws \Doctrine\DBAL\DBALException
42
     * @SuppressWarnings(PHPMD.StaticAccess)
43
     */
44
    public function __construct(
45
        array $params,
46
        Driver $driver,
47
        ?Configuration $config = null,
48
        ?EventManager $eventManager = null
49
    ) {
50
        parent::__construct($params, $driver, $config, $eventManager);
51
    }
52
53
    public function executeUpdate($query, array $params = [], array $types = [])
54
    {
55
        $args = [$query, $params, $types];
56
57
        return $this->pingBeforeMethodCall(__FUNCTION__, $args);
58
    }
59
60
    private function pingBeforeMethodCall(string $function, array $args)
61
    {
62
        $this->pingAndReconnectOnFailure();
63
64
        return parent::$function(...$args);
65
    }
66
67
    public function pingAndReconnectOnFailure(): void
68
    {
69
        if (microtime(true) < ($this->pingTimer + self::PING_INTERVAL_SECONDS)) {
70
            return;
71
        }
72
        $this->pingTimer = microtime(true);
73
        if (false === $this->ping()) {
74
            $this->close();
75
            $this->resetTransactionNestingLevel();
76
            sleep(self::PING_FAILURE_SLEEP_SECONDS);
77
            parent::connect();
78
        }
79
    }
80
81
    /**
82
     * Overriding the ping method so we explicitly call the raw unwrapped methods as required, otherwise we go into
83
     * infinite loop
84
     *
85
     * @return bool
86
     */
87
    public function ping(): bool
88
    {
89
        parent::connect();
90
91
        if ($this->_conn instanceof Driver\PingableConnection) {
92
            return $this->_conn->ping();
93
        }
94
95
        try {
96
            parent::query($this->getDatabasePlatform()->getDummySelectSQL());
97
98
            return true;
99
        } catch (DBALException $e) {
100
            return false;
101
        }
102
    }
103
104
105
    /**
106
     * This is required because beginTransaction increment _transactionNestingLevel
107
     * before the real query is executed, and results incremented also on gone away error.
108
     * This should be safe for a new established connection.
109
     */
110
    private function resetTransactionNestingLevel(): void
111
    {
112
        if (!$this->selfReflectionNestingLevelProperty instanceof \ReflectionProperty) {
0 ignored issues
show
introduced by
$this->selfReflectionNestingLevelProperty is always a sub-type of ReflectionProperty.
Loading history...
113
            $reflection                               = new \ReflectionClass(Connection::class);
114
            $this->selfReflectionNestingLevelProperty = $reflection->getProperty('_transactionNestingLevel');
115
            $this->selfReflectionNestingLevelProperty->setAccessible(true);
116
        }
117
118
        $this->selfReflectionNestingLevelProperty->setValue($this, 0);
119
    }
120
121
    public function query(...$args)
122
    {
123
        return $this->pingBeforeMethodCall(__FUNCTION__, $args);
124
    }
125
126
    public function executeQuery($query, array $params = [], $types = [], QueryCacheProfile $qcp = null)
127
    {
128
        $args = [$query, $params, $types, $qcp];
129
130
        return $this->pingBeforeMethodCall(__FUNCTION__, $args);
131
    }
132
133
    public function beginTransaction()
134
    {
135
        $this->pingBeforeMethodCall(__FUNCTION__, []);
136
    }
137
138
    /**
139
     * @param string $sql
140
     *
141
     * @return Statement
142
     */
143
    public function prepare($sql): Statement
144
    {
145
        return $this->prepareWrapped($sql);
146
    }
147
148
    /**
149
     * returns a reconnect-wrapper for Statements.
150
     *
151
     * @param string $sql
152
     *
153
     * @return Statement
154
     */
155
    protected function prepareWrapped(string $sql): Statement
156
    {
157
        $this->pingAndReconnectOnFailure();
158
159
        return new Statement($sql, $this);
160
    }
161
162
    /**
163
     * do not use, only used by Statement-class
164
     * needs to be public for access from the Statement-class.
165
     *
166
     * @internal
167
     *
168
     * @param string $sql
169
     *
170
     * @return Driver\Statement
171
     * @throws \Doctrine\DBAL\DBALException
172
     */
173
    public function prepareUnwrapped(string $sql): Driver\Statement
174
    {
175
        // returns the actual statement
176
        return parent::prepare($sql);
177
    }
178
}
179