1 | <?php |
||||||
2 | |||||||
3 | /** |
||||||
4 | * Utility class for search functionality. |
||||||
5 | * |
||||||
6 | * @package ElkArte Forum |
||||||
7 | * @copyright ElkArte Forum contributors |
||||||
8 | * @license BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file) |
||||||
9 | * |
||||||
10 | * This file contains code covered by: |
||||||
11 | * copyright: 2011 Simple Machines (http://www.simplemachines.org) |
||||||
12 | * |
||||||
13 | * @version 2.0 dev |
||||||
14 | * |
||||||
15 | */ |
||||||
16 | |||||||
17 | namespace ElkArte\Search; |
||||||
18 | |||||||
19 | use ElkArte\Database\AbstractResult; |
||||||
20 | use ElkArte\Database\QueryInterface; |
||||||
21 | use ElkArte\Search\API\Standard; |
||||||
22 | |||||||
23 | /** |
||||||
24 | * Actually do the searches |
||||||
25 | */ |
||||||
26 | class Search |
||||||
27 | { |
||||||
28 | /** @const the forum version but is repeated due to some people rewriting FORUM_VERSION. */ |
||||||
29 | public const FORUM_VERSION = 'ElkArte 2.0 dev'; |
||||||
30 | |||||||
31 | /** @var array */ |
||||||
32 | protected $_participants = []; |
||||||
33 | |||||||
34 | /** @var SearchParams */ |
||||||
35 | protected $_searchParams; |
||||||
36 | |||||||
37 | /** @var SearchArray Holds the words and phrases to be searched on */ |
||||||
38 | private $_searchArray; |
||||||
39 | |||||||
40 | /** @var null|object Holds instance of the search api in use such as \ElkArte\Search\API\Standard_Search */ |
||||||
41 | private $_searchAPI; |
||||||
42 | |||||||
43 | /** @var QueryInterface Database instance */ |
||||||
44 | private $_db; |
||||||
45 | |||||||
46 | /** @var array Builds the array of words for use in the db query */ |
||||||
47 | private $_searchWords = []; |
||||||
48 | |||||||
49 | /** @var array Words excluded from indexes */ |
||||||
50 | private $_excludedIndexWords = []; |
||||||
51 | |||||||
52 | /** @var array Words not to be found in the subject (-word) */ |
||||||
53 | private $_excludedSubjectWords = []; |
||||||
54 | |||||||
55 | /** @var array Phrases not to be found in the search results (-"some phrase") */ |
||||||
56 | private $_excludedPhrases = []; |
||||||
57 | |||||||
58 | /** @var WeightFactors The weights to associate to various areas for relevancy */ |
||||||
59 | private $_weightFactors = []; |
||||||
60 | |||||||
61 | /** @var bool If we are creating a tmp db table */ |
||||||
62 | private $_createTemporary; |
||||||
63 | |||||||
64 | /** @var array common words that we will not index or search for */ |
||||||
65 | private $_blocklist_words = []; |
||||||
66 | |||||||
67 | /** |
||||||
68 | * Constructor |
||||||
69 | * Easy enough, initialize the database objects (generic db and search db) |
||||||
70 | * |
||||||
71 | * @package Search |
||||||
72 | */ |
||||||
73 | public function __construct() |
||||||
74 | { |
||||||
75 | $this->_db = database(); |
||||||
76 | $db_search = db_search(); |
||||||
77 | |||||||
78 | // Create new temporary table(s) (if we can) to store preliminary results in. |
||||||
79 | $db_search->skip_next_error(); |
||||||
80 | |||||||
81 | $this->_createTemporary = $db_search->createTemporaryTable( |
||||||
82 | '{db_prefix}tmp_log_search_messages', |
||||||
83 | [ |
||||||
84 | [ |
||||||
85 | 'name' => 'id_msg', |
||||||
86 | 'type' => 'int', |
||||||
87 | 'size' => 10, |
||||||
88 | 'unsigned' => true, |
||||||
89 | 'default' => 0, |
||||||
90 | ] |
||||||
91 | ], |
||||||
92 | [ |
||||||
93 | [ |
||||||
94 | 'name' => 'id_msg', |
||||||
95 | 'columns' => ['id_msg'], |
||||||
96 | 'type' => 'primary' |
||||||
97 | ] |
||||||
98 | ] |
||||||
99 | ) !== false; |
||||||
100 | |||||||
101 | // Skip the error as it is not uncommon for temp tables to be denied |
||||||
102 | $db_search->skip_next_error(); |
||||||
103 | $db_search->createTemporaryTable('{db_prefix}tmp_log_search_topics', |
||||||
104 | [ |
||||||
105 | [ |
||||||
106 | 'name' => 'id_topic', |
||||||
107 | 'type' => 'mediumint', |
||||||
108 | 'unsigned' => true, |
||||||
109 | 'size' => 8, |
||||||
110 | 'default' => 0 |
||||||
111 | ] |
||||||
112 | ], |
||||||
113 | [ |
||||||
114 | [ |
||||||
115 | 'name' => 'id_topic', |
||||||
116 | 'columns' => ['id_topic'], |
||||||
117 | 'type' => 'primary' |
||||||
118 | 2 | ] |
|||||
119 | ] |
||||||
120 | 2 | ); |
|||||
121 | 2 | } |
|||||
122 | 2 | ||||||
123 | /** |
||||||
124 | 2 | * Returns a search parameter. |
|||||
125 | * |
||||||
126 | 2 | * @param string $name - name of the search parameters |
|||||
127 | 2 | * |
|||||
128 | * @return bool|mixed - the value of the parameter |
||||||
129 | */ |
||||||
130 | 2 | public function param($name) |
|||||
131 | { |
||||||
132 | return $this->_searchParams[$name] ?? false; |
||||||
133 | } |
||||||
134 | |||||||
135 | /** |
||||||
136 | * Sets $this->_searchParams with all the search parameters. |
||||||
137 | */ |
||||||
138 | public function getParams() |
||||||
139 | 2 | { |
|||||
140 | $this->_searchParams->mergeWith([ |
||||||
141 | 'min_msg_id' => $this->_searchParams->_minMsgID, |
||||||
142 | 'max_msg_id' => $this->_searchParams->_maxMsgID, |
||||||
143 | 'memberlist' => $this->_searchParams->_memberlist, |
||||||
144 | 2 | ]); |
|||||
145 | } |
||||||
146 | 2 | ||||||
147 | 2 | /** |
|||||
148 | * Returns the ignored words |
||||||
149 | */ |
||||||
150 | 2 | public function getIgnored() |
|||||
151 | { |
||||||
152 | return $this->_searchArray->getIgnored(); |
||||||
153 | } |
||||||
154 | |||||||
155 | /** |
||||||
156 | * Set the weight factors |
||||||
157 | * |
||||||
158 | * @param WeightFactors $weight |
||||||
159 | 2 | */ |
|||||
160 | public function setWeights($weight) |
||||||
161 | { |
||||||
162 | $this->_weightFactors = $weight; |
||||||
163 | } |
||||||
164 | |||||||
165 | 2 | /** |
|||||
166 | * Set disallowed words etc. |
||||||
167 | * |
||||||
168 | * @param SearchParams $paramObject |
||||||
169 | * @param false $search_simple_fulltext |
||||||
170 | */ |
||||||
171 | public function setParams($paramObject, $search_simple_fulltext = false) |
||||||
172 | { |
||||||
173 | $this->_searchParams = $paramObject; |
||||||
174 | $this->setBlockListedWords(); |
||||||
175 | $this->_searchArray = new SearchArray($this->_searchParams->search, $this->_blocklist_words, $search_simple_fulltext); |
||||||
0 ignored issues
–
show
Bug
Best Practice
introduced
by
![]() |
|||||||
176 | } |
||||||
177 | |||||||
178 | /** |
||||||
179 | * If any block-listed word has been found |
||||||
180 | * |
||||||
181 | * @return bool |
||||||
182 | */ |
||||||
183 | public function foundBlockListedWords() |
||||||
184 | { |
||||||
185 | return $this->_searchArray->foundBlockListedWords(); |
||||||
186 | } |
||||||
187 | |||||||
188 | /** |
||||||
189 | * Returns the block-listed word array |
||||||
190 | * |
||||||
191 | * @return array |
||||||
192 | */ |
||||||
193 | public function getBlockListedWords() |
||||||
194 | { |
||||||
195 | if (empty($this->_blocklist_words)) |
||||||
196 | { |
||||||
197 | $this->setBlockListedWords(); |
||||||
198 | } |
||||||
199 | |||||||
200 | return $this->_blocklist_words; |
||||||
201 | } |
||||||
202 | |||||||
203 | /** |
||||||
204 | * Sets the block-listed word array |
||||||
205 | */ |
||||||
206 | public function setBlockListedWords() |
||||||
207 | { |
||||||
208 | // Unfortunately, searching for words like these is going to result in to many hits, |
||||||
209 | // so we're blocking them. |
||||||
210 | $blocklist_words = ['img', 'url', 'quote', 'www', 'http', 'the', 'is', 'it', 'are', 'if', 'in']; |
||||||
211 | call_integration_hook('integrate_search_blocklist_words', [&$blocklist_words]); |
||||||
212 | |||||||
213 | 2 | $this->_blocklist_words = $blocklist_words; |
|||||
214 | } |
||||||
215 | 2 | ||||||
216 | 2 | /** |
|||||
217 | * Get the search array from the SearchArray object. |
||||||
218 | 2 | * |
|||||
219 | * @return array The search array. |
||||||
220 | 2 | */ |
|||||
221 | public function getSearchArray() |
||||||
222 | { |
||||||
223 | return $this->_searchArray->getSearchArray(); |
||||||
224 | } |
||||||
225 | 2 | ||||||
226 | 2 | /** |
|||||
227 | * Get the list of excluded words. |
||||||
228 | 2 | * |
|||||
229 | 2 | * @return array |
|||||
230 | */ |
||||||
231 | public function getExcludedWords() |
||||||
232 | { |
||||||
233 | return $this->_searchArray->getExcludedWords(); |
||||||
234 | } |
||||||
235 | |||||||
236 | /** |
||||||
237 | * Get the excluded subject words. |
||||||
238 | * |
||||||
239 | * @return array The excluded subject words. |
||||||
240 | */ |
||||||
241 | public function getExcludedSubjectWords() |
||||||
242 | { |
||||||
243 | return $this->_excludedSubjectWords; |
||||||
244 | } |
||||||
245 | |||||||
246 | /** |
||||||
247 | * Returns the search parameters. |
||||||
248 | * |
||||||
249 | * @param bool $array If true returns an array, otherwise an object |
||||||
250 | * |
||||||
251 | * @return SearchParams|string[] |
||||||
252 | */ |
||||||
253 | public function getSearchParams($array = false) |
||||||
254 | { |
||||||
255 | if ($array) |
||||||
256 | { |
||||||
257 | return $this->_searchParams->get(); |
||||||
258 | } |
||||||
259 | |||||||
260 | return $this->_searchParams; |
||||||
261 | } |
||||||
262 | |||||||
263 | 2 | /** |
|||||
264 | * Get the excluded phrases. |
||||||
265 | 2 | * |
|||||
266 | * @return array The excluded phrases. |
||||||
267 | */ |
||||||
268 | public function getExcludedPhrases() |
||||||
269 | { |
||||||
270 | return $this->_excludedPhrases; |
||||||
271 | 2 | } |
|||||
272 | |||||||
273 | /** |
||||||
274 | * Tell me, do I want to see the full message or just a piece? |
||||||
275 | */ |
||||||
276 | public function isCompact() |
||||||
277 | { |
||||||
278 | return empty($this->_searchParams['show_complete']); |
||||||
279 | } |
||||||
280 | |||||||
281 | /** |
||||||
282 | * Wrapper around SearchParams::compileURL |
||||||
283 | * |
||||||
284 | * @param array $search build param index with specific search term (did you mean?) |
||||||
285 | * |
||||||
286 | * @return string - the encoded string to be appended to the URL |
||||||
287 | */ |
||||||
288 | public function compileURLparams($search = []) |
||||||
289 | { |
||||||
290 | return $this->_searchParams->compileURL($search); |
||||||
291 | } |
||||||
292 | |||||||
293 | /** |
||||||
294 | * Finds the posters of the messages |
||||||
295 | 2 | * |
|||||
296 | * @param int[] $msg_list - All the messages we want to find the posters |
||||||
297 | 2 | * @param int $limit - There are only so many topics |
|||||
298 | * |
||||||
299 | * @return int[] - array of members id |
||||||
300 | */ |
||||||
301 | public function loadPosters($msg_list, $limit) |
||||||
302 | { |
||||||
303 | // Load the posters... |
||||||
304 | $posters = []; |
||||||
305 | $this->_db->fetchQuery(' |
||||||
306 | SELECT |
||||||
307 | id_member |
||||||
308 | FROM {db_prefix}messages |
||||||
309 | WHERE id_member != {int:no_member} |
||||||
310 | AND id_msg IN ({array_int:message_list}) |
||||||
311 | LIMIT {int:limit}', |
||||||
312 | [ |
||||||
313 | 'message_list' => $msg_list, |
||||||
314 | 'limit' => $limit, |
||||||
315 | 'no_member' => 0, |
||||||
316 | ] |
||||||
317 | )->fetch_callback( |
||||||
318 | static function ($row) use (&$posters) { |
||||||
319 | $posters[] = (int) $row['id_member']; |
||||||
320 | } |
||||||
321 | ); |
||||||
322 | |||||||
323 | return $posters; |
||||||
324 | } |
||||||
325 | |||||||
326 | /** |
||||||
327 | * Finds the posters of the messages |
||||||
328 | * |
||||||
329 | * @param int[] $msg_list - All the messages we want to find the posters |
||||||
330 | * @param int $limit - There are only so many topics |
||||||
331 | * |
||||||
332 | * @return bool|AbstractResult |
||||||
333 | */ |
||||||
334 | public function loadMessagesRequest($msg_list, $limit) |
||||||
335 | { |
||||||
336 | global $modSettings; |
||||||
337 | |||||||
338 | return $this->_db->query('', ' |
||||||
339 | SELECT |
||||||
340 | m.id_msg, m.subject, m.poster_name, m.poster_email, m.poster_time, m.id_member, m.icon, m.poster_ip, |
||||||
341 | m.body, m.smileys_enabled, m.modified_time, m.modified_name, first_m.id_msg AS id_first_msg, |
||||||
342 | first_m.subject AS first_subject, first_m.icon AS first_icon, first_m.poster_time AS first_poster_time, |
||||||
343 | first_mem.id_member AS first_id_member, |
||||||
344 | COALESCE(first_mem.real_name, first_m.poster_name) AS first_display_name, |
||||||
345 | COALESCE(first_mem.member_name, first_m.poster_name) AS first_member_name, |
||||||
346 | last_m.id_msg AS id_last_msg, last_m.poster_time AS last_poster_time, last_mem.id_member AS last_id_member, |
||||||
347 | COALESCE(last_mem.real_name, last_m.poster_name) AS last_display_name, |
||||||
348 | COALESCE(last_mem.member_name, last_m.poster_name) AS last_member_name, |
||||||
349 | last_m.icon AS last_icon, last_m.subject AS last_subject, |
||||||
350 | t.id_topic, t.is_sticky, t.locked, t.id_poll, t.num_replies, t.num_views, t.num_likes, |
||||||
351 | b.id_board, b.name AS bname, c.id_cat, c.name AS cat_name |
||||||
352 | FROM {db_prefix}messages AS m |
||||||
353 | INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic) |
||||||
354 | INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board) |
||||||
355 | INNER JOIN {db_prefix}categories AS c ON (c.id_cat = b.id_cat) |
||||||
356 | INNER JOIN {db_prefix}messages AS first_m ON (first_m.id_msg = t.id_first_msg) |
||||||
357 | INNER JOIN {db_prefix}messages AS last_m ON (last_m.id_msg = t.id_last_msg) |
||||||
358 | LEFT JOIN {db_prefix}members AS first_mem ON (first_mem.id_member = first_m.id_member) |
||||||
359 | LEFT JOIN {db_prefix}members AS last_mem ON (last_mem.id_member = first_m.id_member) |
||||||
360 | WHERE m.id_msg IN ({array_int:message_list})' . ($modSettings['postmod_active'] ? ' |
||||||
361 | AND m.approved = {int:is_approved}' : '') . ' |
||||||
362 | ORDER BY FIND_IN_SET(m.id_msg, {string:message_list_in_set}) |
||||||
363 | LIMIT {int:limit}', |
||||||
364 | [ |
||||||
365 | 'message_list' => $msg_list, |
||||||
366 | 'is_approved' => 1, |
||||||
367 | 'message_list_in_set' => implode(',', $msg_list), |
||||||
368 | 'limit' => $limit, |
||||||
369 | ] |
||||||
370 | ); |
||||||
371 | } |
||||||
372 | |||||||
373 | /** |
||||||
374 | * Did the user find any message at all? |
||||||
375 | * |
||||||
376 | * @param AbstractResult $messages_request holds a query result |
||||||
377 | * |
||||||
378 | * @return bool |
||||||
379 | */ |
||||||
380 | public function noMessages($messages_request) |
||||||
381 | { |
||||||
382 | return $messages_request->num_rows() === 0; |
||||||
383 | } |
||||||
384 | |||||||
385 | /** |
||||||
386 | * Sets the query, calls the searchQuery method of the API in use |
||||||
387 | * |
||||||
388 | * @param Standard $searchAPI |
||||||
389 | * @return array |
||||||
390 | */ |
||||||
391 | public function searchQuery($searchAPI) |
||||||
392 | { |
||||||
393 | $this->_searchAPI = $searchAPI; |
||||||
394 | $searchAPI->setExcludedPhrases($this->_excludedPhrases); |
||||||
395 | $searchAPI->setWeightFactors($this->_weightFactors); |
||||||
396 | $searchAPI->useTemporary($this->_createTemporary); |
||||||
397 | $searchAPI->setSearchArray($this->_searchArray); |
||||||
398 | if ($searchAPI->supportsExtended()) |
||||||
399 | { |
||||||
400 | return $searchAPI->searchQuery($this->_searchArray->getSearchArray(), $this->_excludedIndexWords, $this->_participants); |
||||||
401 | } |
||||||
402 | |||||||
403 | 2 | return $searchAPI->searchQuery($this->searchWords(), $this->_excludedIndexWords, $this->_participants); |
|||||
404 | } |
||||||
405 | 2 | ||||||
406 | 2 | /** |
|||||
407 | 2 | * Builds the array of words for the query |
|||||
408 | 2 | */ |
|||||
409 | 2 | public function searchWords() |
|||||
410 | { |
||||||
411 | 2 | global $modSettings, $context; |
|||||
412 | 2 | ||||||
413 | 2 | if (count($this->_searchWords) > 0) |
|||||
414 | 2 | { |
|||||
415 | 2 | return $this->_searchWords; |
|||||
416 | } |
||||||
417 | |||||||
418 | $orParts = []; |
||||||
419 | $this->_searchWords = []; |
||||||
420 | $searchArray = $this->_searchArray->getSearchArray(); |
||||||
421 | $excludedWords = $this->_searchArray->getExcludedWords(); |
||||||
422 | 2 | ||||||
423 | // All words/sentences must match. |
||||||
424 | 2 | if (!empty($searchArray) && empty($this->_searchParams['searchtype'])) |
|||||
425 | { |
||||||
426 | 2 | $orParts[0] = $searchArray; |
|||||
427 | } |
||||||
428 | // Any word/sentence must match. |
||||||
429 | else |
||||||
430 | { |
||||||
431 | 2 | foreach ($searchArray as $index => $value) |
|||||
432 | 2 | { |
|||||
433 | 2 | $orParts[$index] = [$value]; |
|||||
434 | 2 | } |
|||||
435 | } |
||||||
436 | |||||||
437 | 2 | // Make sure the excluded words are in all or-branches. |
|||||
438 | foreach (array_keys($orParts) as $orIndex) |
||||||
439 | 2 | { |
|||||
440 | foreach ($excludedWords as $word) |
||||||
441 | { |
||||||
442 | $orParts[$orIndex][] = $word; |
||||||
443 | } |
||||||
444 | } |
||||||
445 | |||||||
446 | // Determine the or-branches and the fulltext search words. |
||||||
447 | foreach (array_keys($orParts) as $orIndex) |
||||||
448 | { |
||||||
449 | $this->_searchWords[$orIndex] = [ |
||||||
450 | 'indexed_words' => [], |
||||||
451 | 2 | 'words' => [], |
|||||
452 | 'subject_words' => [], |
||||||
453 | 2 | 'all_words' => [], |
|||||
454 | 'complex_words' => [], |
||||||
455 | 1 | ]; |
|||||
456 | |||||||
457 | $this->_searchAPI->setExcludedWords($excludedWords); |
||||||
0 ignored issues
–
show
The method
setExcludedWords() does not exist on null .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces. This is most likely a typographical error or the method has been renamed. ![]() |
|||||||
458 | |||||||
459 | // Sort the indexed words (large words -> small words -> excluded words). |
||||||
460 | 2 | usort($orParts[$orIndex], [$this->_searchAPI, 'searchSort']); |
|||||
461 | |||||||
462 | 2 | foreach ($orParts[$orIndex] as $word) |
|||||
463 | { |
||||||
464 | $is_excluded = in_array($word, $excludedWords, true); |
||||||
465 | $this->_searchWords[$orIndex]['all_words'][] = $word; |
||||||
466 | $subjectWords = text2words($word); |
||||||
467 | |||||||
468 | if (!$is_excluded || count($subjectWords) === 1) |
||||||
469 | { |
||||||
470 | 2 | $this->_searchWords[$orIndex]['subject_words'] = array_merge($this->_searchWords[$orIndex]['subject_words'], $subjectWords); |
|||||
471 | |||||||
472 | 2 | if ($is_excluded) |
|||||
473 | { |
||||||
474 | 2 | $this->_excludedSubjectWords = array_merge($this->_excludedSubjectWords, $subjectWords); |
|||||
475 | } |
||||||
476 | 2 | } |
|||||
477 | 2 | else |
|||||
478 | 2 | { |
|||||
479 | $this->_excludedPhrases[] = $word; |
||||||
480 | 2 | } |
|||||
481 | |||||||
482 | 2 | // Have we got indexes to prepare? |
|||||
483 | $this->_searchAPI->prepareIndexes($word, $this->_searchWords[$orIndex], $this->_excludedIndexWords, $is_excluded, $this->_excludedSubjectWords); |
||||||
484 | 2 | } |
|||||
485 | |||||||
486 | 2 | // Search_force_index requires all AND parts to have at least one fulltext word. |
|||||
487 | if (!empty($modSettings['search_force_index']) && empty($this->_searchWords[$orIndex]['indexed_words'])) |
||||||
488 | { |
||||||
489 | $context['search_errors']['query_not_specific_enough'] = true; |
||||||
490 | break; |
||||||
491 | } |
||||||
492 | |||||||
493 | if ($this->_searchParams->subject_only && empty($this->_searchWords[$orIndex]['subject_words']) && empty($this->_excludedSubjectWords)) |
||||||
0 ignored issues
–
show
The property
subject_only does not exist on ElkArte\Search\SearchParams . Since you implemented __get , consider adding a @property annotation.
![]() |
|||||||
494 | { |
||||||
495 | 2 | $context['search_errors']['query_not_specific_enough'] = true; |
|||||
496 | break; |
||||||
497 | } |
||||||
498 | |||||||
499 | 2 | // Make sure we aren't searching for too many indexed words. |
|||||
500 | $this->_searchWords[$orIndex]['indexed_words'] = array_slice($this->_searchWords[$orIndex]['indexed_words'], 0, 7); |
||||||
501 | $this->_searchWords[$orIndex]['subject_words'] = array_slice($this->_searchWords[$orIndex]['subject_words'], 0, 7); |
||||||
502 | $this->_searchWords[$orIndex]['words'] = array_slice($this->_searchWords[$orIndex]['words'], 0, 4); |
||||||
503 | } |
||||||
504 | 2 | ||||||
505 | return $this->_searchWords; |
||||||
506 | } |
||||||
507 | |||||||
508 | /** |
||||||
509 | * Returns the number of results obtained from the query. |
||||||
510 | * |
||||||
511 | * @return int |
||||||
512 | 2 | */ |
|||||
513 | 2 | public function getNumResults() |
|||||
514 | 2 | { |
|||||
515 | return $this->_searchAPI->getNumResults(); |
||||||
516 | } |
||||||
517 | |||||||
518 | 2 | /** |
|||||
519 | * Get the participants of the event. |
||||||
520 | * |
||||||
521 | * @return array The participants of the event. |
||||||
522 | */ |
||||||
523 | public function getParticipants() |
||||||
524 | { |
||||||
525 | return $this->_participants; |
||||||
526 | } |
||||||
527 | } |
||||||
528 |