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 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 = FALSE)
272  {
273  if ($ItemTypes !== FALSE)
274  {
275  if (!is_array($ItemTypes))
276  {
277  $ItemTypes = array($ItemTypes);
278  }
279  $this->ItemTypes = $ItemTypes;
280  }
281  return $this->ItemTypes;
282  }
283 
288  public function AddSet(SearchParameterSet $Set)
289  {
290  if ($Set->ParameterCount()==0)
291  {
292  throw new Exception("Attempt to add empty subgroup");
293  }
294 
295  # add subgroup to privilege set
296  $this->Subgroups[] = $Set;
297  }
298 
299 
300  /*@)*/
301  # ---- DATA RETRIEVAL -----------------------------------------------------
302  /*@(*/
303 
308  public function ParameterCount()
309  {
310  $Count = count($this->KeywordSearchStrings);
311  foreach ($this->SearchStrings as $Field => $Strings)
312  {
313  $Count += count($Strings);
314  }
315  foreach ($this->Subgroups as $Group)
316  {
317  $Count += $Group->ParameterCount();
318  }
319  return $Count;
320  }
321 
329  public function GetSearchStrings($IncludeSubgroups = FALSE)
330  {
331  $SearchStrings = $this->SearchStrings;
332  if ($IncludeSubgroups)
333  {
334  foreach ($this->Subgroups as $Group)
335  {
336  $SubStrings = $Group->GetSearchStrings(TRUE);
337  foreach ($SubStrings as $Field => $Strings)
338  {
339  if (isset($SearchStrings[$Field]))
340  {
341  $SearchStrings[$Field] = array_merge(
342  $SearchStrings[$Field], $Strings);
343  }
344  else
345  {
346  $SearchStrings[$Field] = $Strings;
347  }
348  }
349  }
350  }
351  return $SearchStrings;
352  }
353 
362  public function GetSearchStringsForField($Field, $IncludeSubgroups = TRUE)
363  {
364  # normalize field value
365  $Field = self::NormalizeField($Field);
366 
367  # start with our string values
368  $Strings = isset($this->SearchStrings[$Field])
369  ? $this->SearchStrings[$Field] : array();
370 
371  # if strings from subgroups should also be returned
372  if ($IncludeSubgroups)
373  {
374  # for each subgroup
375  foreach ($this->Subgroups as $Group)
376  {
377  # add any strings from that subgroup
378  $Strings = array_merge($Strings,
379  $Group->GetSearchStringsForField($Field));
380  }
381  }
382 
383  # return all strings found to caller
384  return $Strings;
385  }
386 
391  public function GetKeywordSearchStrings()
392  {
393  return $this->KeywordSearchStrings;
394  }
395 
400  public function GetSubgroups()
401  {
402  return $this->Subgroups;
403  }
404 
409  public function GetFields()
410  {
411  # retrieve our fields
412  $Fields = array_keys($this->SearchStrings);
413 
414  # for each subgroup
415  foreach ($this->Subgroups as $Group)
416  {
417  # add fields from subgroup to the list
418  $Fields = array_merge($Fields, $Group->GetFields());
419  }
420 
421  # filter out duplicates and sort to ensure consistency
422  $Fields = array_unique($Fields);
423  sort($Fields);
424 
425  # return list of field identifiers to caller
426  return $Fields;
427  }
428 
429 
430  /*@)*/
431  # ---- DATA TRANSLATION ---------------------------------------------------
432  /*@(*/
433 
444  public function Data($NewValue = NULL)
445  {
446  # if new data supplied
447  if ($NewValue !== NULL)
448  {
449  # unpack set data and load
450  $this->LoadFromData($NewValue);
451  }
452 
453  # serialize current data and return to caller
454  $Data = array();
455  if ($this->Logic !== "AND") { $Data["Logic"] = $this->Logic; }
456  if (count($this->SearchStrings))
457  { $Data["SearchStrings"] = $this->SearchStrings; }
458  if (count($this->KeywordSearchStrings))
459  {
460  $Data["KeywordSearchStrings"] = $this->KeywordSearchStrings;
461  }
462  if (count($this->Subgroups))
463  {
464  foreach ($this->Subgroups as $Subgroup)
465  {
466  $Data["Subgroups"][] = $Subgroup->Data();
467  }
468  }
469  return serialize($Data);
470  }
471 
478  public function UrlParameters($NewValue = NULL)
479  {
480  # if new value supplied
481  if ($NewValue !== NULL)
482  {
483  # set new parameters
484  $this->SetFromUrlParameters($NewValue);
485  }
486 
487  # get existing search parameters as URL parameters
488  $Params = $this->GetAsUrlParameters();
489 
490  # sort parameters by parameter name to normalize result
491  ksort($Params);
492 
493  # return parameters to caller
494  return $Params;
495  }
496 
503  public function UrlParameterString($NewValue = NULL)
504  {
505  # get/set parameters
506  $Params = $this->UrlParameters($NewValue);
507 
508  # combine values into string
509  $ParamString = "";
510  $Separator = "";
511  foreach ($Params as $Index => $Value)
512  {
513  $ParamString .= $Separator.$Index."=".urlencode($Value);
514  $Separator = "&";
515  }
516 
517  # return string to caller
518  return $ParamString;
519  }
520 
532  public function TextDescription($IncludeHtml = TRUE, $StartWithBreak = TRUE,
533  $TruncateLongWordsTo = 0, $Indent = "")
534  {
535  # define list of phrases used to represent logical operators
536  $OperatorPhrases = array(
537  "=" => "is",
538  "==" => "is",
539  ">" => "is greater than",
540  "<" => "is less than",
541  ">=" => "is at least",
542  "<=" => "is no more than",
543  "!" => "is not",
544  "!=" => "is not",
545  "^" => "begins with",
546  "$" => "ends with",
547  );
548 
549  # set characters used to indicate literal strings
550  $LiteralStart = $IncludeHtml ? "<i>" : "\"";
551  $LiteralEnd = $IncludeHtml ? "</i>" : "\"";
552  $LiteralBreak = $IncludeHtml ? "<br>\n" : "\n";
553  $Indent .= $IncludeHtml ? "&nbsp;&nbsp;&nbsp;&nbsp;" : " ";
554 
555  # for each keyword search string
556  $Descriptions = array();
557  foreach ($this->KeywordSearchStrings as $SearchString)
558  {
559  # escape search string if appropriate
560  if ($IncludeHtml)
561  {
562  $SearchString = defaulthtmlentities($SearchString);
563  }
564 
565  # add string to list of descriptions
566  $Descriptions[] = $LiteralStart.$SearchString.$LiteralEnd;
567  }
568 
569  # for each field with search strings
570  foreach ($this->SearchStrings as $FieldId => $SearchStrings)
571  {
572  # retrieve field name
573  $FieldName = call_user_func(self::$PrintableFieldFunction, $FieldId);
574 
575  # for each search string
576  foreach ($SearchStrings as $SearchString)
577  {
578  # extract operator from search string
579  $MatchResult = preg_match('/^([=><!^$]+)(.+)/',
580  $SearchString, $Matches);
581 
582  # determine operator phrase
583  if (($MatchResult == 1) && isset($OperatorPhrases[$Matches[1]]))
584  {
585  $OpPhrase = $OperatorPhrases[$Matches[1]];
586  $SearchString = $Matches[2];
587  }
588  else
589  {
590  $OpPhrase = "contains";
591  }
592 
593  # escape field name and search string if appropriate
594  if ($IncludeHtml)
595  {
596  $FieldName = defaulthtmlentities($FieldName);
597  $SearchString = defaulthtmlentities($SearchString);
598  }
599 
600  # assemble field and operator and value into description
601  $Descriptions[] = $FieldName." ".$OpPhrase." "
602  .$LiteralStart.$SearchString.$LiteralEnd;
603  }
604  }
605 
606  # for each subgroup
607  foreach ($this->Subgroups as $Subgroup)
608  {
609  # retrieve description for subgroup
610  if ($Subgroup->ParameterCount() == 1)
611  {
612  $Descriptions[] = $Subgroup->TextDescription($IncludeHtml,
613  $StartWithBreak, $TruncateLongWordsTo, $Indent);
614  }
615  elseif ($Subgroup->ParameterCount() > 1)
616  {
617  $Descriptions[] = "(".$Subgroup->TextDescription($IncludeHtml,
618  $StartWithBreak, $TruncateLongWordsTo, $Indent).")";
619  }
620  }
621 
622  # join descriptions with appropriate conjunction
623  $Descrip = join($LiteralBreak.$Indent." ".strtolower($this->Logic)." ",
624  $Descriptions);
625 
626  # if caller requested that long words be truncated
627  if ($TruncateLongWordsTo > 4)
628  {
629  # break description into words
630  $Words = explode(" ", $Descrip);
631 
632  # for each word
633  $NewDescrip = "";
634  foreach ($Words as $Word)
635  {
636  # if word is longer than specified length
637  if (strlen(strip_tags($Word)) > $TruncateLongWordsTo)
638  {
639  # truncate word and add ellipsis
640  $Word = NeatlyTruncateString($Word, $TruncateLongWordsTo - 3);
641  }
642 
643  # add word to new description
644  $NewDescrip .= " ".$Word;
645  }
646 
647  # set description to new description
648  $Descrip = $NewDescrip;
649  }
650 
651  # return description to caller
652  return $Descrip;
653  }
654 
655 
656  /*@)*/
657  # ---- BACKWARD COMPATIBILITY ---------------------------------------------
658  /*@(*/
659 
668  public function GetAsLegacyArray()
669  {
670  $Legacy = array();
671 
672  $Group = $this->ConvertToLegacyGroup();
673  if (count($Group))
674  {
675  $Legacy["MAIN"] = $Group;
676  }
677 
678  # for each subgroup
679  foreach ($this->Subgroups as $Subgroup)
680  {
681  # skip empty search groups
682  if (count($Subgroup->SearchStrings)==0)
683  {
684  continue;
685  }
686 
687  $SubLegacy = $Subgroup->ConvertToLegacyGroup();
688 
689  # give an index based on the FieldId of the first
690  # element in the SearchStrings
691  $FieldId = call_user_func(
692  self::$CanonicalFieldFunction,
693  current(array_keys($SubLegacy["SearchStrings"])) );
694 
695  # add groups from legacy array to our array
696  if (!isset($Legacy[$FieldId]))
697  {
698  $Legacy[$FieldId] = $SubLegacy;
699  }
700  else
701  {
702  $Num = count($Legacy[$FieldId]);
703  $Legacy[$FieldId."-".$Num] = $SubLegacy;
704  }
705 
706  if (count($Subgroup->Subgroups))
707  {
708  throw new Exception(
709  "Attempt to convert SearchParameterSet containing nested subgroups "
710  ."to legacy format");
711  }
712  }
713 
714  # return array to caller
715  return $Legacy;
716  }
717 
722  public function SetFromLegacyArray($SearchGroups)
723  {
724  # clear current settings
725  $this->KeywordSearchStrings = array();
726  $this->SearchStrings = array();
727  $this->Subgroups = array();
728 
729  # iterate over legacy search groups
730  foreach ($SearchGroups as $GroupId => $SearchGroup)
731  {
732  if ($GroupId == "MAIN")
733  {
734  # add terms from the main search group to ourself
735  $this->LoadFromLegacyGroup($SearchGroup);
736  }
737  else
738  {
739  # create subgroups for other groups
740  $Subgroup = new SearchParameterSet();
741  $Subgroup->LoadFromLegacyGroup($SearchGroup);
742 
743  # add any non-empty groups
744  if ($Subgroup->ParameterCount())
745  {
746  $this->AddSet($Subgroup);
747  }
748  }
749  }
750  }
751 
756  public function SetFromLegacyUrl($ParameterString)
757  {
758  # clear current settings
759  $this->KeywordSearchStrings = array();
760  $this->SearchStrings = array();
761  $this->Subgroups = array();
762 
763  # extact array of parameters from passed string
764  $GetVars = ParseQueryString($ParameterString);
765 
766  # iterate over the provided parameters
767  foreach ($GetVars as $Key => $Val)
768  {
769  # if this param gives search information
770  if (preg_match("/^([FGH])(K|[0-9]+)$/", $Key, $Matches))
771  {
772  # extract what kind of search it was which field
773  $Type = $Matches[1];
774  $FieldId = $Matches[2];
775 
776  # for 'contains' searches
777  if ($Type == "F")
778  {
779  # add this to our search strings
780  $this->AddParameter( $Val,
781  ($FieldId == "K" ? NULL : $FieldId) );
782  }
783  else
784  {
785  # otherwise, create a subgroup for this parameter
786  $Subgroup = new SearchParameterSet();
787 
788  # set logic based on the search type
789  $Subgroup->Logic($Type=="H" ? "AND" : "OR");
790 
791  # extract the values and add them to a subgroup
792  $Values = explode("-", $Val);
793  $Subgroup->AddParameter(
794  self::TranslateLegacySearchValues($FieldId, $Values),
795  $FieldId );
796 
797  # if subgroup was non-empty, slurp it up
798  if ($Subgroup->ParameterCount())
799  {
800  $this->AddSet($Subgroup);
801  }
802  }
803  }
804  }
805  }
806 
812  public static function IsLegacyUrl($ParameterString)
813  {
814  $QueryVars = ParseQueryString($ParameterString);
815 
816  return (array_key_exists("Q", $QueryVars) &&
817  $QueryVars["Q"] == "Y") ? TRUE : FALSE ;
818  }
819 
825  public static function ConvertLegacyUrl($ParameterString)
826  {
827  $SearchParams = new SearchParameterSet();
828  $SearchParams->SetFromLegacyUrl($ParameterString);
829 
830  return $SearchParams->UrlParameterString();
831  }
832 
839  public static function SetLegacyUrlTranslationFunction($Func)
840  {
841  if (is_callable($Func))
842  {
843  self::$LegacyUrlTranslationFunction = $Func;
844  }
845  else
846  {
847  throw new InvalidArgumentException("Invalid function supplied.");
848  }
849  }
850 
857  public static function TranslateLegacySearchValues($Field, $Values)
858  {
859  if (($Field !== NULL) && isset(self::$LegacyUrlTranslationFunction))
860  {
861  $Values = call_user_func(self::$LegacyUrlTranslationFunction,
862  $Field, $Values);
863  }
864  return $Values;
865  }
866 
867  /*@)*/
868  # ---- PRIVATE INTERFACE -------------------------------------------------
869 
870  private $KeywordSearchStrings = array();
871  private $ItemTypes = NULL;
872  private $Logic = self::DEFAULT_LOGIC;
873  private $SearchStrings = array();
874  private $Subgroups = array();
875 
876  static private $CanonicalFieldFunction;
877  static private $PrintableFieldFunction;
878  static private $LegacyUrlTranslationFunction;
879  static private $UrlParameterPrefix = "F";
880 
881  const DEFAULT_LOGIC = "AND";
882  const URL_KEYWORDFREE_RANGE = "A-JL-Z";
884  const URL_LOGIC_INDICATOR = "00";
885 
891  private function LoadFromData($Serialized)
892  {
893  # unpack new data
894  $Data = unserialize($Serialized);
895  if (!is_array($Data))
896  {
897  throw new InvalidArgumentException("Incoming set data"
898  ." appears invalid.");
899  }
900 
901  # load logic
902  $this->Logic = isset($Data["Logic"]) ? $Data["Logic"] : "AND";
903 
904  # load search strings
905  $this->SearchStrings = isset($Data["SearchStrings"])
906  ? $Data["SearchStrings"] : array();
907  $this->KeywordSearchStrings = isset($Data["KeywordSearchStrings"])
908  ? $Data["KeywordSearchStrings"] : array();
909 
910  # load any subgroups
911  $this->Subgroups = array();
912  if (isset($Data["Subgroups"]))
913  {
914  foreach ($Data["Subgroups"] as $SubgroupData)
915  {
916  $this->Subgroups[] = new SearchParameterSet($SubgroupData);
917  }
918  }
919  }
920 
925  private function ConvertToLegacyGroup()
926  {
927  $Group = array();
928  # for each set of search strings
929  foreach ($this->SearchStrings as $Field => $Strings)
930  {
931  # get text name of field
932  $FieldName = call_user_func(self::$PrintableFieldFunction, $Field);
933 
934  # add set to group
935  $Group["SearchStrings"][$FieldName] = $Strings;
936  }
937 
938  # for each keyword search string
939  foreach ($this->KeywordSearchStrings as $String)
940  {
941  # add string to keyword entry in group
942  $Group["SearchStrings"]["XXXKeywordXXX"][] = $String;
943  }
944 
945  # if we had any search terms
946  if (count($Group))
947  {
948  # smash single-value arrays to a scalar
949  foreach ($Group["SearchStrings"] as &$Tgt)
950  {
951  if (count($Tgt) == 1)
952  {
953  $Tgt = current($Tgt);
954  }
955  }
956 
957  # set logic for search group
958  $Group["Logic"] = ($this->Logic == "OR")
960  }
961 
962  return $Group;
963  }
964 
969  private function LoadFromLegacyGroup($Group)
970  {
971  # set logic appropriately
972  $this->Logic(
973  ($Group["Logic"] == SearchEngine::LOGIC_OR) ?
974  "OR" : "AND" );
975 
976  # if this group had no search strings, we're done
977  if (!isset($Group["SearchStrings"]))
978  {
979  return;
980  }
981 
982  # otherwise, load the search strings
983  foreach ($Group["SearchStrings"] as $Field => $Params)
984  {
985  # skip empty groups
986  if (count($Params)==0)
987  {
988  continue;
989  }
990 
991  $this->AddParameter( $Params,
992  ($Field == "XXXKeywordXXX" ? NULL : $Field) );
993 
994  }
995  }
996 
1002  private function GetAsUrlParameters($SetPrefix = "")
1003  {
1004  # for each search string group in set
1005  $Params = array();
1006  foreach ($this->SearchStrings as $FieldId => $Values)
1007  {
1008  # get numeric version of field ID if not already numeric
1009  if (!is_numeric($FieldId))
1010  {
1011  $FieldId = call_user_func(self::$CanonicalFieldFunction, $FieldId);
1012  }
1013 
1014  # for each search string in group
1015  $ParamSuffix = "";
1016  foreach ($Values as $Value)
1017  {
1018  # check for too many search strings for this field
1019  if ($ParamSuffix == "Z")
1020  {
1021  throw new Exception("Maximum search parameter complexity"
1022  ." exceeded: more than 26 search parameters for"
1023  ." field ID ".$FieldId.".");
1024  }
1025 
1026  # add search string to URL
1027  $Params[self::$UrlParameterPrefix.$SetPrefix
1028  .$FieldId.$ParamSuffix] = $Value;
1029  $ParamSuffix = ($ParamSuffix == "") ? "A"
1030  : chr(ord($ParamSuffix) + 1);
1031  }
1032  }
1033 
1034  # for each keyword search string
1035  $ParamSuffix = "";
1036  foreach ($this->KeywordSearchStrings as $Value)
1037  {
1038  # check for too many keyword search strings
1039  if ($ParamSuffix == "Z")
1040  {
1041  throw new Exception("Maximum search parameter complexity"
1042  ." exceeded: more than 26 keyword search parameters.");
1043  }
1044 
1045  # add search string to URL
1046  $Params[self::$UrlParameterPrefix.$SetPrefix
1047  .self::URL_KEYWORD_INDICATOR.$ParamSuffix] = $Value;
1048  $ParamSuffix = ($ParamSuffix == "") ? "A"
1049  : chr(ord($ParamSuffix) + 1);
1050  }
1051 
1052  # add logic if not default
1053  if ($this->Logic != self::DEFAULT_LOGIC)
1054  {
1055  $Params[self::$UrlParameterPrefix.$SetPrefix
1056  .self::URL_LOGIC_INDICATOR] = $this->Logic;
1057  }
1058 
1059  # for each search parameter subgroup
1060  $SetLetter = "A";
1061  foreach ($this->Subgroups as $Subgroup)
1062  {
1063  # check for too many subgroups
1064  if ($SetLetter == "Z")
1065  {
1066  throw new Exception("Maximum search parameter complexity"
1067  ." exceeded: more than 24 search parameter subgroups.");
1068  }
1069 
1070  # retrieve URL string for subgroup and add it to URL
1071  $Params = array_merge($Params, $Subgroup->GetAsUrlParameters(
1072  $SetPrefix.$SetLetter));
1073 
1074  # move to next set letter
1075  $SetLetter = ($SetLetter == chr(ord(self::URL_KEYWORD_INDICATOR) - 1))
1076  ? chr(ord(self::URL_KEYWORD_INDICATOR) + 1)
1077  : chr(ord($SetLetter) + 1);
1078  }
1079 
1080  # return constructed URL parameter string to caller
1081  return $Params;
1082  }
1083 
1090  private function SetFromUrlParameters($UrlParameters)
1091  {
1092  # if string was passed in
1093  if (is_string($UrlParameters))
1094  {
1095  # split string into parameter array
1096  $Params = explode("&", $UrlParameters);
1097 
1098  # pare down parameter array to search parameter elements
1099  # and strip off search parameter prefix
1100  $NewUrlParameters = array();
1101  foreach ($Params as $Param)
1102  {
1103  if (strpos($Param, self::$UrlParameterPrefix) === 0)
1104  {
1105  list($Index, $Value) = explode("=", $Param);
1106  $NewUrlParameters[$Index] = urldecode($Value);
1107  }
1108  }
1109  $UrlParameters = $NewUrlParameters;
1110  }
1111 
1112  # for each search parameter
1113  foreach ($UrlParameters as $ParamName => $SearchString)
1114  {
1115  # strip off standard search parameter prefix
1116  $ParamName = substr($ParamName, strlen(self::$UrlParameterPrefix));
1117 
1118  # split parameter into component parts
1119  $SplitResult = preg_match("/^([".self::URL_KEYWORDFREE_RANGE."]*)"
1120  ."([0-9".self::URL_KEYWORD_INDICATOR."]+)([A-Z]*)$/",
1121  $ParamName, $Matches);
1122 
1123  # if split was successful
1124  if ($SplitResult === 1)
1125  {
1126  # pull components from split pieces
1127  $SetPrefix = $Matches[1];
1128  $FieldId = $Matches[2];
1129  $ParamSuffix = $Matches[3];
1130 
1131  # if set prefix indicates parameter is part of our set
1132  if ($SetPrefix == "")
1133  {
1134  switch ($FieldId)
1135  {
1136  case self::URL_LOGIC_INDICATOR:
1137  # set logic
1138  $this->Logic($SearchString);
1139  break;
1140 
1141  case self::URL_KEYWORD_INDICATOR:
1142  # add string to keyword searches
1143  $this->KeywordSearchStrings[] = $SearchString;
1144  break;
1145 
1146  default:
1147  # add string to searches for appropriate field
1148  $this->SearchStrings[$FieldId][] = $SearchString;
1149  break;
1150  }
1151  }
1152  else
1153  {
1154  # add parameter to array for subgroup
1155  $SubgroupIndex = $SetPrefix[0];
1156  $SubgroupPrefix = (strlen($SetPrefix) > 1)
1157  ? substr($SetPrefix, 1) : "";
1158  $SubgroupParamIndex = self::$UrlParameterPrefix
1159  .$SubgroupPrefix.$FieldId.$ParamSuffix;
1160  $SubgroupParameters[$SubgroupIndex][$SubgroupParamIndex]
1161  = $SearchString;
1162  }
1163  }
1164  }
1165 
1166  # if subgroups were found
1167  if (isset($SubgroupParameters))
1168  {
1169  # for each identified subgroup
1170  foreach ($SubgroupParameters as $SubgroupIndex => $Parameters)
1171  {
1172  # create subgroup and set parameters
1173  $Subgroup = new SearchParameterSet();
1174  $Subgroup->SetFromUrlParameters($Parameters);
1175 
1176  # add subgroup to our set
1177  $this->Subgroups[] = $Subgroup;
1178  }
1179  }
1180  }
1181 
1187  private static function NormalizeField($Field)
1188  {
1189  if (($Field !== NULL) && isset(self::$CanonicalFieldFunction))
1190  {
1191  $Field = call_user_func(self::$CanonicalFieldFunction, $Field);
1192  }
1193  return $Field;
1194  }
1195 }
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...
GetSearchStringsForField($Field, $IncludeSubgroups=TRUE)
Get search strings for specified field.
static SetPrintableFieldFunction($Func)
Register function used to retrieve a printable value for a field.
ItemTypes($ItemTypes=FALSE)
Get/set allowed item types.
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.
__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.
GetSubgroups()
Get parameter subgroups.
ParameterCount()
Get number of search parameters in set, including those in subgroups.