Passed
Push — issue/729 ( 98ba2c...3e0346 )
by Tomas Norre
87:46 queued 72:59
created

ProcessRepository::addProcess()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 6
nc 1
nop 2
dl 0
loc 9
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace AOE\Crawler\Domain\Repository;
6
7
/***************************************************************
8
 *  Copyright notice
9
 *
10
 *  (c) 2019 AOE GmbH <[email protected]>
11
 *
12
 *  All rights reserved
13
 *
14
 *  This script is part of the TYPO3 project. The TYPO3 project is
15
 *  free software; you can redistribute it and/or modify
16
 *  it under the terms of the GNU General Public License as published by
17
 *  the Free Software Foundation; either version 3 of the License, or
18
 *  (at your option) any later version.
19
 *
20
 *  The GNU General Public License can be found at
21
 *  http://www.gnu.org/copyleft/gpl.html.
22
 *
23
 *  This script is distributed in the hope that it will be useful,
24
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
25
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26
 *  GNU General Public License for more details.
27
 *
28
 *  This copyright notice MUST APPEAR in all copies of the script!
29
 ***************************************************************/
30
31
use AOE\Crawler\Configuration\ExtensionConfigurationProvider;
32
use AOE\Crawler\Domain\Model\Process;
33
use AOE\Crawler\Domain\Model\ProcessCollection;
34
use PDO;
35
use TYPO3\CMS\Core\Database\Connection;
36
use TYPO3\CMS\Core\Database\ConnectionPool;
37
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
38
use TYPO3\CMS\Core\Utility\GeneralUtility;
39
use TYPO3\CMS\Extbase\Object\ObjectManager;
40
use TYPO3\CMS\Extbase\Persistence\Repository;
41
42
/**
43
 * @internal since v9.2.5
44
 */
45
class ProcessRepository extends Repository
46
{
47
    public const TABLE_NAME = 'tx_crawler_process';
48
49
    /**
50
     * @var string
51
     * @deprecated Since v9.2.5 - This will be remove in v10
52
     */
53
    protected $tableName = 'tx_crawler_process';
54
55
    /**
56
     * @var QueryBuilder
57
     */
58
    protected $queryBuilder;
59
60
    /**
61
     * @var array
62
     */
63
    protected $extensionSettings = [];
64
65
    public function __construct()
66
    {
67
        $objectManager = GeneralUtility::makeInstance(ObjectManager::class);
68
69
        parent::__construct($objectManager);
70
71
        $this->queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable(self::TABLE_NAME);
72
73
        /** @var ExtensionConfigurationProvider $configurationProvider */
74
        $configurationProvider = GeneralUtility::makeInstance(ExtensionConfigurationProvider::class);
75
        $this->extensionSettings = $configurationProvider->getExtensionConfiguration();
76
    }
77
78
    /**
79
     * This method is used to find all cli processes within a limit.
80
     */
81
    public function findAll(): ProcessCollection
82
    {
83
        /** @var ProcessCollection $collection */
84
        $collection = GeneralUtility::makeInstance(ProcessCollection::class);
85
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable(self::TABLE_NAME);
86
87
        $statement = $queryBuilder
88
            ->select('*')
89
            ->from(self::TABLE_NAME)
90
            ->orderBy('ttl', 'DESC')
91
            ->execute();
92
93
        while ($row = $statement->fetch()) {
94
            $process = GeneralUtility::makeInstance(Process::class);
95
            $process->setProcessId($row['process_id']);
96
            $process->setActive($row['active']);
97
            $process->setTtl($row['ttl']);
98
            $process->setAssignedItemsCount($row['assigned_items_count']);
99
            $process->setDeleted($row['deleted']);
100
            $process->setSystemProcessId($row['system_process_id']);
101
            $collection->append($process);
102
        }
103
104
        return $collection;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $collection returns the type AOE\Crawler\Domain\Model\ProcessCollection which is incompatible with the return type mandated by TYPO3\CMS\Extbase\Persis...oryInterface::findAll() of TYPO3\CMS\Extbase\Persis...ryResultInterface|array.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
105
    }
106
107
    /**
108
     * @param string $processId
109
     */
110
    public function findByProcessId($processId)
111
    {
112
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable(self::TABLE_NAME);
113
114
        return $queryBuilder
115
            ->select('*')
116
            ->from(self::TABLE_NAME)
117
            ->where(
118
                $queryBuilder->expr()->eq('process_id', $queryBuilder->createNamedParameter($processId, PDO::PARAM_STR))
119
            )->execute()->fetch(0);
120
    }
121
122
    public function findAllActive(): ProcessCollection
123
    {
124
        /** @var ProcessCollection $collection */
125
        $collection = GeneralUtility::makeInstance(ProcessCollection::class);
126
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable(self::TABLE_NAME);
127
128
        $statement = $queryBuilder
129
            ->select('*')
130
            ->from(self::TABLE_NAME)
131
            ->where(
132
                $queryBuilder->expr()->eq('active', 1),
133
                $queryBuilder->expr()->eq('deleted', 0)
134
            )
135
            ->orderBy('ttl', 'DESC')
136
            ->execute();
137
138
        while ($row = $statement->fetch()) {
139
            $process = new Process();
140
            $process->setProcessId($row['process_id']);
141
            $process->setActive($row['active']);
142
            $process->setTtl($row['ttl']);
143
            $process->setAssignedItemsCount($row['assigned_items_count']);
144
            $process->setDeleted($row['deleted']);
145
            $process->setSystemProcessId($row['system_process_id']);
146
            $collection->append($process);
147
        }
148
149
        return $collection;
150
    }
151
152
    /**
153
     * @param string $processId
154
     */
155
    public function removeByProcessId($processId): void
156
    {
157
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable(self::TABLE_NAME);
158
159
        $queryBuilder
160
            ->delete(self::TABLE_NAME)
161
            ->where(
162
                $queryBuilder->expr()->eq('process_id', $queryBuilder->createNamedParameter($processId, PDO::PARAM_STR))
163
            )->execute();
164
    }
165
166
    /**
167
     * Returns the number of active processes.
168
     *
169
     * @return int
170
     * @deprecated Using ProcessRepository->countActive() is deprecated since 9.1.1 and will be removed in v11.x, please use ProcessRepository->findAllActive->count() instead
171
     * @codeCoverageIgnore
172
     */
173
    public function countActive()
174
    {
175
        return $this->findAllActive()->count();
176
    }
177
178
    /**
179
     * @return array|null
180
     *
181
     * Function is moved from ProcessCleanUpHook
182
     * TODO: Check why we need both getActiveProcessesOlderThanOneHour and getActiveOrphanProcesses, the get getActiveOrphanProcesses does not really check for Orphan in this implementation.
183
     */
184
    public function getActiveProcessesOlderThanOneHour()
185
    {
186
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable(self::TABLE_NAME);
187
        $activeProcesses = [];
188
        $statement = $queryBuilder
189
            ->select('process_id', 'system_process_id')
190
            ->from(self::TABLE_NAME)
191
            ->where(
192
                $queryBuilder->expr()->lte('ttl', intval(time() - $this->extensionSettings['processMaxRunTime'] - 3600)),
193
                $queryBuilder->expr()->eq('active', 1)
194
            )
195
            ->execute();
196
197
        while ($row = $statement->fetch()) {
198
            $activeProcesses[] = $row;
199
        }
200
201
        return $activeProcesses;
202
    }
203
204
    /**
205
     * Function is moved from ProcessCleanUpHook
206
     *
207
     * @return array
208
     * @see getActiveProcessesOlderThanOneHour
209
     */
210
    public function getActiveOrphanProcesses()
211
    {
212
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable(self::TABLE_NAME);
213
214
        return $queryBuilder
215
            ->select('process_id', 'system_process_id')
216
            ->from(self::TABLE_NAME)
217
            ->where(
218
                $queryBuilder->expr()->lte('ttl', intval(time() - $this->extensionSettings['processMaxRunTime'])),
219
                $queryBuilder->expr()->eq('active', 1)
220
            )
221
            ->execute()->fetchAll();
222
    }
223
224
    /**
225
     * Returns the number of processes that live longer than the given timestamp.
226
     */
227
    public function countNotTimeouted(int $ttl): int
228
    {
229
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable(self::TABLE_NAME);
230
231
        return $queryBuilder
232
            ->count('*')
233
            ->from(self::TABLE_NAME)
234
            ->where(
235
                $queryBuilder->expr()->eq('deleted', 0),
236
                $queryBuilder->expr()->gt('ttl', intval($ttl))
237
            )
238
            ->execute()
239
            ->fetchColumn(0);
240
    }
241
242
    /**
243
     * Get limit clause
244
     * @deprecated Using ProcessRepository::getLimitFromItemCountAndOffset() is deprecated since 9.1.1 and will be removed in v11.x, was not used, so will be removed
245
     */
246
    public static function getLimitFromItemCountAndOffset(int $itemCount, int $offset): string
247
    {
248
        $itemCount = filter_var($itemCount, FILTER_VALIDATE_INT, ['options' => ['min_range' => 1, 'default' => 20]]);
249
        $offset = filter_var($offset, FILTER_VALIDATE_INT, ['options' => ['min_range' => 0, 'default' => 0]]);
250
251
        return $offset . ', ' . $itemCount;
252
    }
253
254
    public function deleteProcessesWithoutItemsAssigned(): void
255
    {
256
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable(self::TABLE_NAME);
257
        $queryBuilder
258
            ->delete(self::TABLE_NAME)
259
            ->where(
260
                $queryBuilder->expr()->eq('assigned_items_count', 0)
261
            )
262
            ->execute();
263
    }
264
265
    public function deleteProcessesMarkedAsDeleted(): void
266
    {
267
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable(self::TABLE_NAME);
268
        $queryBuilder
269
            ->delete(self::TABLE_NAME)
270
            ->where(
271
                $queryBuilder->expr()->eq('deleted', 1)
272
            )
273
            ->execute();
274
    }
275
276
    public function isProcessActive(string $processId): bool
277
    {
278
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable(self::TABLE_NAME);
279
        $isActive = $queryBuilder
280
            ->select('active')
281
            ->from(self::TABLE_NAME)
282
            ->where(
283
                $queryBuilder->expr()->eq('process_id', $queryBuilder->createNamedParameter($processId))
284
            )
285
            ->orderBy('ttl')
286
            ->execute()
287
            ->fetchColumn(0);
288
289
        return (bool) $isActive;
290
    }
291
292
    /**
293
     * @param $numberOfAffectedRows
294
     */
295
    public function updateProcessAssignItemsCount($numberOfAffectedRows, string $processId): void
296
    {
297
        GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable(self::TABLE_NAME)
298
            ->update(
299
                self::TABLE_NAME,
300
                ['assigned_items_count' => (int) $numberOfAffectedRows],
301
                ['process_id' => $processId]
302
            );
303
    }
304
305
    public function markRequestedProcessesAsNotActive(array $processIds): void
306
    {
307
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable(self::TABLE_NAME);
308
        $queryBuilder->update(self::TABLE_NAME)
309
            ->where(
310
                'NOT EXISTS (
311
                SELECT * FROM tx_crawler_queue
312
                    WHERE tx_crawler_queue.process_id = tx_crawler_process.process_id
313
                    AND tx_crawler_queue.exec_time = 0
314
                )',
315
                $queryBuilder->expr()->in('process_id', $queryBuilder->createNamedParameter($processIds, Connection::PARAM_STR_ARRAY)),
316
                $queryBuilder->expr()->eq('deleted', 0)
317
            )
318
            ->set('active', 0)
319
            ->execute();
320
    }
321
322
    public function addProcess(string $processId, int $systemProcessId): void
323
    {
324
        GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable(self::TABLE_NAME)->insert(
325
            self::TABLE_NAME,
326
            [
327
                'process_id' => $processId,
328
                'active' => 1,
329
                'ttl' => time() + (int) $this->extensionSettings['processMaxRunTime'],
330
                'system_process_id' => $systemProcessId,
331
            ]
332
        );
333
    }
334
}
335