Completed
Push — master ( c7f02f...d53d83 )
by Timo
14:49
created

IndexService::indexItem()   B

Complexity

Conditions 4
Paths 8

Size

Total Lines 26
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 4.0092

Importance

Changes 0
Metric Value
dl 0
loc 26
ccs 11
cts 12
cp 0.9167
rs 8.5806
c 0
b 0
f 0
cc 4
eloc 13
nc 8
nop 2
crap 4.0092
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 2 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\IndexQueue\Indexer;
29
use ApacheSolrForTypo3\Solr\IndexQueue\Item;
30
use ApacheSolrForTypo3\Solr\IndexQueue\Queue;
31
use ApacheSolrForTypo3\Solr\Site;
32
use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration;
33
use ApacheSolrForTypo3\Solr\Task\IndexQueueWorkerTask;
34
use TYPO3\CMS\Backend\Utility\BackendUtility;
35
use TYPO3\CMS\Core\Utility\GeneralUtility;
36
use TYPO3\CMS\Extbase\SignalSlot\Dispatcher;
37
38
/**
39
 * Service to perform indexing operations
40
 *
41
 * @author Timo Hund <[email protected]>
42
 */
43
class IndexService
44
{
45
    /**
46
     * @var TypoScriptConfiguration
47
     */
48
    protected $configuration;
49
50
    /**
51
     * @var Site
52
     */
53
    protected $site;
54
55
    /**
56
     * @var IndexQueueWorkerTask
57
     */
58
    protected $contextTask;
59
60
    /**
61
     * @var Queue
62
     */
63
    protected $indexQueue;
64
65
    /**
66
     * @var Dispatcher
67
     */
68
    protected $signalSlotDispatcher;
69
70
    /**
71
     * IndexService constructor.
72
     * @param Site $site
73
     * @param Queue|null $queue
74
     * @param Dispatcher|null $dispatcher
75
     */
76 7
    public function __construct(Site $site, Queue $queue = null, Dispatcher $dispatcher = null)
77
    {
78 7
        $this->site = $site;
79 7
        $this->indexQueue = is_null($queue) ? GeneralUtility::makeInstance(Queue::class) : $queue;
80 7
        $this->signalSlotDispatcher = is_null($dispatcher) ? GeneralUtility::makeInstance(Dispatcher::class) : $dispatcher;
81 7
    }
82
83
    /**
84
     * @param \ApacheSolrForTypo3\Solr\Task\IndexQueueWorkerTask $contextTask
85
     */
86 1
    public function setContextTask($contextTask)
87
    {
88 1
        $this->contextTask = $contextTask;
89 1
    }
90
91
    /**
92
     * @return \ApacheSolrForTypo3\Solr\Task\IndexQueueWorkerTask
93
     */
94 6
    public function getContextTask()
95
    {
96 6
        return $this->contextTask;
97
    }
98
99
    /**
100
     * Indexes items from the Index Queue.
101
     *
102
     * @param int $limit
103
     * @return bool
104
     */
105 6
    public function indexItems($limit)
106
    {
107 6
        $errors     = 0;
108 6
        $indexRunId = uniqid();
109 6
        $configurationToUse = $this->site->getSolrConfiguration();
110
111
        // get items to index
112 6
        $itemsToIndex = $this->indexQueue->getItemsToIndex($this->site, $limit);
113
114 6
        $this->emitSignal('beforeIndexItems', [$itemsToIndex, $this->getContextTask(), $indexRunId]);
115
116 6
        foreach ($itemsToIndex as $itemToIndex) {
117
            try {
118
                // try indexing
119 6
                $this->emitSignal('beforeIndexItem', [$itemToIndex, $this->getContextTask(), $indexRunId]);
120 6
                $this->indexItem($itemToIndex, $configurationToUse);
121 6
                $this->emitSignal('afterIndexItem', [$itemToIndex, $this->getContextTask(), $indexRunId]);
122
            } catch (\Exception $e) {
123
                $errors++;
124
                $this->indexQueue->markItemAsFailed($itemToIndex, $e->getCode() . ': ' . $e->__toString());
125 6
                $this->generateIndexingErrorLog($itemToIndex, $e);
126
            }
127
        }
128
129 6
        $this->emitSignal('afterIndexItems', [$itemsToIndex, $this->getContextTask(), $indexRunId]);
130
131 6
        return ($errors === 0);
132
    }
133
134
    /**
135
     * Generates a message in the error log when an error occured.
136
     *
137
     * @param Item $itemToIndex
138
     * @param \Exception  $e
139
     */
140
    protected function generateIndexingErrorLog(Item $itemToIndex, \Exception $e)
141
    {
142
        $message = 'Failed indexing Index Queue item ' . $itemToIndex->getIndexQueueUid();
143
        $data = ['code' => $e->getCode(), 'message' => $e->getMessage(), 'trace' => $e->getTrace(), 'item' => (array)$itemToIndex];
144
        GeneralUtility::devLog($message, 'solr', 3, $data);
145
    }
146
147
    /**
148
     * Builds an emits a singal for the IndexService.
149
     *
150
     * @param string $name
151
     * @param array $arguments
152
     * @return mixed
153
     */
154 6
    protected function emitSignal($name, $arguments)
155
    {
156 6
        return $this->signalSlotDispatcher->dispatch(__CLASS__, $name, $arguments);
157
    }
158
159
    /**
160
     * Indexes an item from the Index Queue.
161
     *
162
     * @param Item $item An index queue item to index
163
     * @param TypoScriptConfiguration $configuration
164
     * @return bool TRUE if the item was successfully indexed, FALSE otherwise
165
     */
166 5
    protected function indexItem(Item $item, TypoScriptConfiguration $configuration)
167
    {
168 5
        $indexer = $this->getIndexerByItem($item->getIndexingConfigurationName(), $configuration);
169
170
        // Remember original http host value
171 5
        $originalHttpHost = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : null;
172
173 5
        $this->initializeHttpServerEnvironment($item);
174 5
        $itemIndexed = $indexer->index($item);
175
176
        // update IQ item so that the IQ can determine what's been indexed already
177 5
        if ($itemIndexed) {
178 5
            $item->updateIndexedTime();
179
        }
180
181 5
        if (!is_null($originalHttpHost)) {
182
            $_SERVER['HTTP_HOST'] = $originalHttpHost;
183
        } else {
184 5
            unset($_SERVER['HTTP_HOST']);
185
        }
186
187
        // needed since TYPO3 7.5
188 5
        GeneralUtility::flushInternalRuntimeCaches();
189
190 5
        return $itemIndexed;
191
    }
192
193
    /**
194
     * A factory method to get an indexer depending on an item's configuration.
195
     *
196
     * By default all items are indexed using the default indexer
197
     * (ApacheSolrForTypo3\Solr\IndexQueue\Indexer) coming with EXT:solr. Pages by default are
198
     * configured to be indexed through a dedicated indexer
199
     * (ApacheSolrForTypo3\Solr\IndexQueue\PageIndexer). In all other cases a dedicated indexer
200
     * can be specified through TypoScript if needed.
201
     *
202
     * @param string $indexingConfigurationName Indexing configuration name.
203
     * @param TypoScriptConfiguration $configuration
204
     * @return Indexer
205
     */
206 5
    protected function getIndexerByItem($indexingConfigurationName, TypoScriptConfiguration $configuration)
207
    {
208 5
        $indexerClass = $configuration->getIndexQueueIndexerByConfigurationName($indexingConfigurationName);
209 5
        $indexerConfiguration = $configuration->getIndexQueueIndexerConfigurationByConfigurationName($indexingConfigurationName);
210
211 5
        $indexer = GeneralUtility::makeInstance($indexerClass, $indexerConfiguration);
212 5
        if (!($indexer instanceof Indexer)) {
213
            throw new \RuntimeException(
214
                'The indexer class "' . $indexerClass . '" for indexing configuration "' . $indexingConfigurationName . '" is not a valid indexer. Must be a subclass of ApacheSolrForTypo3\Solr\IndexQueue\Indexer.',
215
                1260463206
216
            );
217
        }
218
219 5
        return $indexer;
220
    }
221
222
    /**
223
     * Gets the indexing progress.
224
     *
225
     * @return float Indexing progress as a two decimal precision float. f.e. 44.87
226
     */
227 2
    public function getProgress()
228
    {
229 2
        $itemsIndexedPercentage = 0.0;
230
231 2
        $totalItemsCount = $this->indexQueue->getItemsCountBySite($this->site);
232 2
        $remainingItemsCount = $this->indexQueue->getRemainingItemsCountBySite($this->site);
233 2
        $itemsIndexedCount = $totalItemsCount - $remainingItemsCount;
234
235 2
        if ($totalItemsCount > 0) {
236 2
            $itemsIndexedPercentage = $itemsIndexedCount * 100 / $totalItemsCount;
237 2
            $itemsIndexedPercentage = round($itemsIndexedPercentage, 2);
238
        }
239
240 2
        return $itemsIndexedPercentage;
241
    }
242
243
    /**
244
     * Initializes the $_SERVER['HTTP_HOST'] environment variable in CLI
245
     * environments dependent on the Index Queue item's root page.
246
     *
247
     * When the Index Queue Worker task is executed by a cron job there is no
248
     * HTTP_HOST since we are in a CLI environment. RealURL needs the host
249
     * information to generate a proper URL though. Using the Index Queue item's
250
     * root page information we can determine the correct host although being
251
     * in a CLI environment.
252
     *
253
     * @param Item $item Index Queue item to use to determine the host.
254
     * @param
255
     */
256 5
    protected function initializeHttpServerEnvironment(Item $item)
257
    {
258 5
        static $hosts = [];
259 5
        $rootpageId = $item->getRootPageUid();
260 5
        $hostFound = !empty($hosts[$rootpageId]);
261
262 5
        if (!$hostFound) {
263 5
            $rootline = BackendUtility::BEgetRootLine($rootpageId);
264 5
            $host = BackendUtility::firstDomainRecord($rootline);
265 5
            $hosts[$rootpageId] = $host;
266
        }
267
268 5
        $_SERVER['HTTP_HOST'] = $hosts[$rootpageId];
269
270
        // needed since TYPO3 7.5
271 5
        GeneralUtility::flushInternalRuntimeCaches();
272 5
    }
273
}
274