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.
Completed
Pull Request — master (#154)
by joseph
20:20
created

BulkSimpleEntityCreator::runQuery()   B

Complexity

Conditions 7
Paths 7

Size

Total Lines 46
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 0
Metric Value
cc 7
eloc 30
nc 7
nop 0
dl 0
loc 46
ccs 0
cts 46
cp 0
crap 56
rs 8.5066
c 0
b 0
f 0
1
<?php declare(strict_types=1);
2
3
namespace EdmondsCommerce\DoctrineStaticMeta\Entity\Savers;
4
5
use Doctrine\ORM\EntityManagerInterface;
6
use Doctrine\ORM\Mapping\ClassMetadataInfo;
7
use EdmondsCommerce\DoctrineStaticMeta\Entity\Fields\Factories\UuidFactory;
8
use EdmondsCommerce\DoctrineStaticMeta\Entity\Interfaces\EntityInterface;
9
use EdmondsCommerce\DoctrineStaticMeta\Entity\Savers\BulkEntityUpdater\BulkSimpleEntityCreatorHelper;
10
use EdmondsCommerce\DoctrineStaticMeta\Schema\MysqliConnectionFactory;
11
use EdmondsCommerce\DoctrineStaticMeta\Schema\UuidFunctionPolyfill;
12
use Ramsey\Uuid\Doctrine\UuidBinaryOrderedTimeType;
13
14
class BulkSimpleEntityCreator extends AbstractBulkProcess
15
{
16
    /**
17
     * @var BulkSimpleEntityCreatorHelper
18
     */
19
    private $helper;
20
    /**
21
     * @var string
22
     */
23
    private $tableName;
24
    /**
25
     * @var string
26
     */
27
    private $entityFqn;
28
    /**
29
     * Is the UUID binary
30
     *
31
     * @var bool
32
     */
33
    private $isBinaryUuid = true;
34
    /**
35
     * @var ClassMetadataInfo
36
     */
37
    private $meta;
38
    /**
39
     * @var string
40
     */
41
    private $primaryKeyCol;
42
    /**
43
     * @var \mysqli
44
     */
45
    private $mysqli;
46
    /**
47
     * @var UuidFunctionPolyfill
48
     */
49
    private $uuidFunctionPolyfill;
50
    /**
51
     * @var UuidFactory
52
     */
53
    private $uuidFactory;
54
    /**
55
     * @var string
56
     */
57
    private $query;
58
    /**
59
     * For creation this should always be 100%, so 1
60
     *
61
     * @var int
62
     */
63
    private $requireAffectedRatio = 1;
64
    /**
65
     * @var int
66
     */
67
    private $totalAffectedRows = 0;
68
69
    public function __construct(
70
        EntityManagerInterface $entityManager,
71
        MysqliConnectionFactory $mysqliConnectionFactory,
72
        UuidFunctionPolyfill $uuidFunctionPolyfill,
73
        UuidFactory $uuidFactory
74
    ) {
75
        parent::__construct($entityManager);
76
        $this->mysqli               = $mysqliConnectionFactory->createFromEntityManager($entityManager);
77
        $this->uuidFunctionPolyfill = $uuidFunctionPolyfill;
78
        $this->uuidFactory          = $uuidFactory;
79
    }
80
81
82
    public function addEntityToSave(EntityInterface $entity)
83
    {
84
        throw new \RuntimeException('You should not try to save Entities with this saver');
85
    }
86
87
    public function addEntitiesToSave(array $entities)
88
    {
89
        foreach ($entities as $entityData) {
90
            if (\is_array($entityData)) {
91
                $this->addEntityCreationData($entityData);
92
                continue;
93
            }
94
            throw new \InvalidArgumentException('You should only pass in simple arrays of scalar entity data');
95
        }
96
    }
97
98
    public function addEntityCreationData(array $entityData)
99
    {
100
        $this->entitiesToSave[] = $entityData;
101
        $this->bulkSaveIfChunkBigEnough();
102
    }
103
104
    public function setHelper(BulkSimpleEntityCreatorHelper $helper): void
105
    {
106
        $this->helper        = $helper;
107
        $this->tableName     = $helper->getTableName();
108
        $this->entityFqn     = $helper->getEntityFqn();
109
        $this->meta          = $this->entityManager->getClassMetadata($this->entityFqn);
110
        $this->primaryKeyCol = $this->meta->getSingleIdentifierFieldName();
111
        $this->isBinaryUuid  = $this->isBinaryUuid();
112
        $this->runPolyfillIfRequired();
113
    }
114
115
    private function isBinaryUuid(): bool
116
    {
117
        $idMapping = $this->meta->getFieldMapping($this->meta->getSingleIdentifierFieldName());
118
119
        return $idMapping['type'] === UuidBinaryOrderedTimeType::NAME;
120
    }
121
122
    private function runPolyfillIfRequired(): void
123
    {
124
        if (false === $this->isBinaryUuid) {
125
            return;
126
        }
127
        $this->uuidFunctionPolyfill->run();
128
    }
129
130
    protected function freeResources()
131
    {
132
        /**
133
         * AS these are not actually entities, lets empty them out before free resources tries to detach from the entity manager
134
         */
135
        $this->entitiesToSave = [];
136
        parent::freeResources();
137
    }
138
139
    protected function doSave(): void
140
    {
141
        foreach ($this->entitiesToSave as $entityData) {
142
            $this->appendToQuery($this->buildSql($entityData));
143
        }
144
        $this->runQuery();
145
        $this->reset();
146
    }
147
148
    private function appendToQuery(string $sql)
149
    {
150
        $this->query .= "\n$sql";
151
    }
152
153
    private function buildSql(array $entityData): string
154
    {
155
        $sql  = "insert into {$this->tableName} set ";
156
        $sqls = [
157
            $this->primaryKeyCol . ' = ' . $this->generateId(),
158
        ];
159
        foreach ($entityData as $key => $value) {
160
            if ($key === $this->primaryKeyCol) {
161
                throw new \InvalidArgumentException(
162
                    'You should not pass in IDs, they will be auto generated'
163
                );
164
            }
165
            $value  = $this->mysqli->escape_string((string)$value);
166
            $sqls[] = "`$key` = '$value'";
167
        }
168
        $sql .= implode(', ', $sqls) . ';';
169
170
        return $sql;
171
    }
172
173
    private function generateId()
174
    {
175
        if ($this->isBinaryUuid) {
176
            $uuid = (string)$this->uuidFactory->getOrderedTimeUuid();
177
178
            return "UUID_TO_BIN('$uuid')";
179
        }
180
181
        return (string)$this->uuidFactory->getUuid();
182
    }
183
184
    private function runQuery(): void
185
    {
186
        if ('' === $this->query) {
187
            return;
188
        }
189
        $this->query = "
190
           START TRANSACTION;
191
           SET FOREIGN_KEY_CHECKS = 0; 
192
           SET UNIQUE_CHECKS = 0;
193
           {$this->query}             
194
           SET FOREIGN_KEY_CHECKS = 1; 
195
           SET UNIQUE_CHECKS = 1;
196
           COMMIT;";
197
        $result      = $this->mysqli->multi_query($this->query);
198
        if (true !== $result) {
199
            throw new \RuntimeException(
200
                'Multi Query returned false which means the first statement failed: ' .
201
                $this->mysqli->error
202
            );
203
        }
204
        $affectedRows = 0;
205
        $queryCount   = 0;
206
        do {
207
            $queryCount++;
208
            if (0 !== $this->mysqli->errno) {
209
                throw new \RuntimeException(
210
                    'Query #' . $queryCount .
211
                    ' got MySQL Error #' . $this->mysqli->errno .
212
                    ': ' . $this->mysqli->error
213
                    . "\nQuery: " . $this->getQueryLine($queryCount) . "'\n"
214
                );
215
            }
216
            $affectedRows += max($this->mysqli->affected_rows, 0);
217
            if (false === $this->mysqli->more_results()) {
218
                break;
219
            }
220
            $this->mysqli->next_result();
221
        } while (true);
222
        if ($affectedRows < count($this->entitiesToSave) * $this->requireAffectedRatio) {
223
            throw new \RuntimeException(
224
                'Affected rows count of ' . $affectedRows .
225
                ' does match the expected count of entitiesToSave ' . count($this->entitiesToSave)
226
            );
227
        }
228
        $this->totalAffectedRows += $affectedRows;
229
        $this->mysqli->commit();
230
    }
231
232
    private function getQueryLine(int $line): string
233
    {
234
        $lines = explode(';', $this->query);
235
236
        return $lines[$line + 1];
237
    }
238
239
    private function reset()
240
    {
241
        $this->entityCreationData = [];
0 ignored issues
show
Bug Best Practice introduced by
The property entityCreationData does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
242
        $this->query              = '';
243
    }
244
245
}