1
|
|
|
<?php |
2
|
|
|
namespace ApacheSolrForTypo3\Solr; |
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\Domain\Search\Query\Helper\EscapeService; |
28
|
|
|
use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\Faceting; |
29
|
|
|
use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\Filters; |
30
|
|
|
use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\Grouping; |
31
|
|
|
use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\Highlighting; |
32
|
|
|
use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\QueryFields; |
33
|
|
|
use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\ReturnFields; |
34
|
|
|
use ApacheSolrForTypo3\Solr\Domain\Site\SiteHashService; |
35
|
|
|
use ApacheSolrForTypo3\Solr\FieldProcessor\PageUidToHierarchy; |
36
|
|
|
use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration; |
37
|
|
|
use ApacheSolrForTypo3\Solr\System\Logging\SolrLogManager; |
38
|
|
|
use TYPO3\CMS\Core\Utility\GeneralUtility; |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* A Solr search query |
42
|
|
|
* |
43
|
|
|
* @author Ingo Renner <[email protected]> |
44
|
|
|
* @author Timo Hund <[email protected]> |
45
|
|
|
*/ |
46
|
|
|
class Query |
47
|
|
|
{ |
48
|
|
|
|
49
|
|
|
// FIXME extract link building from the query, it's not the query's domain |
50
|
|
|
|
51
|
|
|
const SORT_ASC = 'ASC'; |
52
|
|
|
const SORT_DESC = 'DESC'; |
53
|
|
|
|
54
|
|
|
const OPERATOR_AND = 'AND'; |
55
|
|
|
const OPERATOR_OR = 'OR'; |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* Used to identify the queries. |
59
|
|
|
* |
60
|
|
|
* @var int |
61
|
|
|
*/ |
62
|
|
|
protected static $idCount = 0; |
63
|
|
|
|
64
|
|
|
/** |
65
|
|
|
* @var int |
66
|
|
|
*/ |
67
|
|
|
protected $id; |
68
|
|
|
|
69
|
|
|
/** |
70
|
|
|
* @var TypoScriptConfiguration |
71
|
|
|
*/ |
72
|
|
|
protected $solrConfiguration; |
73
|
|
|
|
74
|
|
|
/** |
75
|
|
|
* @var string |
76
|
|
|
*/ |
77
|
|
|
protected $keywords; |
78
|
|
|
|
79
|
|
|
/** |
80
|
|
|
* @var string |
81
|
|
|
*/ |
82
|
|
|
protected $keywordsRaw; |
83
|
|
|
|
84
|
|
|
/** |
85
|
|
|
* ParameterBuilder for filters. |
86
|
|
|
* |
87
|
|
|
* @var Filters |
88
|
|
|
*/ |
89
|
|
|
protected $filters = null; |
90
|
|
|
|
91
|
|
|
/** |
92
|
|
|
* @var string |
93
|
|
|
*/ |
94
|
|
|
protected $sorting; |
95
|
|
|
|
96
|
|
|
// TODO check usage of these two variants, especially the check for $rawQueryString in getQueryString() |
97
|
|
|
/** |
98
|
|
|
* @var |
99
|
|
|
*/ |
100
|
|
|
protected $queryString; |
101
|
|
|
|
102
|
|
|
/** |
103
|
|
|
* @var array |
104
|
|
|
*/ |
105
|
|
|
protected $queryParameters = []; |
106
|
|
|
|
107
|
|
|
/** |
108
|
|
|
* @var int |
109
|
|
|
*/ |
110
|
|
|
protected $resultsPerPage; |
111
|
|
|
|
112
|
|
|
/** |
113
|
|
|
* @var int |
114
|
|
|
*/ |
115
|
|
|
protected $page; |
116
|
|
|
|
117
|
|
|
/** |
118
|
|
|
* @var int |
119
|
|
|
*/ |
120
|
|
|
protected $linkTargetPageId; |
121
|
|
|
|
122
|
|
|
/** |
123
|
|
|
* Holds the query fields with their associated boosts. The key represents |
124
|
|
|
* the field name, value represents the field's boost. These are the fields |
125
|
|
|
* that will actually be searched. |
126
|
|
|
* |
127
|
|
|
* Used in Solr's qf parameter |
128
|
|
|
* |
129
|
|
|
* @var QueryFields |
130
|
|
|
* @see http://wiki.apache.org/solr/DisMaxQParserPlugin#qf_.28Query_Fields.29 |
131
|
|
|
*/ |
132
|
|
|
protected $queryFields = null; |
133
|
|
|
|
134
|
|
|
/** |
135
|
|
|
* List of fields that will be returned in the result documents. |
136
|
|
|
* |
137
|
|
|
* used in Solr's fl parameter |
138
|
|
|
* |
139
|
|
|
* @var ReturnFields |
140
|
|
|
* @see http://wiki.apache.org/solr/CommonQueryParameters#fl |
141
|
|
|
*/ |
142
|
|
|
protected $returnFields = null; |
143
|
|
|
|
144
|
|
|
/** |
145
|
|
|
* ParameterBuilder for the highlighting. |
146
|
|
|
* |
147
|
|
|
* @var Highlighting |
148
|
|
|
*/ |
149
|
|
|
protected $highlighting = null; |
150
|
|
|
|
151
|
|
|
/** |
152
|
|
|
* ParameterBuilder for the faceting. |
153
|
|
|
* |
154
|
|
|
* @var Faceting |
155
|
|
|
*/ |
156
|
|
|
protected $faceting = null; |
157
|
|
|
|
158
|
|
|
/** |
159
|
|
|
* ParameterBuilder for the grouping. |
160
|
|
|
* |
161
|
|
|
* @var Grouping |
162
|
|
|
*/ |
163
|
|
|
protected $grouping = null; |
164
|
|
|
|
165
|
|
|
/** |
166
|
|
|
* @var bool |
167
|
|
|
*/ |
168
|
|
|
private $rawQueryString = false; |
169
|
|
|
|
170
|
|
|
/** |
171
|
|
|
* The field by which the result will be collapsed |
172
|
|
|
* @var string |
173
|
|
|
*/ |
174
|
|
|
protected $variantField = 'variantId'; |
175
|
|
|
|
176
|
|
|
/** |
177
|
|
|
* @var SiteHashService |
178
|
|
|
*/ |
179
|
|
|
protected $siteHashService = null; |
180
|
|
|
|
181
|
|
|
/** |
182
|
|
|
* @var \ApacheSolrForTypo3\Solr\System\Logging\SolrLogManager |
183
|
|
|
*/ |
184
|
|
|
protected $logger = null; |
185
|
|
|
|
186
|
|
|
/** |
187
|
|
|
* @var EscapeService |
188
|
|
|
*/ |
189
|
|
|
protected $escapeService = null; |
190
|
|
|
|
191
|
|
|
/** |
192
|
|
|
* Query constructor. |
193
|
|
|
* @param string $keywords |
194
|
|
|
* @param TypoScriptConfiguration $solrConfiguration |
195
|
|
|
* @param SiteHashService|null $siteHashService |
196
|
|
|
* @param EscapeService|null $escapeService |
197
|
|
|
* @param SolrLogManager|null $solrLogManager |
198
|
|
|
*/ |
199
|
121 |
|
public function __construct($keywords, $solrConfiguration = null, SiteHashService $siteHashService = null, EscapeService $escapeService = null, SolrLogManager $solrLogManager = null) |
200
|
|
|
{ |
201
|
121 |
|
$keywords = (string)$keywords; |
202
|
|
|
|
203
|
121 |
|
$this->logger = is_null($solrLogManager) ? GeneralUtility::makeInstance(SolrLogManager::class, __CLASS__) : $solrLogManager; |
204
|
121 |
|
$this->solrConfiguration = is_null($solrConfiguration) ? Util::getSolrConfiguration() : $solrConfiguration; |
205
|
121 |
|
$this->siteHashService = is_null($siteHashService) ? GeneralUtility::makeInstance(SiteHashService::class) : $siteHashService; |
206
|
121 |
|
$this->escapeService = is_null($escapeService) ? GeneralUtility::makeInstance(EscapeService::class) : $escapeService; |
207
|
121 |
|
$this->setKeywords($keywords); |
208
|
121 |
|
$this->sorting = ''; |
209
|
|
|
|
210
|
121 |
|
$this->linkTargetPageId = $this->solrConfiguration->getSearchTargetPage(); |
211
|
|
|
|
212
|
121 |
|
$this->initializeQuery(); |
213
|
|
|
|
214
|
121 |
|
$this->id = ++self::$idCount; |
215
|
121 |
|
} |
216
|
|
|
|
217
|
|
|
/** |
218
|
|
|
* @return void |
219
|
|
|
*/ |
220
|
120 |
|
protected function initializeQuery() |
221
|
|
|
{ |
222
|
|
|
// Filters |
223
|
120 |
|
$this->initializeFilters(); |
224
|
|
|
|
225
|
|
|
// What fields to search |
226
|
120 |
|
$queryFields = QueryFields::fromString($this->solrConfiguration->getSearchQueryQueryFields()); |
227
|
120 |
|
$this->setQueryFields($queryFields); |
228
|
|
|
|
229
|
|
|
// What fields to return from Solr |
230
|
120 |
|
$returnFieldsArray = $this->solrConfiguration->getSearchQueryReturnFieldsAsArray(['*', 'score']); |
231
|
120 |
|
$returnFields = ReturnFields::fromArray($returnFieldsArray); |
232
|
120 |
|
$this->setReturnFields($returnFields); |
233
|
|
|
|
234
|
|
|
// Configure highlighting |
235
|
120 |
|
$highlighting = Highlighting::fromTypoScriptConfiguration($this->solrConfiguration); |
236
|
120 |
|
$this->setHighlighting($highlighting); |
237
|
|
|
|
238
|
|
|
// Configure faceting |
239
|
120 |
|
$this->initializeFaceting(); |
240
|
|
|
|
241
|
|
|
// Initialize grouping |
242
|
120 |
|
$this->initializeGrouping(); |
243
|
|
|
|
244
|
|
|
// Configure collapsing |
245
|
120 |
|
$this->initializeCollapsingFromConfiguration(); |
246
|
120 |
|
} |
247
|
|
|
|
248
|
|
|
/** |
249
|
|
|
* @param QueryFields $queryFields |
250
|
|
|
*/ |
251
|
120 |
|
public function setQueryFields(QueryFields $queryFields) |
252
|
|
|
{ |
253
|
120 |
|
$this->queryFields = $queryFields; |
254
|
120 |
|
} |
255
|
|
|
|
256
|
|
|
/** |
257
|
|
|
* @return QueryFields |
258
|
|
|
*/ |
259
|
83 |
|
public function getQueryFields() |
260
|
|
|
{ |
261
|
83 |
|
return $this->queryFields; |
262
|
|
|
} |
263
|
|
|
|
264
|
|
|
/** |
265
|
|
|
* magic implementation for clone(), makes sure that the id counter is |
266
|
|
|
* incremented |
267
|
|
|
* |
268
|
|
|
* @return void |
269
|
|
|
*/ |
270
|
|
|
public function __clone() |
271
|
|
|
{ |
272
|
|
|
$this->id = ++self::$idCount; |
273
|
|
|
} |
274
|
|
|
|
275
|
|
|
/** |
276
|
|
|
* returns a string representation of the query |
277
|
|
|
* |
278
|
|
|
* @return string the string representation of the query |
279
|
|
|
*/ |
280
|
1 |
|
public function __toString() |
281
|
|
|
{ |
282
|
1 |
|
return $this->getQueryString(); |
283
|
|
|
} |
284
|
|
|
|
285
|
|
|
/** |
286
|
|
|
* Builds the query string which is then used for Solr's q parameters |
287
|
|
|
* |
288
|
|
|
* @return string Solr query string |
289
|
|
|
*/ |
290
|
41 |
|
public function getQueryString() |
291
|
|
|
{ |
292
|
41 |
|
if (!$this->rawQueryString) { |
293
|
38 |
|
$this->buildQueryString(); |
294
|
|
|
} |
295
|
|
|
|
296
|
41 |
|
return $this->queryString; |
297
|
|
|
} |
298
|
|
|
|
299
|
|
|
/** |
300
|
|
|
* Sets the query string without any escaping. |
301
|
|
|
* |
302
|
|
|
* Be cautious with this function! |
303
|
|
|
* TODO remove this method as it basically just sets the q parameter / keywords |
304
|
|
|
* |
305
|
|
|
* @param string $queryString The raw query string. |
306
|
|
|
*/ |
307
|
4 |
|
public function setQueryString($queryString) |
308
|
|
|
{ |
309
|
4 |
|
$this->queryString = $queryString; |
310
|
4 |
|
} |
311
|
|
|
|
312
|
|
|
/** |
313
|
|
|
* Creates the string that is later used as the q parameter in the solr query |
314
|
|
|
* |
315
|
|
|
* @return void |
316
|
|
|
*/ |
317
|
38 |
|
protected function buildQueryString() |
318
|
|
|
{ |
319
|
|
|
// very simple for now |
320
|
38 |
|
$this->queryString = $this->keywords; |
321
|
38 |
|
} |
322
|
|
|
|
323
|
|
|
/** |
324
|
|
|
* Sets whether a raw query sting should be used, that is, whether the query |
325
|
|
|
* string should be escaped or not. |
326
|
|
|
* |
327
|
|
|
* @param bool $useRawQueryString TRUE to use raw queries (like Lucene Query Language) or FALSE for regular, escaped queries |
328
|
|
|
*/ |
329
|
4 |
|
public function useRawQueryString($useRawQueryString) |
330
|
|
|
{ |
331
|
4 |
|
$this->rawQueryString = (boolean)$useRawQueryString; |
332
|
4 |
|
} |
333
|
|
|
|
334
|
|
|
/** |
335
|
|
|
* Returns the query's ID. |
336
|
|
|
* |
337
|
|
|
* @return int The query's ID. |
338
|
|
|
*/ |
339
|
|
|
public function getId() |
340
|
|
|
{ |
341
|
|
|
return $this->id; |
342
|
|
|
} |
343
|
|
|
|
344
|
|
|
/** |
345
|
|
|
* Gets the currently showing page's number |
346
|
|
|
* |
347
|
|
|
* @return int page number currently showing |
348
|
|
|
*/ |
349
|
1 |
|
public function getPage() |
350
|
|
|
{ |
351
|
1 |
|
return $this->page; |
352
|
|
|
} |
353
|
|
|
|
354
|
|
|
/** |
355
|
|
|
* Sets the page that should be shown |
356
|
|
|
* |
357
|
|
|
* @param int $page page number to show |
358
|
|
|
* @return void |
359
|
|
|
*/ |
360
|
2 |
|
public function setPage($page) |
361
|
|
|
{ |
362
|
2 |
|
$this->page = max(intval($page), 0); |
363
|
2 |
|
} |
364
|
|
|
|
365
|
|
|
/** |
366
|
|
|
* Gets the index of the first result document we're showing |
367
|
|
|
* |
368
|
|
|
* @return int index of the currently first document showing |
369
|
|
|
*/ |
370
|
|
|
public function getStartIndex() |
371
|
|
|
{ |
372
|
|
|
return ($this->page - 1) * $this->resultsPerPage; |
373
|
|
|
} |
374
|
|
|
|
375
|
|
|
/** |
376
|
|
|
* Gets the index of the last result document we're showing |
377
|
|
|
* |
378
|
|
|
* @return int index of the currently last document showing |
379
|
|
|
*/ |
380
|
|
|
public function getEndIndex() |
381
|
|
|
{ |
382
|
|
|
return $this->page * $this->resultsPerPage; |
383
|
|
|
} |
384
|
|
|
|
385
|
|
|
// query elevation |
386
|
|
|
|
387
|
|
|
/** |
388
|
|
|
* Activates and deactivates query elevation for the current query. |
389
|
|
|
* |
390
|
|
|
* @param bool $elevation True to enable query elevation (default), FALSE to disable query elevation. |
391
|
|
|
* @param bool $forceElevation Optionally force elevation so that the elevated documents are always on top regardless of sorting, default to TRUE. |
392
|
|
|
* @param bool $markElevatedResults Mark elevated results |
393
|
|
|
* @return void |
394
|
|
|
*/ |
395
|
34 |
|
public function setQueryElevation($elevation = true, $forceElevation = true, $markElevatedResults = true) |
396
|
|
|
{ |
397
|
34 |
|
if ($elevation) { |
398
|
30 |
|
$this->queryParameters['enableElevation'] = 'true'; |
399
|
30 |
|
$this->setForceElevation($forceElevation); |
400
|
30 |
|
if ($markElevatedResults) { |
401
|
30 |
|
$this->getReturnFields()->add('isElevated:[elevated]'); |
402
|
|
|
} |
403
|
|
|
} else { |
404
|
5 |
|
$this->queryParameters['enableElevation'] = 'false'; |
405
|
5 |
|
unset($this->queryParameters['forceElevation']); |
406
|
5 |
|
$this->getReturnFields()->remove('isElevated:[elevated]'); |
407
|
5 |
|
$this->getReturnFields()->remove('[elevated]'); // fallback |
408
|
|
|
} |
409
|
34 |
|
} |
410
|
|
|
|
411
|
|
|
/** |
412
|
|
|
* Enables or disables the forceElevation query parameter. |
413
|
|
|
* |
414
|
|
|
* @param bool $forceElevation |
415
|
|
|
*/ |
416
|
30 |
|
protected function setForceElevation($forceElevation) |
417
|
|
|
{ |
418
|
30 |
|
if ($forceElevation) { |
419
|
29 |
|
$this->queryParameters['forceElevation'] = 'true'; |
420
|
|
|
} else { |
421
|
1 |
|
$this->queryParameters['forceElevation'] = 'false'; |
422
|
|
|
} |
423
|
30 |
|
} |
424
|
|
|
|
425
|
|
|
// collapsing |
426
|
|
|
|
427
|
|
|
/** |
428
|
|
|
* Check whether collapsing is active |
429
|
|
|
* |
430
|
|
|
* @return bool |
431
|
|
|
*/ |
432
|
3 |
|
public function getIsCollapsing() |
433
|
|
|
{ |
434
|
3 |
|
return $this->getFilters()->hasWithName('collapsing'); |
435
|
|
|
} |
436
|
|
|
|
437
|
|
|
/** |
438
|
|
|
* @param string $fieldName |
439
|
|
|
*/ |
440
|
4 |
|
public function setVariantField($fieldName) |
441
|
|
|
{ |
442
|
4 |
|
$this->variantField = $fieldName; |
443
|
4 |
|
} |
444
|
|
|
|
445
|
|
|
/** |
446
|
|
|
* @return string |
447
|
|
|
*/ |
448
|
1 |
|
public function getVariantField() |
449
|
|
|
{ |
450
|
1 |
|
return $this->variantField; |
451
|
|
|
} |
452
|
|
|
|
453
|
|
|
/** |
454
|
|
|
* @param bool $collapsing |
455
|
|
|
*/ |
456
|
6 |
|
public function setCollapsing($collapsing = true) |
457
|
|
|
{ |
458
|
6 |
|
if ($collapsing) { |
459
|
6 |
|
$this->getFilters()->add('{!collapse field=' . $this->variantField . '}', 'collapsing'); |
460
|
6 |
|
if ($this->solrConfiguration->getSearchVariantsExpand()) { |
461
|
2 |
|
$this->queryParameters['expand'] = 'true'; |
462
|
6 |
|
$this->queryParameters['expand.rows'] = $this->solrConfiguration->getSearchVariantsLimit(); |
463
|
|
|
} |
464
|
|
|
} else { |
465
|
1 |
|
$this->getFilters()->removeByName('collapsing'); |
466
|
1 |
|
unset($this->queryParameters['expand']); |
467
|
1 |
|
unset($this->queryParameters['expand.rows']); |
468
|
|
|
} |
469
|
6 |
|
} |
470
|
|
|
|
471
|
|
|
// grouping |
472
|
|
|
|
473
|
|
|
/** |
474
|
|
|
* Activates and deactivates grouping for the current query. |
475
|
|
|
* |
476
|
|
|
* @param Grouping $grouping TRUE to enable grouping, FALSE to disable grouping |
477
|
|
|
* @return void |
478
|
|
|
*/ |
479
|
120 |
|
public function setGrouping(Grouping $grouping) |
480
|
|
|
{ |
481
|
120 |
|
$this->grouping = $grouping; |
482
|
120 |
|
} |
483
|
|
|
|
484
|
|
|
/** |
485
|
|
|
* @return Grouping |
486
|
|
|
*/ |
487
|
86 |
|
public function getGrouping() |
488
|
|
|
{ |
489
|
86 |
|
return $this->grouping; |
490
|
|
|
} |
491
|
|
|
|
492
|
|
|
/** |
493
|
|
|
* Returns the number of results that should be shown per page |
494
|
|
|
* |
495
|
|
|
* @return int number of results to show per page |
496
|
|
|
*/ |
497
|
33 |
|
public function getResultsPerPage() |
498
|
|
|
{ |
499
|
33 |
|
if ($this->getGrouping() instanceof Grouping && $this->getGrouping()->getIsEnabled()) { |
500
|
1 |
|
return $this->getGrouping()->getNumberOfGroups(); |
501
|
|
|
} |
502
|
|
|
|
503
|
33 |
|
return $this->resultsPerPage; |
504
|
|
|
} |
505
|
|
|
|
506
|
|
|
/** |
507
|
|
|
* Sets the number of results that should be shown per page |
508
|
|
|
* |
509
|
|
|
* @param int $resultsPerPage Number of results to show per page |
510
|
|
|
* @return void |
511
|
|
|
*/ |
512
|
39 |
|
public function setResultsPerPage($resultsPerPage) |
513
|
|
|
{ |
514
|
39 |
|
$this->resultsPerPage = max(intval($resultsPerPage), 0); |
515
|
39 |
|
} |
516
|
|
|
|
517
|
|
|
// faceting |
518
|
|
|
|
519
|
|
|
/** |
520
|
|
|
* Activates and deactivates faceting for the current query. |
521
|
|
|
* |
522
|
|
|
* @param Faceting $faceting TRUE to enable faceting, FALSE to disable faceting |
523
|
|
|
* @return void |
524
|
|
|
*/ |
525
|
120 |
|
public function setFaceting(Faceting $faceting) |
526
|
|
|
{ |
527
|
120 |
|
$this->faceting = $faceting; |
528
|
120 |
|
} |
529
|
|
|
|
530
|
|
|
/** |
531
|
|
|
* @return Faceting |
532
|
|
|
*/ |
533
|
80 |
|
public function getFaceting() |
534
|
|
|
{ |
535
|
80 |
|
return $this->faceting; |
536
|
|
|
} |
537
|
|
|
|
538
|
|
|
|
539
|
|
|
/** |
540
|
|
|
* Gets all currently applied filters. |
541
|
|
|
* |
542
|
|
|
* @return Filters Array of filters |
543
|
|
|
*/ |
544
|
95 |
|
public function getFilters() |
545
|
|
|
{ |
546
|
95 |
|
return $this->filters; |
547
|
|
|
} |
548
|
|
|
|
549
|
|
|
/** |
550
|
|
|
* Sets the filters to use. |
551
|
|
|
* |
552
|
|
|
* @param Filters $filters |
553
|
|
|
*/ |
554
|
121 |
|
public function setFilters(Filters $filters) |
555
|
|
|
{ |
556
|
121 |
|
$this->filters = $filters; |
557
|
121 |
|
} |
558
|
|
|
|
559
|
|
|
// sorting |
560
|
|
|
|
561
|
|
|
/** |
562
|
|
|
* Sets access restrictions for a frontend user. |
563
|
|
|
* |
564
|
|
|
* @param array $groups Array of groups a user has been assigned to |
565
|
|
|
*/ |
566
|
38 |
|
public function setUserAccessGroups(array $groups) |
567
|
|
|
{ |
568
|
38 |
|
$groups = array_map('intval', $groups); |
569
|
38 |
|
$groups[] = 0; // always grant access to public documents |
570
|
38 |
|
$groups = array_unique($groups); |
571
|
38 |
|
sort($groups, SORT_NUMERIC); |
572
|
|
|
|
573
|
38 |
|
$accessFilter = '{!typo3access}' . implode(',', $groups); |
574
|
38 |
|
$this->getFilters()->removeByPrefix('{!typo3access}'); |
575
|
38 |
|
$this->getFilters()->add($accessFilter); |
576
|
38 |
|
} |
577
|
|
|
|
578
|
|
|
// query parameters |
579
|
|
|
|
580
|
|
|
/** |
581
|
|
|
* Limits the query to certain sites |
582
|
|
|
* |
583
|
|
|
* @param string $allowedSites Comma-separated list of domains |
584
|
|
|
*/ |
585
|
34 |
|
public function setSiteHashFilter($allowedSites) |
586
|
|
|
{ |
587
|
34 |
|
if (trim($allowedSites) === '*') { |
588
|
1 |
|
return; |
589
|
|
|
} |
590
|
|
|
|
591
|
33 |
|
$allowedSites = GeneralUtility::trimExplode(',', $allowedSites); |
592
|
33 |
|
$filters = []; |
593
|
|
|
|
594
|
33 |
|
foreach ($allowedSites as $site) { |
595
|
33 |
|
$siteHash = $this->siteHashService->getSiteHashForDomain($site); |
596
|
33 |
|
$filters[] = 'siteHash:"' . $siteHash . '"'; |
597
|
|
|
} |
598
|
|
|
|
599
|
33 |
|
$this->getFilters()->add(implode(' OR ', $filters)); |
600
|
33 |
|
} |
601
|
|
|
|
602
|
|
|
/** |
603
|
|
|
* Limits the query to certain page tree branches |
604
|
|
|
* |
605
|
|
|
* @param string $pageIds Comma-separated list of page IDs |
606
|
|
|
*/ |
607
|
|
|
public function setRootlineFilter($pageIds) |
608
|
|
|
{ |
609
|
|
|
$pageIds = GeneralUtility::trimExplode(',', $pageIds); |
610
|
|
|
$filters = []; |
611
|
|
|
|
612
|
|
|
/** @var $processor PageUidToHierarchy */ |
613
|
|
|
$processor = GeneralUtility::makeInstance(PageUidToHierarchy::class); |
614
|
|
|
$hierarchies = $processor->process($pageIds); |
615
|
|
|
|
616
|
|
|
foreach ($hierarchies as $hierarchy) { |
617
|
|
|
$lastLevel = array_pop($hierarchy); |
618
|
|
|
$filters[] = 'rootline:"' . $lastLevel . '"'; |
619
|
|
|
} |
620
|
|
|
|
621
|
|
|
$this->getFilters()->add(implode(' OR ', $filters)); |
622
|
|
|
} |
623
|
|
|
|
624
|
|
|
/** |
625
|
|
|
* @param ReturnFields $returnFields |
626
|
|
|
*/ |
627
|
120 |
|
public function setReturnFields(ReturnFields $returnFields) |
628
|
|
|
{ |
629
|
120 |
|
$this->returnFields = $returnFields; |
630
|
120 |
|
} |
631
|
|
|
|
632
|
|
|
/** |
633
|
|
|
* @return ReturnFields |
634
|
|
|
*/ |
635
|
85 |
|
public function getReturnFields() |
636
|
|
|
{ |
637
|
85 |
|
return $this->returnFields; |
638
|
|
|
} |
639
|
|
|
|
640
|
|
|
/** |
641
|
|
|
* Gets the query type, Solr's qt parameter. |
642
|
|
|
* |
643
|
|
|
* @return string Query type, qt parameter. |
644
|
|
|
*/ |
645
|
1 |
|
public function getQueryType() |
646
|
|
|
{ |
647
|
1 |
|
return $this->queryParameters['qt']; |
648
|
|
|
} |
649
|
|
|
|
650
|
|
|
/** |
651
|
|
|
* Sets the query type, Solr's qt parameter. |
652
|
|
|
* |
653
|
|
|
* @param string|bool $queryType String query type or boolean FALSE to disable / reset the qt parameter. |
654
|
|
|
* @see http://wiki.apache.org/solr/CoreQueryParameters#qt |
655
|
|
|
*/ |
656
|
2 |
|
public function setQueryType($queryType) |
657
|
|
|
{ |
658
|
2 |
|
$this->setQueryParameterWhenStringOrUnsetWhenEmpty('qt', $queryType); |
659
|
2 |
|
} |
660
|
|
|
|
661
|
|
|
/** |
662
|
|
|
* Sets the query operator to AND or OR. Unsets the query operator (actually |
663
|
|
|
* sets it back to default) for FALSE. |
664
|
|
|
* |
665
|
|
|
* @param string|bool $operator AND or OR, FALSE to unset |
666
|
|
|
*/ |
667
|
1 |
|
public function setOperator($operator) |
668
|
|
|
{ |
669
|
1 |
|
if (in_array($operator, [self::OPERATOR_AND, self::OPERATOR_OR])) { |
670
|
1 |
|
$this->queryParameters['q.op'] = $operator; |
671
|
|
|
} |
672
|
|
|
|
673
|
1 |
|
if ($operator === false) { |
674
|
1 |
|
unset($this->queryParameters['q.op']); |
675
|
|
|
} |
676
|
1 |
|
} |
677
|
|
|
|
678
|
|
|
/** |
679
|
|
|
* Gets the alternative query, Solr's q.alt parameter. |
680
|
|
|
* |
681
|
|
|
* @return string Alternative query, q.alt parameter. |
682
|
|
|
*/ |
683
|
1 |
|
public function getAlternativeQuery() |
684
|
|
|
{ |
685
|
1 |
|
return $this->queryParameters['q.alt']; |
686
|
|
|
} |
687
|
|
|
|
688
|
|
|
/** |
689
|
|
|
* Sets an alternative query, Solr's q.alt parameter. |
690
|
|
|
* |
691
|
|
|
* This query supports the complete Lucene Query Language. |
692
|
|
|
* |
693
|
|
|
* @param string $alternativeQuery String alternative query or boolean FALSE to disable / reset the q.alt parameter. |
694
|
|
|
* @see http://wiki.apache.org/solr/DisMaxQParserPlugin#q.alt |
695
|
|
|
*/ |
696
|
34 |
|
public function setAlternativeQuery($alternativeQuery) |
697
|
|
|
{ |
698
|
34 |
|
$this->setQueryParameterWhenStringOrUnsetWhenEmpty('q.alt', $alternativeQuery); |
699
|
34 |
|
} |
700
|
|
|
|
701
|
|
|
// keywords |
702
|
|
|
|
703
|
|
|
/** |
704
|
|
|
* Set the query to omit the response header |
705
|
|
|
* |
706
|
|
|
* @param bool $omitHeader TRUE (default) to omit response headers, FALSE to re-enable |
707
|
|
|
*/ |
708
|
1 |
|
public function setOmitHeader($omitHeader = true) |
709
|
|
|
{ |
710
|
1 |
|
$omitHeader = ($omitHeader === true) ? 'true' : $omitHeader; |
711
|
1 |
|
$this->setQueryParameterWhenStringOrUnsetWhenEmpty('omitHeader', $omitHeader); |
712
|
1 |
|
} |
713
|
|
|
|
714
|
|
|
/** |
715
|
|
|
* Get the query keywords, keywords are escaped. |
716
|
|
|
* |
717
|
|
|
* @return string query keywords |
718
|
|
|
*/ |
719
|
33 |
|
public function getKeywords() |
720
|
|
|
{ |
721
|
33 |
|
return $this->keywords; |
722
|
|
|
} |
723
|
|
|
|
724
|
|
|
/** |
725
|
|
|
* Sets the query keywords, escapes them as needed for Solr/Lucene. |
726
|
|
|
* |
727
|
|
|
* @param string $keywords user search terms/keywords |
728
|
|
|
*/ |
729
|
121 |
|
public function setKeywords($keywords) |
730
|
|
|
{ |
731
|
121 |
|
$this->keywords = $this->escapeService->escape($keywords); |
|
|
|
|
732
|
121 |
|
$this->keywordsRaw = $keywords; |
733
|
121 |
|
} |
734
|
|
|
|
735
|
|
|
/** |
736
|
|
|
* Gets the cleaned keywords so that it can be used in templates f.e. |
737
|
|
|
* |
738
|
|
|
* @return string The cleaned keywords. |
739
|
|
|
*/ |
740
|
26 |
|
public function getKeywordsCleaned() |
741
|
|
|
{ |
742
|
26 |
|
return $this->cleanKeywords($this->keywordsRaw); |
743
|
|
|
} |
744
|
|
|
|
745
|
|
|
/** |
746
|
|
|
* Helper method to escape/encode keywords for use in HTML |
747
|
|
|
* |
748
|
|
|
* @param string $keywords Keywords to prepare for use in HTML |
749
|
|
|
* @return string Encoded keywords |
750
|
|
|
*/ |
751
|
26 |
|
public static function cleanKeywords($keywords) |
752
|
|
|
{ |
753
|
26 |
|
$keywords = trim($keywords); |
754
|
26 |
|
$keywords = htmlspecialchars($keywords); |
755
|
26 |
|
return $keywords; |
756
|
|
|
} |
757
|
|
|
|
758
|
|
|
// relevance, matching |
759
|
|
|
|
760
|
|
|
/** |
761
|
|
|
* Gets the raw, unescaped, unencoded keywords. |
762
|
|
|
* |
763
|
|
|
* USE WITH CAUTION! |
764
|
|
|
* |
765
|
|
|
* @return string raw keywords |
766
|
|
|
*/ |
767
|
|
|
public function getKeywordsRaw() |
768
|
|
|
{ |
769
|
|
|
return $this->keywordsRaw; |
770
|
|
|
} |
771
|
|
|
|
772
|
|
|
/** |
773
|
|
|
* Sets the minimum match (mm) parameter |
774
|
|
|
* |
775
|
|
|
* @param mixed $minimumMatch Minimum match parameter as string or boolean FALSE to disable / reset the mm parameter |
776
|
|
|
* @see http://wiki.apache.org/solr/DisMaxRequestHandler#mm_.28Minimum_.27Should.27_Match.29 |
777
|
|
|
*/ |
778
|
1 |
|
public function setMinimumMatch($minimumMatch) |
779
|
|
|
{ |
780
|
1 |
|
$this->setQueryParameterWhenStringOrUnsetWhenEmpty('mm', $minimumMatch); |
781
|
1 |
|
} |
782
|
|
|
|
783
|
|
|
/** |
784
|
|
|
* Sets the boost function (bf) parameter |
785
|
|
|
* |
786
|
|
|
* @param mixed $boostFunction boost function parameter as string or boolean FALSE to disable / reset the bf parameter |
787
|
|
|
* @see http://wiki.apache.org/solr/DisMaxRequestHandler#bf_.28Boost_Functions.29 |
788
|
|
|
*/ |
789
|
1 |
|
public function setBoostFunction($boostFunction) |
790
|
|
|
{ |
791
|
1 |
|
$this->setQueryParameterWhenStringOrUnsetWhenEmpty('bf', $boostFunction); |
792
|
1 |
|
} |
793
|
|
|
|
794
|
|
|
// query fields |
795
|
|
|
// TODO move up to field list methods |
796
|
|
|
|
797
|
|
|
/** |
798
|
|
|
* Sets the boost query (bq) parameter |
799
|
|
|
* |
800
|
|
|
* @param mixed $boostQuery boost query parameter as string or array to set a boost query or boolean FALSE to disable / reset the bq parameter |
801
|
|
|
* @see http://wiki.apache.org/solr/DisMaxQParserPlugin#bq_.28Boost_Query.29 |
802
|
|
|
*/ |
803
|
1 |
|
public function setBoostQuery($boostQuery) |
804
|
|
|
{ |
805
|
1 |
|
if (is_array($boostQuery)) { |
806
|
|
|
$this->queryParameters['bq'] = $boostQuery; |
807
|
|
|
return; |
808
|
|
|
} |
809
|
1 |
|
$this->setQueryParameterWhenStringOrUnsetWhenEmpty('bq', $boostQuery); |
810
|
1 |
|
} |
811
|
|
|
|
812
|
|
|
/** |
813
|
|
|
* Set the tie breaker (tie) parameter |
814
|
|
|
* |
815
|
|
|
* @param mixed $tieParameter tie breaker parameter as string or boolean FALSE to disable / reset the tie parameter |
816
|
|
|
* @return void |
817
|
|
|
*/ |
818
|
|
|
public function setTieParameter($tieParameter) |
819
|
7 |
|
{ |
820
|
|
|
$this->setQueryParameterWhenStringOrUnsetWhenEmpty('tie', $tieParameter); |
821
|
7 |
|
} |
822
|
7 |
|
|
823
|
|
|
/** |
824
|
|
|
* Gets a specific query parameter by its name. |
825
|
|
|
* |
826
|
|
|
* @param string $parameterName The parameter to return |
827
|
|
|
* @param mixed $defaultIfEmpty |
828
|
|
|
* @return mixed The parameter's value or $defaultIfEmpty if not set |
829
|
|
|
*/ |
830
|
80 |
|
public function getQueryParameter($parameterName, $defaultIfEmpty = null) |
831
|
|
|
{ |
832
|
80 |
|
$parameters = $this->getQueryParameters(); |
833
|
80 |
|
return isset($parameters[$parameterName]) ? $parameters[$parameterName] : $defaultIfEmpty; |
834
|
80 |
|
} |
835
|
80 |
|
|
836
|
80 |
|
/** |
837
|
80 |
|
* Builds an array of query parameters to use for the search query. |
838
|
80 |
|
* |
839
|
|
|
* @return array An array ready to use with query parameters |
840
|
80 |
|
*/ |
841
|
|
|
public function getQueryParameters() |
842
|
|
|
{ |
843
|
|
|
$queryParameters = $this->getReturnFields()->build(); |
844
|
|
|
$queryParameters = array_merge($queryParameters, $this->getFilters()->build()); |
845
|
|
|
$queryParameters = array_merge($queryParameters, $this->queryParameters); |
846
|
|
|
$queryParameters = array_merge($queryParameters, $this->getQueryFields()->build()); |
847
|
|
|
$queryParameters = array_merge($queryParameters, $this->getHighlighting()->build()); |
848
|
|
|
$queryParameters = array_merge($queryParameters, $this->getFaceting()->build()); |
849
|
|
|
$queryParameters = array_merge($queryParameters, $this->getGrouping()->build()); |
850
|
|
|
|
851
|
|
|
return $queryParameters; |
852
|
120 |
|
} |
853
|
|
|
|
854
|
120 |
|
// general query parameters |
855
|
120 |
|
|
856
|
|
|
/** |
857
|
|
|
* Enables or disables highlighting of search terms in result teasers. |
858
|
|
|
* |
859
|
|
|
* @param Highlighting $highlighting |
860
|
80 |
|
* @see http://wiki.apache.org/solr/HighlightingParameters |
861
|
|
|
* @return void |
862
|
80 |
|
*/ |
863
|
|
|
public function setHighlighting(Highlighting $highlighting) |
864
|
|
|
{ |
865
|
|
|
$this->highlighting = $highlighting; |
866
|
|
|
} |
867
|
|
|
|
868
|
|
|
/** |
869
|
|
|
* @return Highlighting |
870
|
|
|
*/ |
871
|
|
|
public function getHighlighting() |
872
|
30 |
|
{ |
873
|
|
|
return $this->highlighting; |
874
|
30 |
|
} |
875
|
30 |
|
|
876
|
30 |
|
// misc |
877
|
30 |
|
|
878
|
30 |
|
/** |
879
|
|
|
* Enables or disables spellchecking for the query. |
880
|
1 |
|
* |
881
|
1 |
|
* @param bool $spellchecking Enables spellchecking when set to TRUE, deactivates spellchecking when set to FALSE, defaults to TRUE. |
882
|
1 |
|
*/ |
883
|
|
|
public function setSpellchecking($spellchecking = true) |
884
|
30 |
|
{ |
885
|
|
|
if ($spellchecking) { |
886
|
|
|
$this->queryParameters['spellcheck'] = 'true'; |
887
|
|
|
$this->queryParameters['spellcheck.collate'] = 'true'; |
888
|
|
|
$maxCollationTries = $this->solrConfiguration->getSearchSpellcheckingNumberOfSuggestionsToTry(); |
889
|
|
|
$this->addQueryParameter('spellcheck.maxCollationTries', $maxCollationTries); |
890
|
|
|
} else { |
891
|
|
|
unset($this->queryParameters['spellcheck']); |
892
|
|
|
unset($this->queryParameters['spellcheck.collate']); |
893
|
40 |
|
unset($this->queryParameters['spellcheck.maxCollationTries']); |
894
|
|
|
} |
895
|
40 |
|
} |
896
|
40 |
|
|
897
|
|
|
/** |
898
|
6 |
|
* This method can be used to set a query parameter when the value is a string and not empty or unset it |
899
|
|
|
* in any other case. Extracted to avoid duplicate code. |
900
|
40 |
|
* |
901
|
|
|
* @param string $parameterName |
902
|
|
|
* @param mixed $value |
903
|
|
|
*/ |
904
|
|
|
private function setQueryParameterWhenStringOrUnsetWhenEmpty($parameterName, $value) |
905
|
|
|
{ |
906
|
|
|
if (is_string($value) && !empty($value)) { |
907
|
|
|
$this->addQueryParameter($parameterName, $value); |
908
|
42 |
|
} else { |
909
|
|
|
unset($this->queryParameters[$parameterName]); |
910
|
42 |
|
} |
911
|
42 |
|
} |
912
|
|
|
|
913
|
|
|
/** |
914
|
|
|
* Adds any generic query parameter. |
915
|
|
|
* |
916
|
|
|
* @param string $parameterName Query parameter name |
917
|
|
|
* @param string $parameterValue Parameter value |
918
|
|
|
*/ |
919
|
|
|
public function addQueryParameter($parameterName, $parameterValue) |
920
|
|
|
{ |
921
|
|
|
$this->queryParameters[$parameterName] = $parameterValue; |
922
|
|
|
} |
923
|
|
|
|
924
|
|
|
/** |
925
|
|
|
* Sets the sort parameter. |
926
|
2 |
|
* |
927
|
|
|
* $sorting must include a field name (or the pseudo-field score), |
928
|
2 |
|
* followed by a space, |
929
|
2 |
|
* followed by a sort direction (asc or desc). |
930
|
|
|
* |
931
|
|
|
* Multiple fallback sortings can be separated by comma, |
932
|
2 |
|
* ie: <field name> <direction>[,<field name> <direction>]... |
933
|
2 |
|
* |
934
|
|
|
* @param string|bool $sorting Either a comma-separated list of sort fields and directions or FALSE to reset sorting to the default behavior (sort by score / relevance) |
935
|
1 |
|
* @see http://wiki.apache.org/solr/CommonQueryParameters#sort |
936
|
|
|
*/ |
937
|
2 |
|
public function setSorting($sorting) |
938
|
|
|
{ |
939
|
|
|
if ($sorting) { |
940
|
|
|
if (!is_string($sorting)) { |
941
|
|
|
throw new \InvalidArgumentException('Sorting needs to be a string!'); |
942
|
|
|
} |
943
|
|
|
$sortParameter = $this->removeRelevanceSortField($sorting); |
944
|
|
|
$this->queryParameters['sort'] = $sortParameter; |
945
|
2 |
|
} else { |
946
|
|
|
unset($this->queryParameters['sort']); |
947
|
2 |
|
} |
948
|
2 |
|
} |
949
|
2 |
|
|
950
|
1 |
|
/** |
951
|
1 |
|
* Removes the relevance sort field if present in the sorting field definition. |
952
|
|
|
* |
953
|
|
|
* @param string $sorting |
954
|
2 |
|
* @return string |
955
|
|
|
*/ |
956
|
|
|
protected function removeRelevanceSortField($sorting) |
957
|
|
|
{ |
958
|
|
|
$sortParameter = $sorting; |
959
|
|
|
list($sortField) = explode(' ', $sorting); |
960
|
|
|
if ($sortField === 'relevance') { |
961
|
|
|
$sortParameter = ''; |
962
|
29 |
|
return $sortParameter; |
963
|
|
|
} |
964
|
29 |
|
|
965
|
29 |
|
return $sortParameter; |
966
|
29 |
|
} |
967
|
|
|
|
968
|
1 |
|
/** |
969
|
1 |
|
* Enables or disables the debug parameter for the query. |
970
|
|
|
* |
971
|
29 |
|
* @param bool $debugMode Enables debugging when set to TRUE, deactivates debugging when set to FALSE, defaults to TRUE. |
972
|
|
|
*/ |
973
|
|
|
public function setDebugMode($debugMode = true) |
974
|
|
|
{ |
975
|
|
|
if ($debugMode) { |
976
|
|
|
$this->queryParameters['debugQuery'] = 'true'; |
977
|
|
|
$this->queryParameters['echoParams'] = 'all'; |
978
|
2 |
|
} else { |
979
|
|
|
unset($this->queryParameters['debugQuery']); |
980
|
2 |
|
unset($this->queryParameters['echoParams']); |
981
|
|
|
} |
982
|
|
|
} |
983
|
|
|
|
984
|
|
|
/** |
985
|
|
|
* Returns the link target page id. |
986
|
|
|
* |
987
|
|
|
* @return int |
988
|
120 |
|
*/ |
989
|
|
|
public function getLinkTargetPageId() |
990
|
|
|
{ |
991
|
120 |
|
return $this->linkTargetPageId; |
992
|
4 |
|
} |
993
|
4 |
|
|
994
|
4 |
|
/** |
995
|
|
|
* Activates the collapsing on the configured field, if collapsing was enabled. |
996
|
4 |
|
* |
997
|
|
|
* @return bool |
998
|
|
|
*/ |
999
|
116 |
|
protected function initializeCollapsingFromConfiguration() |
1000
|
|
|
{ |
1001
|
|
|
// check collapsing |
1002
|
|
|
if ($this->solrConfiguration->getSearchVariants()) { |
1003
|
|
|
$collapseField = $this->solrConfiguration->getSearchVariantsField(); |
1004
|
|
|
$this->setVariantField($collapseField); |
1005
|
120 |
|
$this->setCollapsing(true); |
1006
|
|
|
|
1007
|
120 |
|
return true; |
1008
|
120 |
|
} |
1009
|
120 |
|
|
1010
|
|
|
return false; |
1011
|
|
|
} |
1012
|
|
|
|
1013
|
|
|
/** |
1014
|
120 |
|
* @return void |
1015
|
|
|
*/ |
1016
|
120 |
|
protected function initializeFaceting() |
1017
|
120 |
|
{ |
1018
|
120 |
|
$faceting = Faceting::fromTypoScriptConfiguration($this->solrConfiguration); |
1019
|
|
|
$this->setFaceting($faceting); |
1020
|
|
|
} |
1021
|
|
|
|
1022
|
|
|
/** |
1023
|
121 |
|
* @return void |
1024
|
|
|
*/ |
1025
|
121 |
|
protected function initializeGrouping() |
1026
|
121 |
|
{ |
1027
|
121 |
|
$grouping = Grouping::fromTypoScriptConfiguration($this->solrConfiguration); |
1028
|
|
|
$this->setGrouping($grouping); |
1029
|
|
|
} |
1030
|
|
|
|
1031
|
|
|
/** |
1032
|
|
|
* @return void |
1033
|
|
|
*/ |
1034
|
|
|
protected function initializeFilters() |
1035
|
|
|
{ |
1036
|
|
|
$filters = Filters::fromTypoScriptConfiguration($this->solrConfiguration); |
1037
|
|
|
$this->setFilters($filters); |
1038
|
|
|
} |
1039
|
|
|
} |
1040
|
|
|
|
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.