Completed
Push — master ( 3d2504...b6b629 )
by Timo
13:22
created

IndexService::initializeHttpServerEnvironment()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 17
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 17
ccs 11
cts 11
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 10
nc 2
nop 1
crap 2
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 6
    public function __construct(Site $site, Queue $queue = null, Dispatcher $dispatcher = null)
77
    {
78 6
        $this->configuration = $site->getSolrConfiguration();
79 6
        $this->site = $site;
80 6
        $this->indexQueue = is_null($queue) ? GeneralUtility::makeInstance(Queue::class) : $queue;
81 6
        $this->signalSlotDispatcher = is_null($dispatcher)  ? GeneralUtility::makeInstance(Dispatcher::class) : $dispatcher;
82 6
    }
83
84
    /**
85
     * @param \ApacheSolrForTypo3\Solr\Task\IndexQueueWorkerTask $contextTask
86
     */
87 1
    public function setContextTask($contextTask)
88
    {
89 1
        $this->contextTask = $contextTask;
90 1
    }
91
92
    /**
93
     * @return \ApacheSolrForTypo3\Solr\Task\IndexQueueWorkerTask
94
     */
95 6
    public function getContextTask()
96
    {
97 6
        return $this->contextTask;
98
    }
99
100
    /**
101
     * Indexes items from the Index Queue.
102
     *
103
     * @param int $limit
104
     * @return bool
105
     */
106 6
    public function indexItems($limit)
107
    {
108 6
        $errors     = 0;
109 6
        $indexRunId = uniqid();
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);
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(),
144
                    'message' => $e->getMessage(),
145
                    'trace' => $e->getTrace(),
146
                    'item' => (array)$itemToIndex];
147
        GeneralUtility::devLog($message, 'solr', 3, $data);
148
    }
149
150
    /**
151
     * Builds an emits a singal for the IndexService.
152
     *
153
     * @param string $name
154
     * @param array $arguments
155
     * @return mixed
156
     */
157 6
    protected function emitSignal($name, $arguments)
158
    {
159 6
        return $this->signalSlotDispatcher->dispatch(__CLASS__, $name, $arguments);
160
    }
161
162
    /**
163
     * Indexes an item from the Index Queue.
164
     *
165
     * @param Item $item An index queue item to index
166
     * @return bool TRUE if the item was successfully indexed, FALSE otherwise
167
     */
168 5
    protected function indexItem(Item $item)
169
    {
170 5
        $indexer = $this->getIndexerByItem($item->getIndexingConfigurationName());
171
172
        // Remember original http host value
173 5
        $originalHttpHost = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : null;
174
175 5
        $this->initializeHttpServerEnvironment($item);
176 5
        $itemIndexed = $indexer->index($item);
177
178
        // update IQ item so that the IQ can determine what's been indexed already
179 5
        if ($itemIndexed) {
180 5
            $item->updateIndexedTime();
181
        }
182
183 5
        if (!is_null($originalHttpHost)) {
184
            $_SERVER['HTTP_HOST'] = $originalHttpHost;
185
        } else {
186 5
            unset($_SERVER['HTTP_HOST']);
187
        }
188
189
        // needed since TYPO3 7.5
190 5
        GeneralUtility::flushInternalRuntimeCaches();
191
192 5
        return $itemIndexed;
193
    }
194
195
    /**
196
     * A factory method to get an indexer depending on an item's configuration.
197
     *
198
     * By default all items are indexed using the default indexer
199
     * (ApacheSolrForTypo3\Solr\IndexQueue\Indexer) coming with EXT:solr. Pages by default are
200
     * configured to be indexed through a dedicated indexer
201
     * (ApacheSolrForTypo3\Solr\IndexQueue\PageIndexer). In all other cases a dedicated indexer
202
     * can be specified through TypoScript if needed.
203
     *
204
     * @param string $indexingConfigurationName Indexing configuration name.
205
     * @throws \RuntimeException
206
     * @return Indexer An instance of ApacheSolrForTypo3\Solr\IndexQueue\Indexer or a sub class of it.
207
     */
208 5
    protected function getIndexerByItem($indexingConfigurationName)
209
    {
210 5
        $indexerClass = $this->configuration->getIndexQueueIndexerByConfigurationName($indexingConfigurationName);
211 5
        $indexerConfiguration = $this->configuration->getIndexQueueIndexerConfigurationByConfigurationName($indexingConfigurationName);
212
213 5
        $indexer = GeneralUtility::makeInstance($indexerClass, $indexerConfiguration);
214 5
        if (!($indexer instanceof Indexer)) {
215
            throw new \RuntimeException(
216
                'The indexer class "' . $indexerClass . '" for indexing configuration "' . $indexingConfigurationName . '" is not a valid indexer. Must be a subclass of ApacheSolrForTypo3\Solr\IndexQueue\Indexer.',
217
                1260463206
218
            );
219
        }
220
221 5
        return $indexer;
222
    }
223
224
    /**
225
     * Gets the indexing progress.
226
     *
227
     * @return float Indexing progress as a two decimal precision float. f.e. 44.87
228
     */
229 1
    public function getProgress()
230
    {
231 1
        $itemsIndexedPercentage = 0.0;
232
233 1
        $totalItemsCount = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows(
234 1
            'uid',
235 1
            'tx_solr_indexqueue_item',
236 1
            'root = ' . $this->site->getRootPageId()
237
        );
238 1
        $remainingItemsCount = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows(
239 1
            'uid',
240 1
            'tx_solr_indexqueue_item',
241 1
            'changed > indexed AND root = ' . $this->site->getRootPageId()
242
        );
243 1
        $itemsIndexedCount = $totalItemsCount - $remainingItemsCount;
244
245 1
        if ($totalItemsCount > 0) {
246 1
            $itemsIndexedPercentage = $itemsIndexedCount * 100 / $totalItemsCount;
247 1
            $itemsIndexedPercentage = round($itemsIndexedPercentage, 2);
248
        }
249
250 1
        return $itemsIndexedPercentage;
251
    }
252
253
    /**
254
     * Initializes the $_SERVER['HTTP_HOST'] environment variable in CLI
255
     * environments dependent on the Index Queue item's root page.
256
     *
257
     * When the Index Queue Worker task is executed by a cron job there is no
258
     * HTTP_HOST since we are in a CLI environment. RealURL needs the host
259
     * information to generate a proper URL though. Using the Index Queue item's
260
     * root page information we can determine the correct host although being
261
     * in a CLI environment.
262
     *
263
     * @param Item $item Index Queue item to use to determine the host.
264
     * @param
265
     */
266 5
    protected function initializeHttpServerEnvironment(Item $item)
267
    {
268 5
        static $hosts = array();
269 5
        $rootpageId = $item->getRootPageUid();
270 5
        $hostFound = !empty($hosts[$rootpageId]);
271
272 5
        if (!$hostFound) {
273 5
            $rootline = BackendUtility::BEgetRootLine($rootpageId);
274 5
            $host = BackendUtility::firstDomainRecord($rootline);
275 5
            $hosts[$rootpageId] = $host;
276
        }
277
278 5
        $_SERVER['HTTP_HOST'] = $hosts[$rootpageId];
279
280
        // needed since TYPO3 7.5
281 5
        GeneralUtility::flushInternalRuntimeCaches();
282 5
    }
283
}
284