Passed
Push — master ( 6b264d...5682bc )
by Timo
25:05
created

IndexService::getContextTask()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 1
nc 1
nop 0
crap 1
1
<?php
2
3
namespace ApacheSolrForTypo3\Solr\Domain\Index;
4
5
/***************************************************************
6
 *  Copyright notice
7
 *
8
 *  (c) 2015-2016 Timo Hund <[email protected]>
9
 *  All rights reserved
10
 *
11
 *  This script is part of the TYPO3 project. The TYPO3 project is
12
 *  free software; you can redistribute it and/or modify
13
 *  it under the terms of the GNU General Public License as published by
14
 *  the Free Software Foundation; either version 3 of the License, or
15
 *  (at your option) any later version.
16
 *
17
 *  The GNU General Public License can be found at
18
 *  http://www.gnu.org/copyleft/gpl.html.
19
 *
20
 *  This script is distributed in the hope that it will be useful,
21
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23
 *  GNU General Public License for more details.
24
 *
25
 *  This copyright notice MUST APPEAR in all copies of the script!
26
 ***************************************************************/
27
28
use ApacheSolrForTypo3\Solr\ConnectionManager;
29
use ApacheSolrForTypo3\Solr\IndexQueue\Indexer;
30
use ApacheSolrForTypo3\Solr\IndexQueue\Item;
31
use ApacheSolrForTypo3\Solr\IndexQueue\Queue;
32
use ApacheSolrForTypo3\Solr\Site;
33
use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration;
34
use ApacheSolrForTypo3\Solr\System\Logging\SolrLogManager;
35
use ApacheSolrForTypo3\Solr\Task\IndexQueueWorkerTask;
36
use Solarium\Exception\HttpException;
37
use TYPO3\CMS\Backend\Utility\BackendUtility;
38
use TYPO3\CMS\Core\Utility\GeneralUtility;
39
use TYPO3\CMS\Extbase\SignalSlot\Dispatcher;
40
41
/**
42
 * Service to perform indexing operations
43
 *
44
 * @author Timo Hund <[email protected]>
45
 */
46
class IndexService
47
{
48
    /**
49
     * @var TypoScriptConfiguration
50
     */
51
    protected $configuration;
52
53
    /**
54
     * @var Site
55
     */
56
    protected $site;
57
58
    /**
59
     * @var IndexQueueWorkerTask
60
     */
61
    protected $contextTask;
62
63
    /**
64
     * @var Queue
65
     */
66
    protected $indexQueue;
67
68
    /**
69
     * @var Dispatcher
70
     */
71
    protected $signalSlotDispatcher;
72
73
    /**
74
     * @var \ApacheSolrForTypo3\Solr\System\Logging\SolrLogManager
75
     */
76
    protected $logger = null;
77
78
    /**
79
     * IndexService constructor.
80
     * @param Site $site
81
     * @param Queue|null $queue
82
     * @param Dispatcher|null $dispatcher
83
     * @param SolrLogManager|null $solrLogManager
84
     */
85 7
    public function __construct(Site $site, Queue $queue = null, Dispatcher $dispatcher = null, SolrLogManager $solrLogManager = null)
86
    {
87 7
        $this->site = $site;
88 7
        $this->indexQueue = $queue ?? GeneralUtility::makeInstance(Queue::class);
89 7
        $this->signalSlotDispatcher = $dispatcher ?? GeneralUtility::makeInstance(Dispatcher::class);
90 7
        $this->logger = $solrLogManager ?? GeneralUtility::makeInstance(SolrLogManager::class, /** @scrutinizer ignore-type */ __CLASS__);
91 7
        define('EXT_SOLR_INDEXING_CONTEXT', true);
92 7
    }
93
94
    /**
95
     * @param \ApacheSolrForTypo3\Solr\Task\IndexQueueWorkerTask $contextTask
96
     */
97 1
    public function setContextTask($contextTask)
98
    {
99 1
        $this->contextTask = $contextTask;
100 1
    }
101
102
    /**
103
     * @return \ApacheSolrForTypo3\Solr\Task\IndexQueueWorkerTask
104
     */
105 5
    public function getContextTask()
106
    {
107 5
        return $this->contextTask;
108
    }
109
110
    /**
111
     * Indexes items from the Index Queue.
112
     *
113
     * @param int $limit
114
     * @return bool
115
     */
116 5
    public function indexItems($limit)
117
    {
118 5
        $errors     = 0;
119 5
        $indexRunId = uniqid();
120 5
        $configurationToUse = $this->site->getSolrConfiguration();
121 5
        $enableCommitsSetting = $configurationToUse->getEnableCommits();
122
123
        // get items to index
124 5
        $itemsToIndex = $this->indexQueue->getItemsToIndex($this->site, $limit);
125
126 5
        $this->emitSignal('beforeIndexItems', [$itemsToIndex, $this->getContextTask(), $indexRunId]);
127
128 5
        foreach ($itemsToIndex as $itemToIndex) {
129
            try {
130
                // try indexing
131 5
                $this->emitSignal('beforeIndexItem', [$itemToIndex, $this->getContextTask(), $indexRunId]);
132 5
                $this->indexItem($itemToIndex, $configurationToUse);
133 5
                $this->emitSignal('afterIndexItem', [$itemToIndex, $this->getContextTask(), $indexRunId]);
134
            } catch (\Exception $e) {
135
                $errors++;
136
                $this->indexQueue->markItemAsFailed($itemToIndex, $e->getCode() . ': ' . $e->__toString());
137 5
                $this->generateIndexingErrorLog($itemToIndex, $e);
138
            }
139
        }
140
141 5
        $this->emitSignal('afterIndexItems', [$itemsToIndex, $this->getContextTask(), $indexRunId]);
142
143 5
        if ($enableCommitsSetting && count($itemsToIndex) > 0) {
144 4
            $solrServers = GeneralUtility::makeInstance(ConnectionManager::class)->getConnectionsBySite($this->site);
145 4
            foreach ($solrServers as $solrServer) {
146
                try {
147 4
                    $solrServer->getWriteService()->commit(false, false, false);
148
                } catch (HttpException $e) {
149 4
                    $errors++;
150
                }
151
            }
152
        }
153
154 5
        return ($errors === 0);
155
    }
156
157
    /**
158
     * Generates a message in the error log when an error occured.
159
     *
160
     * @param Item $itemToIndex
161
     * @param \Exception  $e
162
     */
163
    protected function generateIndexingErrorLog(Item $itemToIndex, \Exception $e)
164
    {
165
        $message = 'Failed indexing Index Queue item ' . $itemToIndex->getIndexQueueUid();
166
        $data = ['code' => $e->getCode(), 'message' => $e->getMessage(), 'trace' => $e->getTraceAsString(), 'item' => (array)$itemToIndex];
167
168
        $this->logger->log(
169
            SolrLogManager::ERROR,
170
            $message,
171
            $data
172
        );
173
    }
174
175
    /**
176
     * Builds an emits a singal for the IndexService.
177
     *
178
     * @param string $name
179
     * @param array $arguments
180
     * @return mixed
181
     */
182 5
    protected function emitSignal($name, $arguments)
183
    {
184 5
        return $this->signalSlotDispatcher->dispatch(__CLASS__, $name, $arguments);
185
    }
186
187
    /**
188
     * Indexes an item from the Index Queue.
189
     *
190
     * @param Item $item An index queue item to index
191
     * @param TypoScriptConfiguration $configuration
192
     * @return bool TRUE if the item was successfully indexed, FALSE otherwise
193
     */
194 4
    protected function indexItem(Item $item, TypoScriptConfiguration $configuration)
195
    {
196 4
        $indexer = $this->getIndexerByItem($item->getIndexingConfigurationName(), $configuration);
197
198
        // Remember original http host value
199 4
        $originalHttpHost = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : null;
200
201 4
        $itemChangedDate = $item->getChanged();
202 4
        $itemChangedDateAfterIndex = 0;
203
204 4
        $this->initializeHttpServerEnvironment($item);
205 4
        $itemIndexed = $indexer->index($item);
206
207
        // update IQ item so that the IQ can determine what's been indexed already
208 4
        if ($itemIndexed) {
209 4
            $this->indexQueue->updateIndexTimeByItem($item);
210 4
            $itemChangedDateAfterIndex = $item->getChanged();
211
        }
212
213 4
        if ($itemChangedDateAfterIndex > $itemChangedDate && $itemChangedDateAfterIndex > time()) {
214
            $this->indexQueue->setForcedChangeTimeByItem($item, $itemChangedDateAfterIndex);
215
        }
216
217 4
        if (!is_null($originalHttpHost)) {
218
            $_SERVER['HTTP_HOST'] = $originalHttpHost;
219
        } else {
220 4
            unset($_SERVER['HTTP_HOST']);
221
        }
222
223
        // needed since TYPO3 7.5
224 4
        GeneralUtility::flushInternalRuntimeCaches();
225
226 4
        return $itemIndexed;
227
    }
228
229
    /**
230
     * A factory method to get an indexer depending on an item's configuration.
231
     *
232
     * By default all items are indexed using the default indexer
233
     * (ApacheSolrForTypo3\Solr\IndexQueue\Indexer) coming with EXT:solr. Pages by default are
234
     * configured to be indexed through a dedicated indexer
235
     * (ApacheSolrForTypo3\Solr\IndexQueue\PageIndexer). In all other cases a dedicated indexer
236
     * can be specified through TypoScript if needed.
237
     *
238
     * @param string $indexingConfigurationName Indexing configuration name.
239
     * @param TypoScriptConfiguration $configuration
240
     * @return Indexer
241
     */
242 4
    protected function getIndexerByItem($indexingConfigurationName, TypoScriptConfiguration $configuration)
243
    {
244 4
        $indexerClass = $configuration->getIndexQueueIndexerByConfigurationName($indexingConfigurationName);
245 4
        $indexerConfiguration = $configuration->getIndexQueueIndexerConfigurationByConfigurationName($indexingConfigurationName);
246
247 4
        $indexer = GeneralUtility::makeInstance($indexerClass, /** @scrutinizer ignore-type */ $indexerConfiguration);
248 4
        if (!($indexer instanceof Indexer)) {
249
            throw new \RuntimeException(
250
                'The indexer class "' . $indexerClass . '" for indexing configuration "' . $indexingConfigurationName . '" is not a valid indexer. Must be a subclass of ApacheSolrForTypo3\Solr\IndexQueue\Indexer.',
251
                1260463206
252
            );
253
        }
254
255 4
        return $indexer;
256
    }
257
258
    /**
259
     * Gets the indexing progress.
260
     *
261
     * @return float Indexing progress as a two decimal precision float. f.e. 44.87
262
     */
263 1
    public function getProgress()
264
    {
265 1
        return $this->indexQueue->getStatisticsBySite($this->site)->getSuccessPercentage();
266
    }
267
268
    /**
269
     * Returns the amount of failed queue items for the current site.
270
     *
271
     * @return int
272
     */
273 1
    public function getFailCount()
274
    {
275 1
        return $this->indexQueue->getStatisticsBySite($this->site)->getFailedCount();
276
    }
277
278
    /**
279
     * Initializes the $_SERVER['HTTP_HOST'] environment variable in CLI
280
     * environments dependent on the Index Queue item's root page.
281
     *
282
     * When the Index Queue Worker task is executed by a cron job there is no
283
     * HTTP_HOST since we are in a CLI environment. RealURL needs the host
284
     * information to generate a proper URL though. Using the Index Queue item's
285
     * root page information we can determine the correct host although being
286
     * in a CLI environment.
287
     *
288
     * @param Item $item Index Queue item to use to determine the host.
289
     * @param
290
     */
291 4
    protected function initializeHttpServerEnvironment(Item $item)
292
    {
293 4
        static $hosts = [];
294 4
        $rootpageId = $item->getRootPageUid();
295 4
        $hostFound = !empty($hosts[$rootpageId]);
296
297 4
        if (!$hostFound) {
298 4
            $rootline = BackendUtility::BEgetRootLine($rootpageId);
299 4
            $host = BackendUtility::firstDomainRecord($rootline);
0 ignored issues
show
Deprecated Code introduced by
The function TYPO3\CMS\Backend\Utilit...ty::firstDomainRecord() has been deprecated: since TYPO3 v9.4, will be removed in TYPO3 v10.0. Use Link Generation / Router instead. ( Ignorable by Annotation )

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

299
            $host = /** @scrutinizer ignore-deprecated */ BackendUtility::firstDomainRecord($rootline);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
300 4
            $hosts[$rootpageId] = $host;
301
        }
302
303 4
        $_SERVER['HTTP_HOST'] = $hosts[$rootpageId];
304
305
        // needed since TYPO3 7.5
306 4
        GeneralUtility::flushInternalRuntimeCaches();
307 4
    }
308
}
309