SearchSuggestionSet::__construct()   A
last analyzed

Complexity

Conditions 4
Paths 3

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 6
nc 3
nop 1
dl 0
loc 9
rs 9.2
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Search suggestion sets
5
 *
6
 * This program is free software; you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation; either version 2 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License along
17
 * with this program; if not, write to the Free Software Foundation, Inc.,
18
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
 * http://www.gnu.org/copyleft/gpl.html
20
 *
21
 */
22
23
/**
24
 * A set of search suggestions.
25
 * The set is always ordered by score, with the best match first.
26
 */
27
class SearchSuggestionSet {
28
	/**
29
	 * @var SearchSuggestion[]
30
	 */
31
	private $suggestions = [];
32
33
	/**
34
	 *
35
	 * @var array
36
	 */
37
	private $pageMap = [];
38
39
	/**
40
	 * Builds a new set of suggestions.
41
	 *
42
	 * NOTE: the array should be sorted by score (higher is better),
43
	 * in descending order.
44
	 * SearchSuggestionSet will not try to re-order this input array.
45
	 * Providing an unsorted input array is a mistake and will lead to
46
	 * unexpected behaviors.
47
	 *
48
	 * @param SearchSuggestion[] $suggestions (must be sorted by score)
49
	 */
50
	public function __construct( array $suggestions ) {
51
		foreach ( $suggestions as $suggestion ) {
52
			$pageID = $suggestion->getSuggestedTitleID();
53
			if ( $pageID && empty( $this->pageMap[$pageID] ) ) {
54
				$this->pageMap[$pageID] = true;
55
			}
56
			$this->suggestions[] = $suggestion;
57
		}
58
	}
59
60
	/**
61
	 * Get the list of suggestions.
62
	 * @return SearchSuggestion[]
63
	 */
64
	public function getSuggestions() {
65
		return $this->suggestions;
66
	}
67
68
	/**
69
	 * Call array_map on the suggestions array
70
	 * @param callback $callback
71
	 * @return array
72
	 */
73
	public function map( $callback ) {
74
		return array_map( $callback, $this->suggestions );
75
	}
76
77
	/**
78
	 * Add a new suggestion at the end.
79
	 * If the score of the new suggestion is greater than the worst one,
80
	 * the new suggestion score will be updated (worst - 1).
81
	 *
82
	 * @param SearchSuggestion $suggestion
83
	 */
84 View Code Duplication
	public function append( SearchSuggestion $suggestion ) {
85
		$pageID = $suggestion->getSuggestedTitleID();
86
		if ( $pageID && isset( $this->pageMap[$pageID] ) ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $pageID of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
87
			return;
88
		}
89
		if ( $this->getSize() > 0 && $suggestion->getScore() >= $this->getWorstScore() ) {
90
			$suggestion->setScore( $this->getWorstScore() - 1 );
91
		}
92
		$this->suggestions[] = $suggestion;
93
		if ( $pageID ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $pageID of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
94
			$this->pageMap[$pageID] = true;
95
		}
96
	}
97
98
	/**
99
	 * Add suggestion set to the end of the current one.
100
	 * @param SearchSuggestionSet $set
101
	 */
102
	public function appendAll( SearchSuggestionSet $set ) {
103
		foreach ( $set->getSuggestions() as $sugg ) {
104
			$this->append( $sugg );
105
		}
106
	}
107
108
	/**
109
	 * Move the suggestion at index $key to the first position
110
	 */
111
	public function rescore( $key ) {
112
		$removed = array_splice( $this->suggestions, $key, 1 );
113
		unset( $this->pageMap[$removed[0]->getSuggestedTitleID()] );
114
		$this->prepend( $removed[0] );
115
	}
116
117
	/**
118
	 * Add a new suggestion at the top. If the new suggestion score
119
	 * is lower than the best one its score will be updated (best + 1)
120
	 * @param SearchSuggestion $suggestion
121
	 */
122 View Code Duplication
	public function prepend( SearchSuggestion $suggestion ) {
123
		$pageID = $suggestion->getSuggestedTitleID();
124
		if ( $pageID && isset( $this->pageMap[$pageID] ) ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $pageID of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
125
			return;
126
		}
127
		if ( $this->getSize() > 0 && $suggestion->getScore() <= $this->getBestScore() ) {
128
			$suggestion->setScore( $this->getBestScore() + 1 );
129
		}
130
		array_unshift( $this->suggestions,  $suggestion );
131
		if ( $pageID ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $pageID of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
132
			$this->pageMap[$pageID] = true;
133
		}
134
	}
135
136
	/**
137
	 * @return float the best score in this suggestion set
138
	 */
139
	public function getBestScore() {
140
		if ( empty( $this->suggestions ) ) {
141
			return 0;
142
		}
143
		return $this->suggestions[0]->getScore();
144
	}
145
146
	/**
147
	 * @return float the worst score in this set
148
	 */
149
	public function getWorstScore() {
150
		if ( empty( $this->suggestions ) ) {
151
			return 0;
152
		}
153
		return end( $this->suggestions )->getScore();
154
	}
155
156
	/**
157
	 * @return int the number of suggestion in this set
158
	 */
159
	public function getSize() {
160
		return count( $this->suggestions );
161
	}
162
163
	/**
164
	 * Remove any extra elements in the suggestions set
165
	 * @param int $limit the max size of this set.
166
	 */
167
	public function shrink( $limit ) {
168
		if ( count( $this->suggestions ) > $limit ) {
169
			$this->suggestions = array_slice( $this->suggestions, 0, $limit );
170
		}
171
	}
172
173
	/**
174
	 * Builds a new set of suggestion based on a title array.
175
	 * Useful when using a backend that supports only Titles.
176
	 *
177
	 * NOTE: Suggestion scores will be generated.
178
	 *
179
	 * @param Title[] $titles
180
	 * @return SearchSuggestionSet
181
	 */
182 View Code Duplication
	public static function fromTitles( array $titles ) {
183
		$score = count( $titles );
184
		$suggestions = array_map( function( $title ) use ( &$score ) {
185
			return SearchSuggestion::fromTitle( $score--, $title );
186
		}, $titles );
187
		return new SearchSuggestionSet( $suggestions );
188
	}
189
190
	/**
191
	 * Builds a new set of suggestion based on a string array.
192
	 *
193
	 * NOTE: Suggestion scores will be generated.
194
	 *
195
	 * @param string[] $titles
196
	 * @return SearchSuggestionSet
197
	 */
198 View Code Duplication
	public static function fromStrings( array $titles ) {
199
		$score = count( $titles );
200
		$suggestions = array_map( function( $title ) use ( &$score ) {
201
			return SearchSuggestion::fromText( $score--, $title );
202
		}, $titles );
203
		return new SearchSuggestionSet( $suggestions );
204
	}
205
206
	/**
207
	 * @return SearchSuggestionSet an empty suggestion set
208
	 */
209
	public static function emptySuggestionSet() {
210
		return new SearchSuggestionSet( [] );
211
	}
212
}
213