CWIS Developer Documentation

- Home
- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

SearchParameterSet.php
Go to the documentation of this file.
1 <?PHP
2 #
3 # FILE: SearchParameterSet.php
4 #
5 # Part of the ScoutLib application support library
6 # Copyright 2015 Edward Almasy and Internet Scout Research Group
7 # http://scout.wisc.edu
8 #
9
13 class SearchParameterSet {
14
15 # ---- SETUP / CONFIGURATION ---------------------------------------------
16 /*@(*/
17
25 function __construct($Data = NULL)
26 {
27 # if set data supplied
28 if ($Data !== NULL)
29 {
30 # set internal values from data
31 $this->LoadFromData($Data);
32 }
33 }
34
42 static function SetCanonicalFieldFunction($Func)
43 {
44 if (is_callable($Func))
45 {
46 self::$CanonicalFieldFunction = $Func;
47 }
48 else
49 {
50 throw new InvalidArgumentException("Invalid function supplied.");
51 }
52 }
53
61 static function SetPrintableFieldFunction($Func)
62 {
63 if (is_callable($Func))
64 {
65 self::$PrintableFieldFunction = $Func;
66 }
67 else
68 {
69 throw new InvalidArgumentException("Invalid function supplied.");
70 }
71 }
72
78 /*@)*/
79 # ---- SET CONSTRUCTION ---------------------------------------------------
80 /*@(*/
81
92 function AddParameter($SearchStrings, $Field = NULL)
93 {
94 # normalize field value if supplied
95 if (($Field !== NULL) && isset(self::$CanonicalFieldFunction))
96 {
97 $Field = call_user_func(self::$CanonicalFieldFunction, $Field);
98 }
99
100 # make sure search strings are an array
101 if (!is_array($SearchStrings))
102 { $SearchStrings = array($SearchStrings); }
103
104 # for each search string
105 foreach ($SearchStrings as $String)
106 {
107 # if field specified
108 if ($Field !== NULL)
109 {
110 # add strings to search values for field
111 $this->SearchStrings[$Field][] = $String;
112 }
113 else
114 {
115 # add strings to keyword search values
116 $this->KeywordSearchStrings[] = $String;
117 }
118 }
119 }
120
127 function Logic($NewValue = NULL)
128 {
129 # if new value supplied
130 if ($NewValue !== NULL)
131 {
132 # normalize value
133 $NormValue = strtoupper($NewValue);
134
135 # error out if value appears invalid
136 if (($NormValue !== "AND") && ($NormValue !== "OR"))
137 {
138 throw new InvalidArgumentException("New logic setting"
139 ." is invalid (".$NewValue.").");
140 }
141
142 # save new setting
143 $this->Logic = $NormValue;
144 }
145
146 # return current logic setting to caller
147 return $this->Logic;
148 }
149
154 function AddSet(SearchParameterSet $Set)
155 {
156 # add subgroup to privilege set
157 $this->Subgroups[] = $Set;
158 }
159
160
161 /*@)*/
162 # ---- DATA TRANSLATION ---------------------------------------------------
163 /*@(*/
164
175 function Data($NewValue = NULL)
176 {
177 # if new data supplied
178 if ($NewValue !== NULL)
179 {
180 # unpack set data and load
181 $this->LoadFromData($NewValue);
182 }
183
184 # serialize current data and return to caller
185 $Data = array();
186 if ($this->Logic !== "AND") { $Data["Logic"] = $this->Logic; }
187 if (count($this->SearchStrings))
188 { $Data["SearchStrings"] = $this->SearchStrings; }
189 if (count($this->KeywordSearchStrings))
190 {
191 $Data["KeywordSearchStrings"] = $this->KeywordSearchStrings;
192 }
193 if (count($this->Subgroups))
194 {
195 foreach ($this->Subgroups as $Subgroup)
196 {
197 $Data["Subgroups"][] = $Subgroup->Data();
198 }
199 }
200 return serialize($Data);
201 }
202
203 /*
204 * Get/set search parameter set, in the form of URL parameters.
205 * @param string $NewValue New parameter set in the form of a URL
206 * parameter string. (OPTIONAL)
207 * @return array URL parameter values, with parameter names for the index..
208 */
209 function UrlParameters($NewValue = NULL)
210 {
211 # if new value supplied
212 if ($NewValue !== NULL)
213 {
214 # set new parameters
215 $this->SetFromUrlParameters($NewValue);
216 }
217
218 # get existing search parameters as URL parameters
219 $Params = $this->GetAsUrlParameters();
220
221 # sort parameters by parameter name to normalize result
222 ksort($Params);
223
224 # return parameters to caller
225 return $Params;
226 }
227
228 /*
229 * Get/set search parameter set, in the form of an URL parameter string.
230 * @param string $NewValue New parameter set in the form of a URL
231 * parameter string. (OPTIONAL)
232 * @return string URL parameter string.
233 */
234 function UrlParameterString($NewValue = NULL)
235 {
236 # get/set parameters
237 $Params = $this->UrlParameters($NewValue);
238
239 # combine values into string
240 $ParamString = "";
241 $Separator = "";
242 foreach ($Params as $Index => $Value)
243 {
244 $ParamString .= $Separator.$Index."=".urlencode($Value);
245 $Separator = "&";
246 }
247
248 # return string to caller
249 return $ParamString;
250 }
251
252 /*
253 * Get text description of search parameter set.
254 * @param bool $IncludeHtml Whether to include HTML tags for formatting.
255 * (OPTIONAL, defaults to TRUE)
256 * @param bool $StartWithBreak Whether to start string with BR tag.
257 * (OPTIONAL, defaults to TRUE)
258 * @param int $TruncateLongWordsTo Number of characters to truncate long
259 * words to (use 0 for no truncation). (OPTIONAL, defaults to 0)
260 * @param string $Indent (for internal (recursive) use only)
261 * @return string Text description of search parameters.
262 */
263 function TextDescription($IncludeHtml = TRUE, $StartWithBreak = TRUE,
264 $TruncateLongWordsTo = 0, $Indent = "")
265 {
266 # define list of phrases used to represent logical operators
267 $OperatorPhrases = array(
268 "=" => "is",
269 "==" => "is",
270 ">" => "is greater than",
271 "<" => "is less than",
272 ">=" => "is at least",
273 "<=" => "is no more than",
274 "!" => "is not",
275 "!=" => "is not",
276 );
277
278 # set characters used to indicate literal strings
279 $LiteralStart = $IncludeHtml ? "<i>" : "\"";
280 $LiteralEnd = $IncludeHtml ? "</i>" : "\"";
281 $LiteralBreak = $IncludeHtml ? "<br>\n" : "\n";
282 $Indent .= $IncludeHtml ? "&nbsp;&nbsp;&nbsp;&nbsp;" : " ";
283
284 # for each keyword search string
285 $Descriptions = array();
286 foreach ($this->KeywordSearchStrings as $SearchString)
287 {
288 # escape search string if appropriate
289 if ($IncludeHtml)
290 {
291 $SearchString = defaulthtmlentities($SearchString);
292 }
293
294 # add string to list of descriptions
295 $Descriptions[] = $LiteralStart.$SearchString.$LiteralEnd;
296 }
297
298 # for each field with search strings
299 foreach ($this->SearchStrings as $FieldId => $SearchStrings)
300 {
301 # retrieve field name
302 $FieldName = call_user_func(self::$PrintableFieldFunction, $FieldId);
303
304 # for each search string
305 foreach ($SearchStrings as $SearchString)
306 {
307 # extract operator from search string
308 $MatchResult = preg_match("/^([=><!]+)(.+)/",
309 $SearchString, $Matches);
310
311 # determine operator phrase
312 if (($MatchResult == 1) && isset($OperatorPhrases[$Matches[1]]))
313 {
314 $OpPhrase = $OperatorPhrases[$Matches[1]];
315 $SearchString = $Matches[2];
316 }
317 else
318 {
319 $OpPhrase = "contains";
320 }
321
322 # escape field name and search string if appropriate
323 if ($IncludeHtml)
324 {
325 $FieldName = defaulthtmlentities($FieldName);
326 $SearchString = defaulthtmlentities($SearchString);
327 }
328
329 # assemble field and operator and value into description
330 $Descriptions[] = $FieldName." ".$OpPhrase." "
331 .$LiteralStart.$SearchString.$LiteralEnd;
332 }
333 }
334
335 # for each subgroup
336 foreach ($this->Subgroups as $Subgroup)
337 {
338 # retrieve description for subgroup
339 $Descriptions[] = "(".$Subgroup->TextDescription($IncludeHtml,
340 $StartWithBreak, $TruncateLongWordsTo, $Indent).")";
341 }
342
343 # join descriptions with appropriate conjunction
344 $Descrip = join($LiteralBreak.$Indent." ".strtolower($this->Logic)." ",
345 $Descriptions);
346
347 # if caller requested that long words be truncated
348 if ($TruncateLongWordsTo > 4)
349 {
350 # break description into words
351 $Words = explode(" ", $Descrip);
352
353 # for each word
354 $NewDescrip = "";
355 foreach ($Words as $Word)
356 {
357 # if word is longer than specified length
358 if (strlen(strip_tags($Word)) > $TruncateLongWordsTo)
359 {
360 # truncate word and add ellipsis
361 $Word = NeatlyTruncateString($Word, $TruncateLongWordsTo - 3);
362 }
363
364 # add word to new description
365 $NewDescrip .= " ".$Word;
366 }
367
368 # set description to new description
369 $Descrip = $NewDescrip;
370 }
371
372 # return description to caller
373 return $Descrip;
374 }
375
376
377 /*@)*/
378 # ---- BACKWARD COMPATIBILITY ---------------------------------------------
379 /*@(*/
380
389 function GetAsLegacyArray()
390 {
391 # set logic for search group
392 $Group["Logic"] = ($this->Logic == "OR")
393 ? SearchEngine::LOGIC_OR : SearchEngine::LOGIC_AND;
394
395 # for each set of search strings
396 foreach ($this->SearchStrings as $Field => $Strings)
397 {
398 # get text name of field
399 $FieldName = call_user_func(self::$PrintableFieldFunction, $Field);
400
401 # add set to group
402 $Group["SearchStrings"][$FieldName] = $Strings;
403 }
404
405 # for each keyword search string
406 foreach ($this->KeywordSearchStrings as $String)
407 {
408 # add string to keyword entry in group
409 $Group["SearchStrings"]["XXXKeywordXXX"][] = $String;
410 }
411
412 # add group to array
413 $Legacy[] = $Group;
414
415 # for each subgroup
416 foreach ($this->Subgroups as $Subgroup)
417 {
418 # retrieve legacy array for subgroup
419 $SubLegacy = $Subgroup->GetAsLegacyArray();
420
421 # add groups from legacy array to our array
422 $Legacy = array_merge($Legacy, $SubLegacy);
423 }
424
425 # set logic for whole array
426 $Legacy["Logic"] = $Group["Logic"];
427
428 # return array to caller
429 return $Legacy;
430 }
431
432
433 /*@)*/
434 # ---- PRIVATE INTERFACE -------------------------------------------------
435
436 private $KeywordSearchStrings = array();
437 private $Logic = self::DEFAULT_LOGIC;
438 private $SearchStrings = array();
439 private $Subgroups = array();
440
441 static private $CanonicalFieldFunction;
442 static private $PrintableFieldFunction;
443 static private $UrlParameterPrefix = "F";
444
445 const DEFAULT_LOGIC = "AND";
446 const URL_KEYWORDFREE_RANGE = "A-JL-Z";
447 const URL_KEYWORD_INDICATOR = "K";
448 const URL_LOGIC_INDICATOR = "00";
449
455 private function LoadFromData($Serialized)
456 {
457 # unpack new data
458 $Data = unserialize($Serialized);
459 if (!is_array($Data))
460 {
461 throw new InvalidArgumentException("Incoming set data"
462 ." appears invalid.");
463 }
464
465 # load logic
466 $this->Logic = isset($Data["Logic"]) ? $Data["Logic"] : "AND";
467
468 # load search strings
469 $this->SearchStrings = isset($Data["SearchStrings"])
470 ? $Data["SearchStrings"] : array();
471 $this->KeywordSearchStrings = isset($Data["KeywordSearchStrings"])
472 ? $Data["KeywordSearchStrings"] : array();
473
474 # load any subgroups
475 $this->Subgroups = array();
476 if (isset($Data["Subgroups"]))
477 {
478 foreach ($Data["Subgroups"] as $SubgroupData)
479 {
480 $this->Subgroups[] = new SearchParameterSet($SubgroupData);
481 }
482 }
483 }
484
485 /*
486 * Get the search set parameter set as an URL parameter string.
487 * @param string $SetPrefix Prefix to use after the URL parameter prefix.
488 * @return array Array of URL parameters.
489 */
490 private function GetAsUrlParameters($SetPrefix = "")
491 {
492 # for each search string group in set
493 $Params = array();
494 foreach ($this->SearchStrings as $FieldId => $Values)
495 {
496 # get numeric version of field ID if not already numeric
497 if (!is_numeric($FieldId))
498 {
499 $FieldId = call_user_func(self::$CanonicalFieldFunction, $FieldId);
500 }
501
502 # for each search string in group
503 $ParamSuffix = "";
504 foreach ($Values as $Value)
505 {
506 # check for too many search strings for this field
507 if ($ParamSuffix == "Z")
508 {
509 throw new Exception("Maximum search parameter complexity"
510 ." exceeded: more than 26 search parameters for"
511 ." field ID ".$FieldId.".");
512 }
513
514 # add search string to URL
515 $Params[self::$UrlParameterPrefix.$SetPrefix
516 .$FieldId.$ParamSuffix] = $Value;
517 $ParamSuffix = ($ParamSuffix == "") ? "A"
518 : chr(ord($ParamSuffix) + 1);
519 }
520 }
521
522 # for each keyword search string
523 $ParamSuffix = "";
524 foreach ($this->KeywordSearchStrings as $Value)
525 {
526 # check for too many keyword search strings
527 if ($ParamSuffix == "Z")
528 {
529 throw new Exception("Maximum search parameter complexity"
530 ." exceeded: more than 26 keyword search parameters.");
531 }
532
533 # add search string to URL
534 $Params[self::$UrlParameterPrefix.$SetPrefix
535 .self::URL_KEYWORD_INDICATOR.$ParamSuffix] = $Value;
536 $ParamSuffix = ($ParamSuffix == "") ? "A"
537 : chr(ord($ParamSuffix) + 1);
538 }
539
540 # add logic if not default
541 if ($this->Logic != self::DEFAULT_LOGIC)
542 {
543 $Params[self::$UrlParameterPrefix.$SetPrefix
544 .self::URL_LOGIC_INDICATOR] = $this->Logic;
545 }
546
547 # for each search parameter subgroup
548 $SetLetter = "A";
549 foreach ($this->Subgroups as $Subgroup)
550 {
551 # check for too many subgroups
552 if ($SetLetter == "Z")
553 {
554 throw new Exception("Maximum search parameter complexity"
555 ." exceeded: more than 24 search parameter subgroups.");
556 }
557
558 # retrieve URL string for subgroup and add it to URL
559 $Params = array_merge($Params, $Subgroup->GetAsUrlParameters(
560 $SetPrefix.$SetLetter));
561
562 # move to next set letter
563 $SetLetter = ($SetLetter == chr(ord(self::URL_KEYWORD_INDICATOR) - 1))
564 ? chr(ord(self::URL_KEYWORD_INDICATOR) + 1)
565 : chr(ord($SetLetter) + 1);
566 }
567
568 # return constructed URL parameter string to caller
569 return $Params;
570 }
571
572 /*
573 * Set search parameter from URL parameters in the same format as
574 * produced by GetAsUrlParameters().
575 * @param string $UrlParameter URL parameter string or array.
576 * @see SearchParameterSet::GetAsUrlParameters()
577 */
578 private function SetFromUrlParameters($UrlParameters)
579 {
580 # if string was passed in
581 if (is_string($UrlParameters))
582 {
583 # split string into parameter array
584 $Params = explode("&", $UrlParameters);
585
586 # pare down parameter array to search parameter elements
587 # and strip off search parameter prefix
588 $NewUrlParameters = array();
589 foreach ($Params as $Param)
590 {
591 if (strpos($Param, self::$UrlParameterPrefix) === 0)
592 {
593 list($Index, $Value) = explode("=", $Param);
594 $NewUrlParameters[$Index] = urldecode($Value);
595 }
596 }
597 $UrlParameters = $NewUrlParameters;
598 }
599
600 # for each search parameter
601 foreach ($UrlParameters as $ParamName => $SearchString)
602 {
603 # strip off standard search parameter prefix
604 $ParamName = substr($ParamName, strlen(self::$UrlParameterPrefix));
605
606 # split parameter into component parts
607 $SplitResult = preg_match("/^([".self::URL_KEYWORDFREE_RANGE."]*)"
608 ."([0-9".self::URL_KEYWORD_INDICATOR."]+)([A-Z]*)$/",
609 $ParamName, $Matches);
610
611 # if split was successful
612 if ($SplitResult === 1)
613 {
614 # pull components from split pieces
615 $SetPrefix = $Matches[1];
616 $FieldId = $Matches[2];
617 $ParamSuffix = $Matches[3];
618
619 # if set prefix indicates parameter is part of our set
620 if ($SetPrefix == "")
621 {
622 switch ($FieldId)
623 {
624 case self::URL_LOGIC_INDICATOR:
625 # set logic
626 $this->Logic($SearchString);
627 break;
628
629 case self::URL_KEYWORD_INDICATOR:
630 # add string to keyword searches
631 $this->KeywordSearchStrings[] = $SearchString;
632 break;
633
634 default:
635 # add string to searches for appropriate field
636 $this->SearchStrings[$FieldId][] = $SearchString;
637 break;
638 }
639 }
640 else
641 {
642 # add parameter to array for subgroup
643 $SubgroupIndex = $SetPrefix[0];
644 $SubgroupPrefix = (strlen($SetPrefix) > 1)
645 ? substr($SetPrefix, 1) : "";
646 $SubgroupParamIndex = self::$UrlParameterPrefix
647 .$SubgroupPrefix.$FieldId.$ParamSuffix;
648 $SubgroupParameters[$SubgroupIndex][$SubgroupParamIndex]
649 = $SearchString;
650 }
651 }
652 }
653
654 # if subgroups were found
655 if (isset($SubgroupParameters))
656 {
657 # for each identified subgroup
658 foreach ($SubgroupParameters as $SubgroupIndex => $Parameters)
659 {
660 # create subgroup and set parameters
661 $Subgroup = new SearchParameterSet();
662 $Subgroup->SetFromUrlParameters($Parameters);
663
664 # add subgroup to our set
665 $this->Subgroups[] = $Subgroup;
666 }
667 }
668 }
669 }
SearchParameterSet\TextDescription
TextDescription($IncludeHtml=TRUE, $StartWithBreak=TRUE, $TruncateLongWordsTo=0, $Indent="")
Definition: SearchParameterSet.php:263
SearchParameterSet\Data
Data($NewValue=NULL)
Get/set search parameter set data, in the form of an opaque string.
Definition: SearchParameterSet.php:175
SearchParameterSet\SetCanonicalFieldFunction
static SetCanonicalFieldFunction($Func)
Register function used to retrieve a canonical value for a field.
Definition: SearchParameterSet.php:42
SearchParameterSet
Set of parameters used to perform a search.
Definition: SearchParameterSet.php:13
SearchParameterSet\AddSet
AddSet(SearchParameterSet $Set)
Add subgroup of search parameters to set.
Definition: SearchParameterSet.php:154
SearchParameterSet\UrlParameterString
UrlParameterString($NewValue=NULL)
Definition: SearchParameterSet.php:234
SearchParameterSet\UrlParameters
UrlParameters($NewValue=NULL)
Definition: SearchParameterSet.php:209
SearchParameterSet\URL_KEYWORD_INDICATOR
const URL_KEYWORD_INDICATOR
Definition: SearchParameterSet.php:447
SearchParameterSet\URL_KEYWORDFREE_RANGE
const URL_KEYWORDFREE_RANGE
Definition: SearchParameterSet.php:446
SearchParameterSet\Logic
Logic($NewValue=NULL)
Get/set logic for set.
Definition: SearchParameterSet.php:127
SearchEngine\LOGIC_OR
const LOGIC_OR
Definition: SearchEngine.php:19
SearchParameterSet\SetPrintableFieldFunction
static SetPrintableFieldFunction($Func)
Register function used to retrieve a printable value for a field.
Definition: SearchParameterSet.php:61
SearchEngine\LOGIC_AND
const LOGIC_AND
Definition: SearchEngine.php:18
PHP
PHP
Definition: RSSClient--Test.php:12
SearchParameterSet\AddParameter
AddParameter($SearchStrings, $Field=NULL)
Add search parameter to set.
Definition: SearchParameterSet.php:92
SearchParameterSet\GetAsLegacyArray
GetAsLegacyArray()
Retrieve search parameters in legacy array format.
Definition: SearchParameterSet.php:389
SearchParameterSet\__construct
__construct($Data=NULL)
Class constructor, used to create a new set or reload an existing set from previously-constructed dat...
Definition: SearchParameterSet.php:25
SearchParameterSet\DEFAULT_LOGIC
const DEFAULT_LOGIC
Definition: SearchParameterSet.php:445
SearchParameterSet\URL_LOGIC_INDICATOR
const URL_LOGIC_INDICATOR
Definition: SearchParameterSet.php:448