|
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\Domain\Site\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 |
|
try { |
|
205
|
4 |
|
$this->initializeHttpServerEnvironment($item); |
|
206
|
|
|
$itemIndexed = $indexer->index($item); |
|
207
|
|
|
|
|
208
|
4 |
|
// update IQ item so that the IQ can determine what's been indexed already |
|
209
|
4 |
|
if ($itemIndexed) { |
|
210
|
4 |
|
$this->indexQueue->updateIndexTimeByItem($item); |
|
211
|
|
|
$itemChangedDateAfterIndex = $item->getChanged(); |
|
212
|
|
|
} |
|
213
|
4 |
|
|
|
214
|
|
|
if ($itemChangedDateAfterIndex > $itemChangedDate && $itemChangedDateAfterIndex > time()) { |
|
215
|
|
|
$this->indexQueue->setForcedChangeTimeByItem($item, $itemChangedDateAfterIndex); |
|
216
|
|
|
} |
|
217
|
4 |
|
} catch (\Exception $e) { |
|
218
|
|
|
$this->restoreOriginalHttpHost($originalHttpHost); |
|
219
|
|
|
throw $e; |
|
220
|
4 |
|
} |
|
221
|
|
|
|
|
222
|
|
|
$this->restoreOriginalHttpHost($originalHttpHost); |
|
223
|
|
|
|
|
224
|
4 |
|
return $itemIndexed; |
|
225
|
|
|
} |
|
226
|
4 |
|
|
|
227
|
|
|
/** |
|
228
|
|
|
* A factory method to get an indexer depending on an item's configuration. |
|
229
|
|
|
* |
|
230
|
|
|
* By default all items are indexed using the default indexer |
|
231
|
|
|
* (ApacheSolrForTypo3\Solr\IndexQueue\Indexer) coming with EXT:solr. Pages by default are |
|
232
|
|
|
* configured to be indexed through a dedicated indexer |
|
233
|
|
|
* (ApacheSolrForTypo3\Solr\IndexQueue\PageIndexer). In all other cases a dedicated indexer |
|
234
|
|
|
* can be specified through TypoScript if needed. |
|
235
|
|
|
* |
|
236
|
|
|
* @param string $indexingConfigurationName Indexing configuration name. |
|
237
|
|
|
* @param TypoScriptConfiguration $configuration |
|
238
|
|
|
* @return Indexer |
|
239
|
|
|
*/ |
|
240
|
|
|
protected function getIndexerByItem($indexingConfigurationName, TypoScriptConfiguration $configuration) |
|
241
|
|
|
{ |
|
242
|
4 |
|
$indexerClass = $configuration->getIndexQueueIndexerByConfigurationName($indexingConfigurationName); |
|
243
|
|
|
$indexerConfiguration = $configuration->getIndexQueueIndexerConfigurationByConfigurationName($indexingConfigurationName); |
|
244
|
4 |
|
|
|
245
|
4 |
|
$indexer = GeneralUtility::makeInstance($indexerClass, /** @scrutinizer ignore-type */ $indexerConfiguration); |
|
246
|
|
|
if (!($indexer instanceof Indexer)) { |
|
247
|
4 |
|
throw new \RuntimeException( |
|
248
|
4 |
|
'The indexer class "' . $indexerClass . '" for indexing configuration "' . $indexingConfigurationName . '" is not a valid indexer. Must be a subclass of ApacheSolrForTypo3\Solr\IndexQueue\Indexer.', |
|
249
|
|
|
1260463206 |
|
250
|
|
|
); |
|
251
|
|
|
} |
|
252
|
|
|
|
|
253
|
|
|
return $indexer; |
|
254
|
|
|
} |
|
255
|
4 |
|
|
|
256
|
|
|
/** |
|
257
|
|
|
* Gets the indexing progress. |
|
258
|
|
|
* |
|
259
|
|
|
* @return float Indexing progress as a two decimal precision float. f.e. 44.87 |
|
260
|
|
|
*/ |
|
261
|
|
|
public function getProgress() |
|
262
|
|
|
{ |
|
263
|
1 |
|
return $this->indexQueue->getStatisticsBySite($this->site)->getSuccessPercentage(); |
|
264
|
|
|
} |
|
265
|
1 |
|
|
|
266
|
|
|
/** |
|
267
|
|
|
* Returns the amount of failed queue items for the current site. |
|
268
|
|
|
* |
|
269
|
|
|
* @return int |
|
270
|
|
|
*/ |
|
271
|
|
|
public function getFailCount() |
|
272
|
|
|
{ |
|
273
|
1 |
|
return $this->indexQueue->getStatisticsBySite($this->site)->getFailedCount(); |
|
274
|
|
|
} |
|
275
|
1 |
|
|
|
276
|
|
|
/** |
|
277
|
|
|
* Initializes the $_SERVER['HTTP_HOST'] environment variable in CLI |
|
278
|
|
|
* environments dependent on the Index Queue item's root page. |
|
279
|
|
|
* |
|
280
|
|
|
* When the Index Queue Worker task is executed by a cron job there is no |
|
281
|
|
|
* HTTP_HOST since we are in a CLI environment. RealURL needs the host |
|
282
|
|
|
* information to generate a proper URL though. Using the Index Queue item's |
|
283
|
|
|
* root page information we can determine the correct host although being |
|
284
|
|
|
* in a CLI environment. |
|
285
|
|
|
* |
|
286
|
|
|
* @param Item $item Index Queue item to use to determine the host. |
|
287
|
|
|
* @param |
|
288
|
|
|
*/ |
|
289
|
|
|
protected function initializeHttpServerEnvironment(Item $item) |
|
290
|
|
|
{ |
|
291
|
4 |
|
static $hosts = []; |
|
292
|
|
|
$rootpageId = $item->getRootPageUid(); |
|
293
|
4 |
|
$hostFound = !empty($hosts[$rootpageId]); |
|
294
|
4 |
|
|
|
295
|
4 |
|
if (!$hostFound) { |
|
296
|
|
|
$host = $this->getHostByRootPageId($rootpageId); |
|
297
|
4 |
|
$hosts[$rootpageId] = $host; |
|
298
|
4 |
|
} |
|
299
|
4 |
|
|
|
300
|
4 |
|
$_SERVER['HTTP_HOST'] = $hosts[$rootpageId]; |
|
301
|
|
|
|
|
302
|
|
|
// needed since TYPO3 7.5 |
|
303
|
4 |
|
GeneralUtility::flushInternalRuntimeCaches(); |
|
304
|
|
|
} |
|
305
|
|
|
|
|
306
|
4 |
|
/** |
|
307
|
4 |
|
* @param string|null $originalHttpHost |
|
308
|
|
|
*/ |
|
309
|
|
|
protected function restoreOriginalHttpHost($originalHttpHost) |
|
310
|
|
|
{ |
|
311
|
|
|
if (!is_null($originalHttpHost)) { |
|
312
|
|
|
$_SERVER['HTTP_HOST'] = $originalHttpHost; |
|
313
|
|
|
} else { |
|
314
|
|
|
unset($_SERVER['HTTP_HOST']); |
|
315
|
|
|
} |
|
316
|
|
|
|
|
317
|
|
|
// needed since TYPO3 7.5 |
|
318
|
|
|
GeneralUtility::flushInternalRuntimeCaches(); |
|
319
|
|
|
} |
|
320
|
|
|
|
|
321
|
|
|
/** |
|
322
|
|
|
* @param $rootpageId |
|
323
|
|
|
* @return string |
|
324
|
|
|
*/ |
|
325
|
|
|
protected function getHostByRootPageId($rootpageId) |
|
326
|
|
|
{ |
|
327
|
|
|
$rootline = BackendUtility::BEgetRootLine($rootpageId); |
|
328
|
|
|
$host = BackendUtility::firstDomainRecord($rootline); |
|
|
|
|
|
|
329
|
|
|
return $host; |
|
330
|
|
|
} |
|
331
|
|
|
} |
|
332
|
|
|
|
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.