Completed
Push — master ( 799f25...7682df )
by Timo
09:35
created

Queue   F

Complexity

Total Complexity 72

Size/Duplication

Total Lines 854
Duplicated Lines 10.07 %

Coupling/Cohesion

Components 2
Dependencies 10

Importance

Changes 5
Bugs 0 Features 1
Metric Value
c 5
b 0
f 1
dl 86
loc 854
rs 3.3846
wmc 72
lcom 2
cbo 10

24 Methods

Rating   Name   Duplication   Size   Complexity  
B initialize() 0 41 6
A initializeIndexingConfiguration() 0 23 1
A getIndexingConfigurationByItem() 0 15 2
A getIndexingConfigurationsByItem() 0 15 2
B updateItem() 0 32 4
C addItem() 0 71 12
B getItemChangedTime() 4 47 5
A getPageItemChangedTime() 0 16 2
A getLocalizableItemChangedTime() 0 19 2
A containsItem() 12 12 1
A containsIndexedItem() 13 13 1
B deleteItem() 28 28 3
B deleteItemsByType() 29 29 3
B deleteItemsBySite() 0 50 5
A deleteAllItems() 0 4 1
A getItem() 0 21 2
A getItems() 0 12 1
A getItemsCountBySite() 0 17 2
A getAllItemsCount() 0 11 1
A getItemsToIndex() 0 23 2
A getLastIndexTime() 0 19 2
A getLastIndexedItemId() 0 18 2
A markItemAsFailed() 0 23 3
B getIndexQueueItemObjectsFromRecords() 0 52 7

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Queue often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Queue, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace ApacheSolrForTypo3\Solr\IndexQueue;
3
4
/***************************************************************
5
 *  Copyright notice
6
 *
7
 *  (c) 2009-2015 Ingo Renner <[email protected]>
8
 *  All rights reserved
9
 *
10
 *  This script is part of the TYPO3 project. The TYPO3 project is
11
 *  free software; you can redistribute it and/or modify
12
 *  it under the terms of the GNU General Public License as published by
13
 *  the Free Software Foundation; either version 2 of the License, or
14
 *  (at your option) any later version.
15
 *
16
 *  The GNU General Public License can be found at
17
 *  http://www.gnu.org/copyleft/gpl.html.
18
 *
19
 *  This script is distributed in the hope that it will be useful,
20
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
21
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22
 *  GNU General Public License for more details.
23
 *
24
 *  This copyright notice MUST APPEAR in all copies of the script!
25
 ***************************************************************/
26
27
use ApacheSolrForTypo3\Solr\Utility\DatabaseUtility;
28
use ApacheSolrForTypo3\Solr\Site;
29
use ApacheSolrForTypo3\Solr\Util;
30
use TYPO3\CMS\Backend\Utility\BackendUtility;
31
use TYPO3\CMS\Core\Utility\GeneralUtility;
32
33
/**
34
 * The Indexing Queue. It allows us to decouple from frontend indexing and
35
 * reacting to changes faster.
36
 *
37
 * @author Ingo Renner <[email protected]>
38
 * @package TYPO3
39
 * @subpackage solr
40
 */
41
class Queue
42
{
43
44
    // FIXME some of the methods should be renamed to plural forms
45
    // FIXME singular form methods should deal with exactly one item only
46
47
48
    /**
49
     * Returns the timestamp of the last indexing run.
50
     *
51
     * @param integer $rootPageId The root page uid for which to get
52
     *      the last indexed item id
53
     * @return integer Timestamp of last index run.
54
     */
55
    public function getLastIndexTime($rootPageId)
1 ignored issue
show
Coding Style introduced by
getLastIndexTime uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
56
    {
57
        $lastIndexTime = 0;
58
59
        $lastIndexedRow = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
60
            'indexed',
61
            'tx_solr_indexqueue_item',
62
            'root = ' . (int)$rootPageId,
63
            '',
64
            'indexed DESC',
65
            1
66
        );
67
68
        if ($lastIndexedRow[0]['indexed']) {
69
            $lastIndexTime = $lastIndexedRow[0]['indexed'];
70
        }
71
72
        return $lastIndexTime;
73
    }
74
75
    /**
76
     * Returns the uid of the last indexed item in the queue
77
     *
78
     * @param integer $rootPageId The root page uid for which to get
79
     *      the last indexed item id
80
     * @return integer The last indexed item's ID.
81
     */
82
    public function getLastIndexedItemId($rootPageId)
1 ignored issue
show
Coding Style introduced by
getLastIndexedItemId uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
83
    {
84
        $lastIndexedItemId = 0;
85
86
        $lastIndexedItemRow = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
87
            'uid',
88
            'tx_solr_indexqueue_item',
89
            'root = ' . (int)$rootPageId,
90
            '',
91
            'indexed DESC',
92
            1
93
        );
94
        if ($lastIndexedItemRow[0]['uid']) {
95
            $lastIndexedItemId = $lastIndexedItemRow[0]['uid'];
96
        }
97
98
        return $lastIndexedItemId;
99
    }
100
101
    /**
102
     * Truncate and rebuild the tx_solr_indexqueue_item table. This is the most
103
     * complete way to force reindexing, or to build the Index Queue for the
104
     * first time. The Index Queue initialization is site-specific.
105
     *
106
     * @param Site $site The site to initialize
107
     * @param string $indexingConfigurationName Name of a specific
108
     *      indexing configuration
109
     * @return array An array of booleans, each representing whether the
110
     *      initialization for an indexing configuration was successful
111
     */
112
    public function initialize(Site $site, $indexingConfigurationName = '')
1 ignored issue
show
Coding Style introduced by
initialize uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
113
    {
114
        $indexingConfigurations = array();
115
        $initializationStatus = array();
116
117
        if (empty($indexingConfigurationName)) {
118
            $solrConfiguration = $site->getSolrConfiguration();
119
            $indexingConfigurations = $solrConfiguration->getEnabledIndexQueueConfigurationNames();
120
        } else {
121
            $indexingConfigurations[] = $indexingConfigurationName;
122
        }
123
124
        foreach ($indexingConfigurations as $indexingConfigurationName) {
125
            $initializationStatus[$indexingConfigurationName] = $this->initializeIndexingConfiguration(
126
                $site,
127
                $indexingConfigurationName
128
            );
129
        }
130
131
        if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['postProcessIndexQueueInitialization'])) {
132
            foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['postProcessIndexQueueInitialization'] as $classReference) {
133
                $indexQueueInitializationPostProcessor = GeneralUtility::getUserObj($classReference);
134
135
                if ($indexQueueInitializationPostProcessor instanceof InitializationPostProcessor) {
136
                    $indexQueueInitializationPostProcessor->postProcessIndexQueueInitialization(
137
                        $site,
138
                        $indexingConfigurations,
139
                        $initializationStatus
140
                    );
141
                } else {
142
                    throw new \UnexpectedValueException(
143
                        get_class($indexQueueInitializationPostProcessor) .
144
                        ' must implement interface ApacheSolrForTypo3\Solr\IndexQueue\InitializationPostProcessor',
145
                        1345815561
146
                    );
147
                }
148
            }
149
        }
150
151
        return $initializationStatus;
152
    }
153
154
    /**
155
     * Initializes the Index Queue for a specific indexing configuration.
156
     *
157
     * @param Site $site The site to initialize
158
     * @param string $indexingConfigurationName name of a specific
159
     *      indexing configuration
160
     * @return boolean TRUE if the initialization was successful, FALSE otherwise
161
     */
162
    protected function initializeIndexingConfiguration(
163
        Site $site,
164
        $indexingConfigurationName
165
    ) {
166
        // clear queue
167
        $this->deleteItemsBySite($site, $indexingConfigurationName);
168
169
        $solrConfiguration = $site->getSolrConfiguration();
170
171
        $tableToIndex = $solrConfiguration->getIndexQueueTableNameOrFallbackToConfigurationName($indexingConfigurationName);
172
        $initializerClass = $solrConfiguration->getIndexQueueInitializerClassByConfigurationName($indexingConfigurationName);
173
174
        $initializer = GeneralUtility::makeInstance($initializerClass);
175
        /** @var $initializer \ApacheSolrForTypo3\Solr\IndexQueue\Initializer\AbstractInitializer */
176
        $initializer->setSite($site);
177
        $initializer->setType($tableToIndex);
178
        $initializer->setIndexingConfigurationName($indexingConfigurationName);
179
180
        $indexConfiguration = $solrConfiguration->getIndexQueueConfigurationByName($indexingConfigurationName);
181
        $initializer->setIndexingConfiguration($indexConfiguration);
182
183
        return $initializer->initialize();
184
    }
185
186
    /**
187
     * Gets the indexing configuration to use for an item.
188
     * Sometimes, when there are multiple configurations for a certain item type
189
     * (table) it can be hard or even impossible to find which one to use
190
     * though.
191
     * Currently selects the first indexing configuration where the name matches
192
     * the itemType or where the configured tbale is the same as the itemType.
193
     *
194
     * !!! Might return incorrect results for complex configurations !!!
195
     * Try to set the indexingConfiguration directly when using the updateItem()
196
     * method in such situations.
197
     *
198
     * @param string $itemType The item's type, usually a table name.
199
     * @param string $itemUid The item's uid, usually an integer uid, could be a
200
     *      different value for non-database-record types.
201
     * @param integer $rootPageId The configuration's page tree's root page id.
202
     *      Optional, not needed for all types.
203
     * @return string The indexing configuration's name to use when indexing
204
     * @deprecated Use getIndexingConfigurationsByItem() now, which behaves
205
     *      almost the same way but returns an array of configurations
206
     */
207
    protected function getIndexingConfigurationByItem(
208
        $itemType,
209
        $itemUid,
210
        $rootPageId = null
211
    ) {
212
        $indexingConfigurationName = '';
213
214
        $configurations = $this->getIndexingConfigurationsByItem($itemType,
215
            $itemUid, $rootPageId);
216
        if (count($configurations) > 0) {
217
            $indexingConfigurationName = $configurations[0];
218
        }
219
220
        return $indexingConfigurationName;
221
    }
222
223
    /**
224
     * Gets the indexing configurations to use for an item.
225
     * Multiple configurations for a certain item type (table) might be available.
226
     *
227
     * @param string $itemType The item's type, usually a table name.
228
     * @param string $itemUid The item's uid, usually an integer uid, could be a
229
     *      different value for non-database-record types.
230
     * @param integer $rootPageId The configuration's page tree's root page id.
231
     *      Optional, not needed for all types.
232
     * @return array<string> The indexing configurations names to use when indexing
233
     */
234
    protected function getIndexingConfigurationsByItem(
235
        $itemType,
236
        $itemUid,
0 ignored issues
show
Unused Code introduced by
The parameter $itemUid is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
237
        $rootPageId = null
238
    ) {
239
        $possibleIndexingConfigurationNames = array();
240
241
        if (!is_null($rootPageId)) {
242
            // get configuration for the root's branch
243
            $solrConfiguration = Util::getSolrConfigurationFromPageId($rootPageId);
244
            $possibleIndexingConfigurationNames = $solrConfiguration->getIndexQueueConfigurationNamesByTableName($itemType);
245
        }
246
247
        return $possibleIndexingConfigurationNames;
248
    }
249
250
    /**
251
     * Marks an item as needing (re)indexing.
252
     *
253
     * Like with Solr itself, there's no add method, just a simple update method
254
     * that handles the adds, too.
255
     *
256
     * @param string $itemType The item's type, usually a table name.
257
     * @param string $itemUid The item's uid, usually an integer uid, could be a
258
     *      different value for non-database-record types.
259
     * @param string $indexingConfiguration The item's indexing configuration to use.
260
     *      Optional, overwrites existing / determined configuration.
261
     * @param int $forcedChangeTime The change time for the item if set, otherwise
262
     *          value from getItemChangedTime() is used.
263
     */
264
    public function updateItem(
1 ignored issue
show
Coding Style introduced by
updateItem uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
265
        $itemType,
266
        $itemUid,
267
        $indexingConfiguration = null,
268
        $forcedChangeTime = 0
269
    ) {
270
        $itemInQueue = $this->containsItem($itemType, $itemUid);
271
272
        if ($itemInQueue) {
273
            // update if that item is in the queue already
274
            $changes = array(
275
                'changed' => ($forcedChangeTime > 0)
276
                                ? $forcedChangeTime
277
                                : $this->getItemChangedTime($itemType, $itemUid)
278
            );
279
280
            if (!empty($indexingConfiguration)) {
281
                $changes['indexing_configuration'] = $indexingConfiguration;
282
            }
283
284
            $GLOBALS['TYPO3_DB']->exec_UPDATEquery(
285
                'tx_solr_indexqueue_item',
286
                'item_type = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr($itemType,
287
                    'tx_solr_indexqueue_item') .
288
                ' AND item_uid = ' . (int)$itemUid,
289
                $changes
290
            );
291
        } else {
292
            // add the item since it's not in the queue yet
293
            $this->addItem($itemType, $itemUid, $indexingConfiguration);
294
        }
295
    }
296
297
    /**
298
     * Adds an item to the index queue.
299
     *
300
     * Not meant for public use.
301
     *
302
     * @param string $itemType The item's type, usually a table name.
303
     * @param string $itemUid The item's uid, usually an integer uid, could be a
304
     *      different value for non-database-record types.
305
     * @param string $indexingConfiguration The item's indexing configuration to use.
306
     *      Optional, overwrites existing / determined configuration.
307
     * @return void
308
     */
309
    private function addItem($itemType, $itemUid, $indexingConfiguration)
1 ignored issue
show
Coding Style introduced by
addItem uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
310
    {
311
        $additionalRecordFields = '';
312
        if ($itemType == 'pages') {
313
            $additionalRecordFields = ', doktype, uid';
314
        }
315
316
        $record = BackendUtility::getRecord($itemType, $itemUid,
317
            'pid' . $additionalRecordFields);
318
319
        if (empty($record) || ($itemType == 'pages' && !Util::isAllowedPageType($record))) {
0 ignored issues
show
Bug introduced by
It seems like $record defined by \TYPO3\CMS\Backend\Utili...additionalRecordFields) on line 316 can also be of type boolean; however, ApacheSolrForTypo3\Solr\Util::isAllowedPageType() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
320
            return;
321
        }
322
323
        if ($itemType == 'pages') {
324
            $rootPageId = Util::getRootPageId($itemUid);
325
        } else {
326
            $rootPageId = Util::getRootPageId($record['pid'], true);
327
        }
328
329
        if (Util::isRootPage($rootPageId)) {
330
            $item = array(
331
                'root' => $rootPageId,
332
                'item_type' => $itemType,
333
                'item_uid' => $itemUid,
334
                'changed' => $this->getItemChangedTime($itemType, $itemUid),
335
                'errors' => ''
336
            );
337
338
            if (!empty($indexingConfiguration)) {
339
                $indexingConfigurationList = array($indexingConfiguration);
340
            } else {
341
                $indexingConfigurationList = $this->getIndexingConfigurationsByItem(
342
                    $itemType, $itemUid, $rootPageId
343
                );
344
            }
345
346
            $solrConfiguration = Util::getSolrConfigurationFromPageId($rootPageId);
347
348
            // make a backup of the current item
349
            $baseItem = $item;
350
            foreach ($indexingConfigurationList as $indexingConfigurationCurrent) {
351
                $item = $baseItem;
352
                $item['indexing_configuration'] = $indexingConfigurationCurrent;
353
354
                $addItemToQueue = true;
355
                // Ensure additionalWhereClause is applied.
356
                $additionalWhere = $solrConfiguration->getIndexQueueAdditionalWhereClauseByConfigurationName($item['indexing_configuration']);
357
                if ($additionalWhere !== '') {
358
                    $indexingConfigurationCheckRecord = BackendUtility::getRecord(
359
                        $itemType,
360
                        $itemUid,
361
                        'pid' . $additionalRecordFields,
362
                        $additionalWhere
363
                    );
364
365
                    if (empty($indexingConfigurationCheckRecord)) {
366
                        // item does not match the indexing configuration's additionalWhereClause
367
                        $addItemToQueue = false;
368
                    }
369
                }
370
371
                if ($addItemToQueue) {
372
                    $GLOBALS['TYPO3_DB']->exec_INSERTquery(
373
                        'tx_solr_indexqueue_item',
374
                        $item
375
                    );
376
                }
377
            }
378
        }
379
    }
380
381
    /**
382
     * Determines the time for when an item should be indexed. This timestamp
383
     * is then stored in the changed column in the Index Queue.
384
     *
385
     * The changed timestamp usually is now - time(). For records which are set
386
     * to published at a later time, this timestamp is the start time. So if a
387
     * future start time has been set, that will be used to delay indexing
388
     * of an item.
389
     *
390
     * @param string $itemType The item's table name.
391
     * @param string $itemUid The item's uid, usually an integer uid, could be a
392
     *      different value for non-database-record types.
393
     * @return integer Timestamp of the item's changed time or future start time
394
     */
395
    protected function getItemChangedTime($itemType, $itemUid)
1 ignored issue
show
Coding Style introduced by
getItemChangedTime uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
396
    {
397
        $itemTypeHasStartTimeColumn = false;
398
        $changedTimeColumns = $GLOBALS['TCA'][$itemType]['ctrl']['tstamp'];
399
        $startTime = 0;
400
        $pageChangedTime = 0;
401
402 View Code Duplication
        if (!empty($GLOBALS['TCA'][$itemType]['ctrl']['enablecolumns']['starttime'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
403
            $itemTypeHasStartTimeColumn = true;
404
            $changedTimeColumns .= ', ' . $GLOBALS['TCA'][$itemType]['ctrl']['enablecolumns']['starttime'];
405
        }
406
        if ($itemType == 'pages') {
407
            // does not carry time information directly, but needed to support
408
            // canonical pages
409
            $changedTimeColumns .= ', content_from_pid';
410
        }
411
412
        $record = BackendUtility::getRecord($itemType, $itemUid,
413
            $changedTimeColumns);
414
        $itemChangedTime = $record[$GLOBALS['TCA'][$itemType]['ctrl']['tstamp']];
415
416
        if ($itemTypeHasStartTimeColumn) {
417
            $startTime = $record[$GLOBALS['TCA'][$itemType]['ctrl']['enablecolumns']['starttime']];
418
        }
419
420
        if ($itemType == 'pages') {
421
            $record['uid'] = $itemUid;
422
            // overrule the page's last changed time with the most recent
423
            //content element change
424
            $pageChangedTime = $this->getPageItemChangedTime($record);
0 ignored issues
show
Bug introduced by
It seems like $record defined by \TYPO3\CMS\Backend\Utili...d, $changedTimeColumns) on line 412 can also be of type boolean; however, ApacheSolrForTypo3\Solr\...etPageItemChangedTime() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
425
        }
426
427
        $localizationsChangedTime = $this->getLocalizableItemChangedTime($itemType,
428
            $itemUid);
429
430
        // if start time exists and start time is higher than last changed timestamp
431
        // then set changed to the future start time to make the item
432
        // indexed at a later time
433
        $changedTime = max(
434
            $itemChangedTime,
435
            $pageChangedTime,
436
            $localizationsChangedTime,
437
            $startTime
438
        );
439
440
        return $changedTime;
441
    }
442
443
    /**
444
     * Gets the most recent changed time of a page's content elements
445
     *
446
     * @param array $page Partial page record
447
     * @return integer Timestamp of the most recent content element change
448
     */
449
    protected function getPageItemChangedTime(array $page)
1 ignored issue
show
Coding Style introduced by
getPageItemChangedTime uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
450
    {
451
        if (!empty($page['content_from_pid'])) {
452
            // canonical page, get the original page's last changed time
453
            $pageContentLastChangedTime = $this->getPageItemChangedTime(array('uid' => $page['content_from_pid']));
454
        } else {
455
            $pageContentLastChangedTime = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow(
456
                'MAX(tstamp) AS changed_time',
457
                'tt_content',
458
                'pid = ' . (int)$page['uid']
459
            );
460
            $pageContentLastChangedTime = $pageContentLastChangedTime['changed_time'];
461
        }
462
463
        return $pageContentLastChangedTime;
464
    }
465
466
    /**
467
     * Gets the most recent changed time for an item taking into account
468
     * localized records.
469
     *
470
     * @param string $itemType The item's type, usually a table name.
471
     * @param string $itemUid The item's uid, usually an integer uid, could be a
472
     *      different value for non-database-record types.
473
     * @return integer Timestamp of the most recent content element change
474
     */
475
    protected function getLocalizableItemChangedTime($itemType, $itemUid)
1 ignored issue
show
Coding Style introduced by
getLocalizableItemChangedTime uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
476
    {
477
        $localizedChangedTime = 0;
478
479
        if (isset($GLOBALS['TCA'][$itemType]['ctrl']['transOrigPointerField'])) {
480
            // table is localizable
481
            $translationOriginalPointerField = $GLOBALS['TCA'][$itemType]['ctrl']['transOrigPointerField'];
482
483
            $itemUid = intval($itemUid);
484
            $localizedChangedTime = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow(
485
                'MAX(tstamp) AS changed_time',
486
                $itemType,
487
                "uid = $itemUid OR $translationOriginalPointerField = $itemUid"
488
            );
489
            $localizedChangedTime = $localizedChangedTime['changed_time'];
490
        }
491
492
        return $localizedChangedTime;
493
    }
494
495
    /**
496
     * Checks whether the Index Queue contains a specific item.
497
     *
498
     * @param string $itemType The item's type, usually a table name.
499
     * @param string $itemUid The item's uid, usually an integer uid, could be a
500
     *      different value for non-database-record types.
501
     * @return boolean TRUE if the item is found in the queue, FALSE otherwise
502
     */
503 View Code Duplication
    public function containsItem($itemType, $itemUid)
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
Coding Style introduced by
containsItem uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
504
    {
505
        $itemIsInQueue = (boolean)$GLOBALS['TYPO3_DB']->exec_SELECTcountRows(
506
            'uid',
507
            'tx_solr_indexqueue_item',
508
            'item_type = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr($itemType,
509
                'tx_solr_indexqueue_item') .
510
            ' AND item_uid = ' . (int)$itemUid
511
        );
512
513
        return $itemIsInQueue;
514
    }
515
516
    /**
517
     * Checks whether the Index Queue contains a specific item that has been
518
     * marked as indexed.
519
     *
520
     * @param string $itemType The item's type, usually a table name.
521
     * @param string $itemUid The item's uid, usually an integer uid, could be a
522
     *      different value for non-database-record types.
523
     * @return boolean TRUE if the item is found in the queue and marked as
524
     *      indexed, FALSE otherwise
525
     */
526 View Code Duplication
    public function containsIndexedItem($itemType, $itemUid)
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
Coding Style introduced by
containsIndexedItem uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
527
    {
528
        $itemIsInQueue = (boolean)$GLOBALS['TYPO3_DB']->exec_SELECTcountRows(
529
            'uid',
530
            'tx_solr_indexqueue_item',
531
            'item_type = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr($itemType,
532
                'tx_solr_indexqueue_item') .
533
            ' AND item_uid = ' . (int)$itemUid .
534
            ' AND indexed > 0'
535
        );
536
537
        return $itemIsInQueue;
538
    }
539
540
    /**
541
     * Removes an item from the Index Queue.
542
     *
543
     * @param string $itemType The type of the item to remove, usually a table name.
544
     * @param integer $itemUid The uid of the item to remove
545
     */
546 View Code Duplication
    public function deleteItem($itemType, $itemUid)
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
Coding Style introduced by
deleteItem uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
547
    {
548
        $uidList = array();
549
550
        // get the item uids to use them in the deletes afterwards
551
        $items = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
552
            'uid',
553
            'tx_solr_indexqueue_item',
554
            'item_type = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr($itemType,
555
                'tx_solr_indexqueue_item') .
556
            ' AND item_uid = ' . intval($itemUid)
557
        );
558
559
        if (count($items)) {
560
            foreach ($items as $item) {
561
                $uidList[] = $item['uid'];
562
            }
563
564
            $GLOBALS['TYPO3_DB']->exec_DELETEquery(
565
                'tx_solr_indexqueue_item',
566
                'uid IN(' . implode(',', $uidList) . ')'
567
            );
568
            $GLOBALS['TYPO3_DB']->exec_DELETEquery(
569
                'tx_solr_indexqueue_indexing_property',
570
                'item_id IN(' . implode(',', $uidList) . ')'
571
            );
572
        }
573
    }
574
575
    /**
576
     * Removes all items of a certain type from the Index Queue.
577
     *
578
     * @param string $itemType The type of items to remove, usually a table name.
579
     */
580 View Code Duplication
    public function deleteItemsByType($itemType)
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
Coding Style introduced by
deleteItemsByType uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
581
    {
582
        $uidList = array();
583
584
        // get the item uids to use them in the deletes afterwards
585
        $items = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
586
            'uid',
587
            'tx_solr_indexqueue_item',
588
            'item_type = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr(
589
                $itemType,
590
                'tx_solr_indexqueue_item'
591
            )
592
        );
593
594
        if (count($items)) {
595
            foreach ($items as $item) {
596
                $uidList[] = $item['uid'];
597
            }
598
599
            $GLOBALS['TYPO3_DB']->exec_DELETEquery(
600
                'tx_solr_indexqueue_item',
601
                'uid IN(' . implode(',', $uidList) . ')'
602
            );
603
            $GLOBALS['TYPO3_DB']->exec_DELETEquery(
604
                'tx_solr_indexqueue_indexing_property',
605
                'item_id IN(' . implode(',', $uidList) . ')'
606
            );
607
        }
608
    }
609
610
    /**
611
     * Removes all items of a certain site from the Index Queue. Accepts an
612
     * optional parameter to limit the deleted items by indexing configuration.
613
     *
614
     * @param Site $site The site to remove items for.
615
     * @param string $indexingConfigurationName Name of a specific indexing
616
     *      configuration
617
     */
618
    public function deleteItemsBySite(
1 ignored issue
show
Coding Style introduced by
deleteItemsBySite uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
619
        Site $site,
620
        $indexingConfigurationName = ''
621
    ) {
622
        $rootPageConstraint = 'tx_solr_indexqueue_item.root = ' . $site->getRootPageId();
623
624
        $indexingConfigurationConstraint = '';
625
        if (!empty($indexingConfigurationName)) {
626
            $indexingConfigurationConstraint =
627
                ' AND tx_solr_indexqueue_item.indexing_configuration = \'' .
628
                $indexingConfigurationName . '\'';
629
        }
630
631
        DatabaseUtility::transactionStart();
632
        try {
633
            // reset Index Queue
634
            $result = $GLOBALS['TYPO3_DB']->exec_DELETEquery(
635
                'tx_solr_indexqueue_item',
636
                $rootPageConstraint . $indexingConfigurationConstraint
637
            );
638
            if (!$result) {
639
                throw new \RuntimeException(
640
                    'Failed to reset Index Queue for site ' . $site->getLabel(),
641
                    1412986560
642
                );
643
            }
644
645
            // reset Index Queue Properties
646
            $indexQueuePropertyResetQuery = '
647
				DELETE tx_solr_indexqueue_indexing_property.*
648
				FROM tx_solr_indexqueue_indexing_property
649
				INNER JOIN tx_solr_indexqueue_item
650
					ON tx_solr_indexqueue_item.uid = tx_solr_indexqueue_indexing_property.item_id
651
					AND ' .
652
                $rootPageConstraint .
653
                $indexingConfigurationConstraint;
654
655
            $result = $GLOBALS['TYPO3_DB']->sql_query($indexQueuePropertyResetQuery);
656
            if (!$result) {
657
                throw new \RuntimeException(
658
                    'Failed to reset Index Queue properties for site ' . $site->getLabel(),
659
                    1412986604
660
                );
661
            }
662
663
            DatabaseUtility::transactionCommit();
664
        } catch (\RuntimeException $e) {
665
            DatabaseUtility::transactionRollback();
666
        }
667
    }
668
669
    /**
670
     * Removes all items from the Index Queue.
671
     *
672
     */
673
    public function deleteAllItems()
1 ignored issue
show
Coding Style introduced by
deleteAllItems uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
674
    {
675
        $GLOBALS['TYPO3_DB']->exec_TRUNCATEquery('tx_solr_indexqueue_item', '');
676
    }
677
678
    /**
679
     * Gets a single Index Queue item by its uid.
680
     *
681
     * @param integer $itemId Index Queue item uid
682
     * @return Item The request Index Queue item or NULL
683
     *      if no item with $itemId was found
684
     */
685
    public function getItem($itemId)
1 ignored issue
show
Coding Style introduced by
getItem uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
686
    {
687
        $item = null;
688
689
        $indexQueueItemRecord = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
690
            '*',
691
            'tx_solr_indexqueue_item',
692
            'uid = ' . intval($itemId)
693
        );
694
695
        if (count($indexQueueItemRecord) == 1) {
696
            $indexQueueItemRecord = $indexQueueItemRecord[0];
697
698
            $item = GeneralUtility::makeInstance(
699
                'ApacheSolrForTypo3\\Solr\\IndexQueue\\Item',
700
                $indexQueueItemRecord
701
            );
702
        }
703
704
        return $item;
705
    }
706
707
    /**
708
     * Gets Index Queue items by type and uid.
709
     *
710
     * @param string $itemType item type, ususally  the table name
711
     * @param integer $itemUid item uid
712
     * @return array An array of items matching $itemType and $itemUid
713
     */
714
    public function getItems($itemType, $itemUid)
1 ignored issue
show
Coding Style introduced by
getItems uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
715
    {
716
        $indexQueueItemRecords = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
717
            '*',
718
            'tx_solr_indexqueue_item',
719
            'item_type = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr($itemType,
720
                'tx_solr_indexqueue_item') .
721
            ' AND item_uid = ' . intval($itemUid)
722
        );
723
724
        return $this->getIndexQueueItemObjectsFromRecords($indexQueueItemRecords);
725
    }
726
727
    /**
728
     * Gets number of Index Queue items for a specific site / indexing configuration
729
     * optional parameter to limit the deleted items by indexing configuration.
730
     *
731
     * @param Site $site The site to search for.
732
     * @param string $indexingConfigurationName name of a specific indexing
733
     *      configuration
734
     * @return mixed Number of items (integer) or FALSE if something went
735
     *      wrong (boolean)
736
     */
737
    public function getItemsCountBySite(
1 ignored issue
show
Coding Style introduced by
getItemsCountBySite uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
738
        Site $site,
739
        $indexingConfigurationName = ''
740
    ) {
741
        $indexingConfigurationConstraint = '';
742
        if (!empty($indexingConfigurationName)) {
743
            $indexingConfigurationConstraint = ' AND indexing_configuration = \'' . $indexingConfigurationName . '\'';
744
        }
745
746
        $itemCount = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows(
747
            'uid',
748
            'tx_solr_indexqueue_item',
749
            'root = ' . $site->getRootPageId() . $indexingConfigurationConstraint
750
        );
751
752
        return $itemCount;
753
    }
754
755
    /**
756
     * Returns the number of items for all queues.
757
     *
758
     * @return integer
759
     */
760
    public function getAllItemsCount()
1 ignored issue
show
Coding Style introduced by
getAllItemsCount uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
761
    {
762
        $db = $GLOBALS['TYPO3_DB'];
763
        /**  @var $db \TYPO3\CMS\Core\Database\DatabaseConnection */
764
        $itemCount = $db->exec_SELECTcountRows(
765
            'uid',
766
            'tx_solr_indexqueue_item'
767
        );
768
769
        return (int)$itemCount;
770
    }
771
772
    /**
773
     * Gets $limit number of items to index for a particular $site.
774
     *
775
     * @param Site $site TYPO3 site
776
     * @param integer $limit Number of items to get from the queue
777
     * @return Item[] Items to index to the given solr server
778
     */
779
    public function getItemsToIndex(Site $site, $limit = 50)
1 ignored issue
show
Coding Style introduced by
getItemsToIndex uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
780
    {
781
        $itemsToIndex = array();
782
783
        // determine which items to index with this run
784
        $indexQueueItemRecords = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
785
            '*',
786
            'tx_solr_indexqueue_item',
787
            'root = ' . $site->getRootPageId() .
788
            ' AND changed > indexed' .
789
            ' AND changed <= ' . time() .
790
            ' AND errors = \'\'',
791
            '',
792
            'indexing_priority DESC, changed DESC, uid DESC',
793
            intval($limit)
794
        );
795
        if (!empty($indexQueueItemRecords)) {
796
            // convert queued records to index queue item objects
797
            $itemsToIndex = $this->getIndexQueueItemObjectsFromRecords($indexQueueItemRecords);
798
        }
799
800
        return $itemsToIndex;
801
    }
802
803
    /**
804
     * Creates an array of ApacheSolrForTypo3\Solr\IndexQueue\Item objects from an array of
805
     * index queue records.
806
     *
807
     * @param array $indexQueueItemRecords Array of plain index queue records
808
     * @return array Array of ApacheSolrForTypo3\Solr\IndexQueue\Item objects
809
     */
810
    protected function getIndexQueueItemObjectsFromRecords(
1 ignored issue
show
Coding Style introduced by
getIndexQueueItemObjectsFromRecords uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
811
        array $indexQueueItemRecords
812
    ) {
813
        $indexQueueItems = array();
814
        $tableUids = array();
815
        $tableRecords = array();
816
817
        // grouping records by table
818
        foreach ($indexQueueItemRecords as $indexQueueItemRecord) {
819
            $tableUids[$indexQueueItemRecord['item_type']][] = $indexQueueItemRecord['item_uid'];
820
        }
821
822
        // fetching records by table, saves us a lot of single queries
823
        foreach ($tableUids as $table => $uids) {
824
            $uidList = implode(',', $uids);
825
            $records = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
826
                '*',
827
                $table,
828
                'uid IN(' . $uidList . ')',
829
                '', '', '', // group, order, limit
830
                'uid'
831
            );
832
            $tableRecords[$table] = $records;
833
834
            if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['postProcessFetchRecordsForIndexQueueItem'])) {
835
                $params = ['table' => $table, 'uids' => $uids, 'tableRecords' => &$tableRecords];
836
                foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['postProcessFetchRecordsForIndexQueueItem'] as $reference) {
837
                    GeneralUtility::callUserFunction($reference, $params, $this);
838
                }
839
                unset($params);
840
            }
841
        }
842
843
        // creating index queue item objects and assigning / mapping
844
        // records to index queue items
845
        foreach ($indexQueueItemRecords as $indexQueueItemRecord) {
846
            if (isset($tableRecords[$indexQueueItemRecord['item_type']][$indexQueueItemRecord['item_uid']])) {
847
                $indexQueueItems[] = GeneralUtility::makeInstance(
848
                    'ApacheSolrForTypo3\\Solr\\IndexQueue\\Item',
849
                    $indexQueueItemRecord,
850
                    $tableRecords[$indexQueueItemRecord['item_type']][$indexQueueItemRecord['item_uid']]
851
                );
852
            } else {
853
                GeneralUtility::devLog('Record missing for Index Queue item. Item removed.',
854
                    'solr', 3, array($indexQueueItemRecord));
855
                $this->deleteItem($indexQueueItemRecord['item_type'],
856
                    $indexQueueItemRecord['item_uid']);
857
            }
858
        }
859
860
        return $indexQueueItems;
861
    }
862
863
    /**
864
     * Marks an item as failed and causes the indexer to skip the item in the
865
     * next run.
866
     *
867
     * @param int|Item $item Either the item's Index Queue
868
     *      uid or the complete item
869
     * @param string $errorMessage Error message
870
     */
871
    public function markItemAsFailed($item, $errorMessage = '')
1 ignored issue
show
Coding Style introduced by
markItemAsFailed uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
872
    {
873
        $itemUid = 0;
0 ignored issues
show
Unused Code introduced by
$itemUid is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
874
875
        if ($item instanceof Item) {
876
            $itemUid = $item->getIndexQueueUid();
877
        } else {
878
            $itemUid = (int)$item;
879
        }
880
881
        if (empty($errorMessage)) {
882
            // simply set to "TRUE"
883
            $errorMessage = '1';
884
        }
885
886
        $GLOBALS['TYPO3_DB']->exec_UPDATEquery(
887
            'tx_solr_indexqueue_item',
888
            'uid = ' . $itemUid,
889
            array(
890
                'errors' => $errorMessage
891
            )
892
        );
893
    }
894
}
895