CWIS Developer Documentation
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-2016 Edward Almasy and Internet Scout Research Group
7 # http://scout.wisc.edu
8 #
9 
14 {
15 
16  # ---- SETUP / CONFIGURATION ---------------------------------------------
17  /*@(*/
18 
26  public function __construct($Data = NULL)
27  {
28  # if set data supplied
29  if ($Data !== NULL)
30  {
31  # set internal values from data
32  $this->LoadFromData($Data);
33  }
34  }
35 
40  public function __clone()
41  {
42  foreach ($this->Subgroups as &$Group)
43  {
44  $Group = clone $Group;
45  }
46  }
47 
55  public static function SetCanonicalFieldFunction($Func)
56  {
57  if (is_callable($Func))
58  {
59  self::$CanonicalFieldFunction = $Func;
60  }
61  else
62  {
63  throw new InvalidArgumentException("Invalid function supplied.");
64  }
65  }
66 
74  public static function SetPrintableFieldFunction($Func)
75  {
76  if (is_callable($Func))
77  {
78  self::$PrintableFieldFunction = $Func;
79  }
80  else
81  {
82  throw new InvalidArgumentException("Invalid function supplied.");
83  }
84  }
85 
91  /*@)*/
92  # ---- SET CONSTRUCTION ---------------------------------------------------
93  /*@(*/
94 
105  public function AddParameter($SearchStrings, $Field = NULL)
106  {
107  # normalize field value if supplied
108  $Field = self::NormalizeField($Field);
109 
110  # make sure search strings are an array
111  if (!is_array($SearchStrings))
112  { $SearchStrings = array($SearchStrings); }
113 
114  # for each search string
115  foreach ($SearchStrings as $String)
116  {
117  # if field specified
118  if ($Field !== NULL)
119  {
120  # add strings to search values for field
121  $this->SearchStrings[$Field][] = $String;
122  }
123  else
124  {
125  # add strings to keyword search values
126  $this->KeywordSearchStrings[] = $String;
127  }
128  }
129  }
130 
139  public function RemoveParameter($SearchStrings, $Field = NULL)
140  {
141  # normalize field value if supplied
142  $Field = self::NormalizeField($Field);
143 
144  # if search strings specified
145  if ($SearchStrings != NULL)
146  {
147  # make sure search strings are an array
148  if (!is_array($SearchStrings))
149  { $SearchStrings = array($SearchStrings); }
150 
151  # for each search string
152  foreach ($SearchStrings as $String)
153  {
154  # if field specified
155  if ($Field !== NULL)
156  {
157  # if there are search parameters for this field
158  if (isset($this->SearchStrings[$Field]))
159  {
160  # remove any matching search parameters
161  $NewSearchStrings = array();
162  foreach ($this->SearchStrings[$Field] as $Value)
163  {
164  if ($Value != $String)
165  {
166  $NewSearchStrings[] = $Value;
167  }
168  }
169  if (count($NewSearchStrings))
170  {
171  $this->SearchStrings[$Field] = $NewSearchStrings;
172  }
173  else
174  {
175  unset($this->SearchStrings[$Field]);
176  }
177  }
178  }
179  else
180  {
181  # remove any matching keyword search parameters
182  $NewSearchStrings = array();
183  foreach ($this->KeywordSearchStrings as $Value)
184  {
185  if ($Value != $String)
186  {
187  $NewSearchStrings[] = $Value;
188  }
189  }
190  $this->KeywordSearchStrings = $NewSearchStrings;
191  }
192  }
193  }
194  else
195  {
196  # if field specified
197  if ($Field !== NULL)
198  {
199  # clear any search strings for this field
200  if (isset($this->SearchStrings[$Field]))
201  {
202  unset($this->SearchStrings[$Field]);
203  }
204  }
205  else
206  {
207  # clear all keyword search parameters
208  $this->KeywordSearchStrings = array();
209  }
210  }
211 
212  # for each parameter subgroup
213  $NewSubgroups = array();
214  foreach ($this->Subgroups as $Group)
215  {
216  # remove parameter from subgroup
217  $Group->RemoveParameter($SearchStrings, $Field);
218 
219  # if the subgroup is not empty
220  if ($Group->ParameterCount())
221  {
222  # keep subgroup
223  $NewSubgroups[] = $Group;
224  }
225  }
226  $this->Subgroups = $NewSubgroups;
227  }
228 
236  public function Logic($NewValue = NULL)
237  {
238  # if new value supplied
239  if ($NewValue !== NULL)
240  {
241  # normalize value
242  $NormValue = ($NewValue == SearchEngine::LOGIC_OR)
243  ? "OR"
244  : (($NewValue == SearchEngine::LOGIC_AND)
245  ? "AND"
246  : strtoupper($NewValue));
247 
248  # error out if value appears invalid
249  if (($NormValue !== "AND") && ($NormValue !== "OR"))
250  {
251  throw new InvalidArgumentException("New logic setting"
252  ." is invalid (".$NewValue.").");
253  }
254 
255  # save new setting
256  $this->Logic = $NormValue;
257  }
258 
259  # return current logic setting to caller
260  return $this->Logic;
261  }
262 
271  public function ItemTypes($ItemTypes = NULL)
272  {
273  if ($ItemTypes !== NULL)
274  {
275  if ($ItemTypes === FALSE)
276  {
277  $this->ItemTypes = FALSE;
278  }
279  else
280  {
281  if (!is_array($ItemTypes))
282  {
283  $ItemTypes = array($ItemTypes);
284  }
285  $this->ItemTypes = $ItemTypes;
286  }
287  }
288  return $this->ItemTypes;
289  }
290 
300  public function SortBy($NewValue = NULL)
301  {
302  if ($NewValue !== NULL)
303  {
304  $this->SortBy = $NewValue;
305  }
306  return $this->SortBy;
307  }
308 
320  public function SortDescending($NewValue = NULL)
321  {
322  if ($NewValue !== NULL)
323  {
324  $this->SortDescending = $NewValue;
325  }
326  return $this->SortDescending;
327  }
328 
333  public function AddSet(SearchParameterSet $Set)
334  {
335  if ($Set->ParameterCount())
336  {
337  $this->Subgroups[] = $Set;
338  }
339  }
340 
341 
342  /*@)*/
343  # ---- DATA RETRIEVAL -----------------------------------------------------
344  /*@(*/
345 
350  public function ParameterCount()
351  {
352  $Count = count($this->KeywordSearchStrings);
353  foreach ($this->SearchStrings as $Field => $Strings)
354  {
355  $Count += count($Strings);
356  }
357  foreach ($this->Subgroups as $Group)
358  {
359  $Count += $Group->ParameterCount();
360  }
361  return $Count;
362  }
363 
371  public function GetSearchStrings($IncludeSubgroups = FALSE)
372  {
373  $SearchStrings = $this->SearchStrings;
374  if ($IncludeSubgroups)
375  {
376  foreach ($this->Subgroups as $Group)
377  {
378  $SubStrings = $Group->GetSearchStrings(TRUE);
379  foreach ($SubStrings as $Field => $Strings)
380  {
381  if (isset($SearchStrings[$Field]))
382  {
383  $SearchStrings[$Field] = array_merge(
384  $SearchStrings[$Field], $Strings);
385  }
386  else
387  {
388  $SearchStrings[$Field] = $Strings;
389  }
390  }
391  }
392  }
393  return $SearchStrings;
394  }
395 
404  public function GetSearchStringsForField($Field, $IncludeSubgroups = TRUE)
405  {
406  # normalize field value
407  $Field = self::NormalizeField($Field);
408 
409  # start with our string values
410  $Strings = isset($this->SearchStrings[$Field])
411  ? $this->SearchStrings[$Field] : array();
412 
413  # if strings from subgroups should also be returned
414  if ($IncludeSubgroups)
415  {
416  # for each subgroup
417  foreach ($this->Subgroups as $Group)
418  {
419  # add any strings from that subgroup
420  $Strings = array_merge($Strings,
421  $Group->GetSearchStringsForField($Field));
422  }
423  }
424 
425  # return all strings found to caller
426  return $Strings;
427  }
428 
433  public function GetKeywordSearchStrings()
434  {
435  return $this->KeywordSearchStrings;
436  }
437 
442  public function GetSubgroups()
443  {
444  return $this->Subgroups;
445  }
446 
451  public function GetFields()
452  {
453  # retrieve our fields
454  $Fields = array_keys($this->SearchStrings);
455 
456  # for each subgroup
457  foreach ($this->Subgroups as $Group)
458  {
459  # add fields from subgroup to the list
460  $Fields = array_merge($Fields, $Group->GetFields());
461  }
462 
463  # filter out duplicates and sort to ensure consistency
464  $Fields = array_unique($Fields);
465  sort($Fields);
466 
467  # return list of field identifiers to caller
468  return $Fields;
469  }
470 
471 
472  /*@)*/
473  # ---- DATA TRANSLATION ---------------------------------------------------
474  /*@(*/
475 
486  public function Data($NewValue = NULL)
487  {
488  # if new data supplied
489  if ($NewValue !== NULL)
490  {
491  # unpack set data and load
492  $this->LoadFromData($NewValue);
493  }
494 
495  # serialize current data and return to caller
496  $Data = array();
497  if ($this->Logic !== "AND") { $Data["Logic"] = $this->Logic; }
498  if (count($this->SearchStrings))
499  { $Data["SearchStrings"] = $this->SearchStrings; }
500  if (count($this->KeywordSearchStrings))
501  {
502  $Data["KeywordSearchStrings"] = $this->KeywordSearchStrings;
503  }
504  if (count($this->Subgroups))
505  {
506  foreach ($this->Subgroups as $Subgroup)
507  {
508  $Data["Subgroups"][] = $Subgroup->Data();
509  }
510  }
511  return serialize($Data);
512  }
513 
520  public function UrlParameters($NewValue = NULL)
521  {
522  # if new value supplied
523  if ($NewValue !== NULL)
524  {
525  # set new parameters
526  $this->SetFromUrlParameters($NewValue);
527  }
528 
529  # get existing search parameters as URL parameters
530  $Params = $this->GetAsUrlParameters();
531 
532  # sort parameters by parameter name to normalize result
533  ksort($Params);
534 
535  # return parameters to caller
536  return $Params;
537  }
538 
545  public function UrlParameterString($NewValue = NULL)
546  {
547  # get/set parameters
548  $Params = $this->UrlParameters($NewValue);
549 
550  # combine values into string
551  $ParamString = "";
552  $Separator = "";
553  foreach ($Params as $Index => $Value)
554  {
555  $ParamString .= $Separator.$Index."=".urlencode($Value);
556  $Separator = "&";
557  }
558 
559  # return string to caller
560  return $ParamString;
561  }
562 
574  public function TextDescription($IncludeHtml = TRUE, $StartWithBreak = TRUE,
575  $TruncateLongWordsTo = 0, $Indent = "")
576  {
577  # define list of phrases used to represent logical operators
578  $OperatorPhrases = array(
579  "=" => "is",
580  "==" => "is",
581  ">" => "is greater than",
582  "<" => "is less than",
583  ">=" => "is at least",
584  "<=" => "is no more than",
585  "!" => "is not",
586  "!=" => "is not",
587  "^" => "begins with",
588  "$" => "ends with",
589  "@" => "was last modified on or after",
590  "@>" => "was last modified after",
591  "@>=" => "was last modified on or after",
592  "@<" => "was last modified before",
593  "@<=" => "was last modified on or before",
594  );
595  $AgoOperatorPhrases = array(
596  "@>" => "was last modified more than",
597  "@>=" => "was last modified at least or more than",
598  "@<" => "was last modified less than",
599  "@<=" => "was last modified at most or less than",
600  );
601 
602  # set characters used to indicate literal strings
603  $LiteralStart = $IncludeHtml ? "<i>" : "\"";
604  $LiteralEnd = $IncludeHtml ? "</i>" : "\"";
605  $LiteralBreak = $IncludeHtml ? "<br>\n" : "\n";
606  $Indent .= $IncludeHtml ? "&nbsp;&nbsp;&nbsp;&nbsp;" : " ";
607 
608  # for each keyword search string
609  $Descriptions = array();
610  foreach ($this->KeywordSearchStrings as $SearchString)
611  {
612  # escape search string if appropriate
613  if ($IncludeHtml)
614  {
615  $SearchString = defaulthtmlentities($SearchString);
616  }
617 
618  # add string to list of descriptions
619  $Descriptions[] = $LiteralStart.$SearchString.$LiteralEnd;
620  }
621 
622  # for each field with search strings
623  foreach ($this->SearchStrings as $FieldId => $SearchStrings)
624  {
625  # retrieve field name
626  $FieldName = call_user_func(self::$PrintableFieldFunction, $FieldId);
627 
628  # for each search string
629  foreach ($SearchStrings as $SearchString)
630  {
631  # extract operator from search string
632  $MatchResult = preg_match('/^([=><!^$@]+)(.+)/',
633  $SearchString, $Matches);
634 
635  # determine operator phrase
636  if (($MatchResult == 1) && isset($OperatorPhrases[$Matches[1]]))
637  {
638  if (isset($AgoOperatorPhrases[$Matches[1]])
639  && strstr($SearchString, "ago"))
640  {
641  $OpPhrase = $AgoOperatorPhrases[$Matches[1]];
642  }
643  else
644  {
645  $OpPhrase = $OperatorPhrases[$Matches[1]];
646  }
647  $SearchString = $Matches[2];
648  }
649  else
650  {
651  $OpPhrase = "contains";
652  }
653 
654  # escape field name and search string if appropriate
655  if ($IncludeHtml)
656  {
657  $FieldName = defaulthtmlentities($FieldName);
658  $SearchString = defaulthtmlentities($SearchString);
659  }
660 
661  # assemble field and operator and value into description
662  $Descriptions[] = $FieldName." ".$OpPhrase." "
663  .$LiteralStart.$SearchString.$LiteralEnd;
664  }
665  }
666 
667  # for each subgroup
668  foreach ($this->Subgroups as $Subgroup)
669  {
670  # retrieve description for subgroup
671  if ($Subgroup->ParameterCount() == 1)
672  {
673  $Descriptions[] = $Subgroup->TextDescription($IncludeHtml,
674  $StartWithBreak, $TruncateLongWordsTo, $Indent);
675  }
676  elseif ($Subgroup->ParameterCount() > 1)
677  {
678  $Descriptions[] = "(".$Subgroup->TextDescription($IncludeHtml,
679  $StartWithBreak, $TruncateLongWordsTo, $Indent).")";
680  }
681  }
682 
683  # join descriptions with appropriate conjunction
684  $Descrip = join($LiteralBreak.$Indent." ".strtolower($this->Logic)." ",
685  $Descriptions);
686 
687  # if caller requested that long words be truncated
688  if ($TruncateLongWordsTo > 4)
689  {
690  # break description into words
691  $Words = explode(" ", $Descrip);
692 
693  # for each word
694  $NewDescrip = "";
695  foreach ($Words as $Word)
696  {
697  # if word is longer than specified length
698  if (strlen(strip_tags($Word)) > $TruncateLongWordsTo)
699  {
700  # truncate word and add ellipsis
701  $Word = StdLib::NeatlyTruncateString($Word, $TruncateLongWordsTo - 3);
702  }
703 
704  # add word to new description
705  $NewDescrip .= " ".$Word;
706  }
707 
708  # set description to new description
709  $Descrip = $NewDescrip;
710  }
711 
712  if (isset(self::$TextDescriptionFilterFunction))
713  {
714  $Descrip = call_user_func_array(
715  self::$TextDescriptionFilterFunction,
716  [$Descrip]);
717  }
718 
719  # return description to caller
720  return trim($Descrip);
721  }
722 
723 
724  /*@)*/
725  # ---- UTILITY METHODS ----------------------------------------------------
726  /*@(*/
727 
733  public function ReplaceSearchString($Pattern, $Replacement)
734  {
735  # modify our fielded search strings
736  foreach ($this->SearchStrings as $Field => $Strings)
737  {
738  $this->SearchStrings[$Field] =
739  preg_replace($Pattern, $Replacement, $Strings);
740  }
741 
742  # modify our keyword search strings
743  if (count($this->KeywordSearchStrings))
744  {
745  $this->KeywordSearchStrings =
746  preg_replace($Pattern, $Replacement, $this->KeywordSearchStrings);
747  }
748 
749  # modify any subgroups
750  foreach ($this->Subgroups as $Group)
751  {
752  $Group->ReplaceSearchString($Pattern, $Replacement);
753  }
754  }
755 
756 
757  /*@)*/
758  # ---- BACKWARD COMPATIBILITY ---------------------------------------------
759  /*@(*/
760 
769  public function GetAsLegacyArray()
770  {
771  $Legacy = array();
772 
773  $Group = $this->ConvertToLegacyGroup();
774  if (count($Group))
775  {
776  $Legacy["MAIN"] = $Group;
777  }
778 
779  # for each subgroup
780  foreach ($this->Subgroups as $Subgroup)
781  {
782  # skip empty search groups
783  if (count($Subgroup->SearchStrings)==0)
784  {
785  continue;
786  }
787 
788  $SubLegacy = $Subgroup->ConvertToLegacyGroup();
789 
790  # give an index based on the FieldId of the first
791  # element in the SearchStrings
792  $FieldId = call_user_func(
793  self::$CanonicalFieldFunction,
794  current(array_keys($SubLegacy["SearchStrings"])) );
795 
796  # add groups from legacy array to our array
797  if (!isset($Legacy[$FieldId]))
798  {
799  $Legacy[$FieldId] = $SubLegacy;
800  }
801  else
802  {
803  $Num = count($Legacy[$FieldId]);
804  $Legacy[$FieldId."-".$Num] = $SubLegacy;
805  }
806 
807  if (count($Subgroup->Subgroups))
808  {
809  throw new Exception(
810  "Attempt to convert SearchParameterSet containing nested subgroups "
811  ."to legacy format");
812  }
813  }
814 
815  # return array to caller
816  return $Legacy;
817  }
818 
823  public function SetFromLegacyArray($SearchGroups)
824  {
825  # clear current settings
826  $this->KeywordSearchStrings = array();
827  $this->SearchStrings = array();
828  $this->Subgroups = array();
829 
830  # iterate over legacy search groups
831  foreach ($SearchGroups as $GroupId => $SearchGroup)
832  {
833  if ($GroupId == "MAIN")
834  {
835  # add terms from the main search group to ourself
836  $this->LoadFromLegacyGroup($SearchGroup);
837  }
838  else
839  {
840  # create subgroups for other groups
841  $Subgroup = new SearchParameterSet();
842  $Subgroup->LoadFromLegacyGroup($SearchGroup);
843 
844  # add any non-empty groups
845  if ($Subgroup->ParameterCount())
846  {
847  $this->AddSet($Subgroup);
848  }
849  }
850  }
851  }
852 
857  public function SetFromLegacyUrl($ParameterString)
858  {
859  # clear current settings
860  $this->KeywordSearchStrings = array();
861  $this->SearchStrings = array();
862  $this->Subgroups = array();
863 
864  # extact array of parameters from passed string
865  $GetVars = ParseQueryString($ParameterString);
866 
867  # iterate over the provided parameters
868  foreach ($GetVars as $Key => $Val)
869  {
870  # if this param gives search information
871  if (preg_match("/^([FGH])(K|[0-9]+)$/", $Key, $Matches))
872  {
873  # extract what kind of search it was which field
874  $Type = $Matches[1];
875  $FieldId = $Matches[2];
876 
877  # for 'contains' searches
878  if ($Type == "F")
879  {
880  # add this to our search strings
881  $this->AddParameter( $Val,
882  ($FieldId == "K" ? NULL : $FieldId) );
883  }
884  else
885  {
886  # otherwise, create a subgroup for this parameter
887  $Subgroup = new SearchParameterSet();
888 
889  # set logic based on the search type
890  $Subgroup->Logic($Type=="H" ? "AND" : "OR");
891 
892  # extract the values and add them to a subgroup
893  $Values = explode("-", $Val);
894  $Subgroup->AddParameter(
895  self::TranslateLegacySearchValues($FieldId, $Values),
896  $FieldId );
897 
898  # if subgroup was non-empty, slurp it up
899  if ($Subgroup->ParameterCount())
900  {
901  $this->AddSet($Subgroup);
902  }
903  }
904  }
905  }
906  }
907 
913  public static function IsLegacyUrl($ParameterString)
914  {
915  $QueryVars = ParseQueryString($ParameterString);
916 
917  return (array_key_exists("Q", $QueryVars) &&
918  $QueryVars["Q"] == "Y") ? TRUE : FALSE ;
919  }
920 
926  public static function ConvertLegacyUrl($ParameterString)
927  {
928  $SearchParams = new SearchParameterSet();
929  $SearchParams->SetFromLegacyUrl($ParameterString);
930 
931  return $SearchParams->UrlParameterString();
932  }
933 
940  public static function SetLegacyUrlTranslationFunction($Func)
941  {
942  if (is_callable($Func))
943  {
944  self::$LegacyUrlTranslationFunction = $Func;
945  }
946  else
947  {
948  throw new InvalidArgumentException("Invalid function supplied.");
949  }
950  }
951 
959  public static function SetTextDescriptionFilterFunction($Func)
960  {
961  if (is_callable($Func))
962  {
963  self::$TextDescriptionFilterFunction = $Func;
964  }
965  else
966  {
967  throw new InvalidArgumentException("Invalid function supplied.");
968  }
969  }
970 
977  public static function TranslateLegacySearchValues($Field, $Values)
978  {
979  if (($Field !== NULL) && isset(self::$LegacyUrlTranslationFunction))
980  {
981  $Values = call_user_func(self::$LegacyUrlTranslationFunction,
982  $Field, $Values);
983  }
984  return $Values;
985  }
986 
987  /*@)*/
988  # ---- PRIVATE INTERFACE -------------------------------------------------
989 
990  private $KeywordSearchStrings = array();
991  private $ItemTypes = FALSE;
992  private $Logic = self::DEFAULT_LOGIC;
993  private $SearchStrings = array();
994  private $SortBy = FALSE;
995  private $SortDescending = TRUE;
996  private $Subgroups = array();
997 
998  static private $CanonicalFieldFunction;
999  static private $PrintableFieldFunction;
1000  static private $LegacyUrlTranslationFunction;
1001  static private $TextDescriptionFilterFunction;
1002  static private $UrlParameterPrefix = "F";
1003 
1004  const DEFAULT_LOGIC = "AND";
1005  const URL_KEYWORDFREE_RANGE = "A-JL-Z";
1007  const URL_LOGIC_INDICATOR = "00";
1008 
1014  private function LoadFromData($Serialized)
1015  {
1016  # unpack new data
1017  $Data = unserialize($Serialized);
1018  if (!is_array($Data))
1019  {
1020  throw new InvalidArgumentException("Incoming set data"
1021  ." appears invalid.");
1022  }
1023 
1024  # load logic
1025  $this->Logic = isset($Data["Logic"]) ? $Data["Logic"] : "AND";
1026 
1027  # load search strings
1028  $this->SearchStrings = isset($Data["SearchStrings"])
1029  ? $Data["SearchStrings"] : array();
1030  $this->KeywordSearchStrings = isset($Data["KeywordSearchStrings"])
1031  ? $Data["KeywordSearchStrings"] : array();
1032 
1033  # load any subgroups
1034  $this->Subgroups = array();
1035  if (isset($Data["Subgroups"]))
1036  {
1037  foreach ($Data["Subgroups"] as $SubgroupData)
1038  {
1039  $this->Subgroups[] = new SearchParameterSet($SubgroupData);
1040  }
1041  }
1042  }
1043 
1048  private function ConvertToLegacyGroup()
1049  {
1050  $Group = array();
1051  # for each set of search strings
1052  foreach ($this->SearchStrings as $Field => $Strings)
1053  {
1054  # get text name of field
1055  $FieldName = call_user_func(self::$PrintableFieldFunction, $Field);
1056 
1057  # add set to group
1058  $Group["SearchStrings"][$FieldName] = $Strings;
1059  }
1060 
1061  # for each keyword search string
1062  foreach ($this->KeywordSearchStrings as $String)
1063  {
1064  # add string to keyword entry in group
1065  $Group["SearchStrings"]["XXXKeywordXXX"][] = $String;
1066  }
1067 
1068  # if we had any search terms
1069  if (count($Group))
1070  {
1071  # smash single-value arrays to a scalar
1072  foreach ($Group["SearchStrings"] as &$Tgt)
1073  {
1074  if (count($Tgt) == 1)
1075  {
1076  $Tgt = current($Tgt);
1077  }
1078  }
1079 
1080  # set logic for search group
1081  $Group["Logic"] = ($this->Logic == "OR")
1083  }
1084 
1085  return $Group;
1086  }
1087 
1092  private function LoadFromLegacyGroup($Group)
1093  {
1094  # set logic appropriately
1095  $this->Logic(
1096  ($Group["Logic"] == SearchEngine::LOGIC_OR) ?
1097  "OR" : "AND" );
1098 
1099  # if this group had no search strings, we're done
1100  if (!isset($Group["SearchStrings"]))
1101  {
1102  return;
1103  }
1104 
1105  # otherwise, load the search strings
1106  foreach ($Group["SearchStrings"] as $Field => $Params)
1107  {
1108  # skip empty groups
1109  if (count($Params)==0)
1110  {
1111  continue;
1112  }
1113 
1114  $this->AddParameter( $Params,
1115  ($Field == "XXXKeywordXXX" ? NULL : $Field) );
1116 
1117  }
1118  }
1119 
1125  private function GetAsUrlParameters($SetPrefix = "")
1126  {
1127  # for each search string group in set
1128  $Params = array();
1129  foreach ($this->SearchStrings as $FieldId => $Values)
1130  {
1131  # get numeric version of field ID if not already numeric
1132  if (!is_numeric($FieldId))
1133  {
1134  $FieldId = call_user_func(self::$CanonicalFieldFunction, $FieldId);
1135  }
1136 
1137  # for each search string in group
1138  $ParamSuffix = "";
1139  foreach ($Values as $Value)
1140  {
1141  # check for too many search strings for this field
1142  if ($ParamSuffix == "Z")
1143  {
1144  throw new Exception("Maximum search parameter complexity"
1145  ." exceeded: more than 26 search parameters for"
1146  ." field ID ".$FieldId.".");
1147  }
1148 
1149  # add search string to URL
1150  $Params[self::$UrlParameterPrefix.$SetPrefix
1151  .$FieldId.$ParamSuffix] = $Value;
1152  $ParamSuffix = ($ParamSuffix == "") ? "A"
1153  : chr(ord($ParamSuffix) + 1);
1154  }
1155  }
1156 
1157  # for each keyword search string
1158  $ParamSuffix = "";
1159  foreach ($this->KeywordSearchStrings as $Value)
1160  {
1161  # check for too many keyword search strings
1162  if ($ParamSuffix == "Z")
1163  {
1164  throw new Exception("Maximum search parameter complexity"
1165  ." exceeded: more than 26 keyword search parameters.");
1166  }
1167 
1168  # add search string to URL
1169  $Params[self::$UrlParameterPrefix.$SetPrefix
1170  .self::URL_KEYWORD_INDICATOR.$ParamSuffix] = $Value;
1171  $ParamSuffix = ($ParamSuffix == "") ? "A"
1172  : chr(ord($ParamSuffix) + 1);
1173  }
1174 
1175  # add logic if not default
1176  if ($this->Logic != self::DEFAULT_LOGIC)
1177  {
1178  $Params[self::$UrlParameterPrefix.$SetPrefix
1179  .self::URL_LOGIC_INDICATOR] = $this->Logic;
1180  }
1181 
1182  # for each search parameter subgroup
1183  $SetLetter = "A";
1184  foreach ($this->Subgroups as $Subgroup)
1185  {
1186  # check for too many subgroups
1187  if ($SetLetter == "Z")
1188  {
1189  throw new Exception("Maximum search parameter complexity"
1190  ." exceeded: more than 24 search parameter subgroups.");
1191  }
1192 
1193  # retrieve URL string for subgroup and add it to URL
1194  $Params = array_merge($Params, $Subgroup->GetAsUrlParameters(
1195  $SetPrefix.$SetLetter));
1196 
1197  # move to next set letter
1198  $SetLetter = ($SetLetter == chr(ord(self::URL_KEYWORD_INDICATOR) - 1))
1199  ? chr(ord(self::URL_KEYWORD_INDICATOR) + 1)
1200  : chr(ord($SetLetter) + 1);
1201  }
1202 
1203  # return constructed URL parameter string to caller
1204  return $Params;
1205  }
1206 
1213  private function SetFromUrlParameters($UrlParameters)
1214  {
1215  # if string was passed in
1216  if (is_string($UrlParameters))
1217  {
1218  # split string into parameter array
1219  $Params = explode("&", $UrlParameters);
1220 
1221  # pare down parameter array to search parameter elements
1222  # and strip off search parameter prefix
1223  $NewUrlParameters = array();
1224  foreach ($Params as $Param)
1225  {
1226  if (strpos($Param, self::$UrlParameterPrefix) === 0)
1227  {
1228  list($Index, $Value) = explode("=", $Param);
1229  $NewUrlParameters[$Index] = urldecode($Value);
1230  }
1231  }
1232  $UrlParameters = $NewUrlParameters;
1233  }
1234 
1235  # for each search parameter
1236  foreach ($UrlParameters as $ParamName => $SearchString)
1237  {
1238  # strip off standard search parameter prefix
1239  $ParamName = substr($ParamName, strlen(self::$UrlParameterPrefix));
1240 
1241  # split parameter into component parts
1242  $SplitResult = preg_match("/^([".self::URL_KEYWORDFREE_RANGE."]*)"
1243  ."([0-9".self::URL_KEYWORD_INDICATOR."]+)([A-Z]*)$/",
1244  $ParamName, $Matches);
1245 
1246  # if split was successful
1247  if ($SplitResult === 1)
1248  {
1249  # pull components from split pieces
1250  $SetPrefix = $Matches[1];
1251  $FieldId = $Matches[2];
1252  $ParamSuffix = $Matches[3];
1253 
1254  # if set prefix indicates parameter is part of our set
1255  if ($SetPrefix == "")
1256  {
1257  switch ($FieldId)
1258  {
1259  case self::URL_LOGIC_INDICATOR:
1260  # set logic
1261  $this->Logic($SearchString);
1262  break;
1263 
1264  case self::URL_KEYWORD_INDICATOR:
1265  # add string to keyword searches
1266  $this->KeywordSearchStrings[] = $SearchString;
1267  break;
1268 
1269  default:
1270  # add string to searches for appropriate field
1271  $this->SearchStrings[$FieldId][] = $SearchString;
1272  break;
1273  }
1274  }
1275  else
1276  {
1277  # add parameter to array for subgroup
1278  $SubgroupIndex = $SetPrefix[0];
1279  $SubgroupPrefix = (strlen($SetPrefix) > 1)
1280  ? substr($SetPrefix, 1) : "";
1281  $SubgroupParamIndex = self::$UrlParameterPrefix
1282  .$SubgroupPrefix.$FieldId.$ParamSuffix;
1283  $SubgroupParameters[$SubgroupIndex][$SubgroupParamIndex]
1284  = $SearchString;
1285  }
1286  }
1287  }
1288 
1289  # if subgroups were found
1290  if (isset($SubgroupParameters))
1291  {
1292  # for each identified subgroup
1293  foreach ($SubgroupParameters as $SubgroupIndex => $Parameters)
1294  {
1295  # create subgroup and set parameters
1296  $Subgroup = new SearchParameterSet();
1297  $Subgroup->SetFromUrlParameters($Parameters);
1298 
1299  # add subgroup to our set
1300  $this->Subgroups[] = $Subgroup;
1301  }
1302  }
1303  }
1304 
1310  private static function NormalizeField($Field)
1311  {
1312  if (($Field !== NULL) && isset(self::$CanonicalFieldFunction))
1313  {
1314  $Field = call_user_func(self::$CanonicalFieldFunction, $Field);
1315  }
1316  return $Field;
1317  }
1318 }
TextDescription($IncludeHtml=TRUE, $StartWithBreak=TRUE, $TruncateLongWordsTo=0, $Indent="")
Get text description of search parameter set.
Data($NewValue=NULL)
Get/set search parameter set data, in the form of an opaque string.
static TranslateLegacySearchValues($Field, $Values)
Translate legacy search values to modern equivalents if possible.
SetFromLegacyArray($SearchGroups)
Set search parameters from legacy array format.
static SetCanonicalFieldFunction($Func)
Register function used to retrieve a canonical value for a field.
Set of parameters used to perform a search.
AddSet(SearchParameterSet $Set)
Add subgroup of search parameters to set.
UrlParameterString($NewValue=NULL)
Get/set search parameter set, in the form of an URL parameter string.
UrlParameters($NewValue=NULL)
Get/set search parameter set, in the form of URL parameters.
Logic($NewValue=NULL)
Get/set logic for set.
GetFields()
Get fields used in search parameters (including subgroups).
static SetLegacyUrlTranslationFunction($Func)
Register function used to converrt values from the format used in a Legacy URL string to the modern v...
ItemTypes($ItemTypes=NULL)
Get/set allowed item types.
GetSearchStringsForField($Field, $IncludeSubgroups=TRUE)
Get search strings for specified field.
static SetPrintableFieldFunction($Func)
Register function used to retrieve a printable value for a field.
SetFromLegacyUrl($ParameterString)
Set search parameters from legacy URL string.
__clone()
Class clone handler, implemented so that clones will be deep copies rather than PHP5&#39;s default shallo...
AddParameter($SearchStrings, $Field=NULL)
Add search parameter to set.
static ConvertLegacyUrl($ParameterString)
Convert legacy URL to the current URL format.
GetAsLegacyArray()
Retrieve search parameters in legacy array format.
GetSearchStrings($IncludeSubgroups=FALSE)
Get search strings in set.
static NeatlyTruncateString($String, $MaxLength, $BreakAnywhere=FALSE)
Attempt to truncate a string as neatly as possible with respect to word breaks, punctuation, and HTML tags.
Definition: StdLib.php:239
__construct($Data=NULL)
Class constructor, used to create a new set or reload an existing set from previously-constructed dat...
RemoveParameter($SearchStrings, $Field=NULL)
Remove search parameter from set.
static IsLegacyUrl($ParameterString)
Determine if a URL uses legacy format.
GetKeywordSearchStrings()
Get keyword search strings in set.
ReplaceSearchString($Pattern, $Replacement)
Modify all search strings with the specified regular expression.
SortBy($NewValue=NULL)
Get/set field to sort results by.
SortDescending($NewValue=NULL)
Get/set whether to sort results will be sorted in descending (as opposed to ascending) order...
GetSubgroups()
Get parameter subgroups.
ParameterCount()
Get number of search parameters in set, including those in subgroups.
static SetTextDescriptionFilterFunction($Func)
Register function used to filter text descriptions of searches prior to display.