CWIS Developer Documentation

- Home
- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

StdLib.php
Go to the documentation of this file.
1 <?PHP
2 #
3 # FILE: StdLib.php
4 #
5 # Part of the ScoutLib application support library
6 # Copyright 2016 Edward Almasy and Internet Scout Research Group
7 # http://scout.wisc.edu
8 #
9
14 class StdLib
15 {
16
17 # ---- PUBLIC INTERFACE --------------------------------------------------
18
26 public static function GetCallerInfo($Element = NULL)
27 {
28 $Trace = version_compare(PHP_VERSION, "5.4.0", ">=")
29 ? debug_backtrace(FALSE, 2) : debug_backtrace(FALSE);
30 $FullFileName = $Trace[1]["file"];
31 $Info = [
32 "FileName" => basename($FullFileName),
33 "RelativeFileName" => str_replace(getcwd()."/", "", $FullFileName),
34 "FullFileName" => $FullFileName,
35 "LineNumber" => $Trace[1]["line"],
36 ];
37 return ($Element === NULL) ? $Info : $Info[$Element];
38 }
39
44 public static function GetMyCaller()
45 {
46 $Trace = version_compare(PHP_VERSION, "5.4.0", ">=")
47 ? debug_backtrace(FALSE, 2) : debug_backtrace(FALSE);
48 $FileName = $Trace[1]["file"];
49 $Caller = basename($FileName).":".$Trace[1]["line"];
50 return $Caller;
51 }
52
69 public static function CheckMyCaller(
70 $DesiredCaller,
71 $ExceptionMsg = NULL)
72 {
73 # retrieve caller info
74 $Trace = version_compare(PHP_VERSION, "5.4.0", ">=")
75 ? debug_backtrace(FALSE, 3) : debug_backtrace(FALSE);
76 $FullFile = $Trace[1]["file"];
77 $File = basename($FullFile);
78 $Line = $Trace[1]["line"];
79 $Class = isset($Trace[2]["class"]) ? $Trace[2]["class"] : "";
80 $Function = isset($Trace[2]["function"]) ? $Trace[2]["function"] : "";
81
82 # if caller does not match desired caller
83 if (($DesiredCaller != $Class)
84 && ($DesiredCaller != $Class."::".$Function)
85 && ($DesiredCaller != $Class.$Function)
86 && ($DesiredCaller != $File)
87 && ($DesiredCaller != $File.":".$Line))
88 {
89 # if exception message supplied
90 if ($ExceptionMsg !== NULL)
91 {
92 # make any needed substitutions in exception message
93 $Msg = str_replace(
94 array(
95 "%FILE%",
96 "%LINE%",
97 "%FULLFILE%",
98 "%CLASS%",
99 "%FUNCTION%",
100 "%METHOD%"),
101 array(
102 $File,
103 $Line,
104 $FullFile,
105 $Class,
106 $Function,
107 $Class."::".$Function),
108 $ExceptionMsg);
109
110 # throw exception
111 throw new Exception($Msg);
112 }
113 else
114 {
115 # report to our caller that their caller was not the desired one
116 return FALSE;
117 }
118 }
119
120 # report to our caller that their caller was not the desired one
121 return TRUE;
122 }
123
130 public static function GetBacktraceAsString($IncludeArgs = TRUE)
131 {
132 # get backtrace text
133 ob_start();
134 $TraceOpts = $IncludeArgs ? 0 : DEBUG_BACKTRACE_IGNORE_ARGS;
135 debug_print_backtrace($TraceOpts);
136 $TraceString = ob_get_contents();
137 ob_end_clean();
138
139 # remove this function from backtrace entries
140 $TraceString = preg_replace(
141 "/^#0\s+".__METHOD__."[^\n]*\n/", "", $TraceString, 1);
142
143 # renumber backtrace entries
144 $TraceString = preg_replace_callback("/^#(\d+)/m", function($Matches)
145 {
146 return "#".($Matches[1] - 1);
147 },
148 $TraceString);
149
150 # strip leading path off files names
151 $HomeDir = dirname($_SERVER["SCRIPT_FILENAME"]);
152 $TraceString = preg_replace("%".preg_quote($HomeDir, "%")."/%",
153 "", $TraceString);
154
155 # return backtrace string to caller
156 return $TraceString;
157 }
158
164 public static function Pluralize($Word)
165 {
166 # return word unchanged if singular and plural are the same
167 if (in_array(strtolower($Word), self::$UncountableWords))
168 {
169 return $Word;
170 }
171
172 # check for irregular singular forms
173 foreach (self::$IrregularWords as $Pattern => $Result)
174 {
175 $Pattern = '/'.$Pattern.'$/i';
176 if (preg_match($Pattern, $Word))
177 {
178 return preg_replace($Pattern, $Result, $Word);
179 }
180 }
181
182 # check for matches using regular expressions
183 foreach (self::$PluralizePatterns as $Pattern => $Result)
184 {
185 if (preg_match($Pattern, $Word))
186 {
187 return preg_replace($Pattern, $Result, $Word);
188 }
189 }
190
191 # return word unchanged if we could not process it
192 return $Word;
193 }
194
200 public static function Singularize($Word)
201 {
202 # return word unchanged if singular and plural are the same
203 if (in_array(strtolower($Word), self::$UncountableWords))
204 {
205 return $Word;
206 }
207
208 # check for irregular plural forms
209 foreach (self::$IrregularWords as $Result => $Pattern)
210 {
211 $Pattern = '/'.$Pattern.'$/i';
212 if (preg_match($Pattern, $Word))
213 {
214 return preg_replace($Pattern, $Result, $Word);
215 }
216 }
217
218 # check for matches using regular expressions
219 foreach (self::$SingularizePatterns as $Pattern => $Result)
220 {
221 if (preg_match($Pattern, $Word))
222 {
223 return preg_replace($Pattern, $Result, $Word);
224 }
225 }
226
227 # return word unchanged if we could not process it
228 return $Word;
229 }
230
239 public static function NeatlyTruncateString(
240 $String, $MaxLength, $BreakAnywhere = FALSE)
241 {
242 $TagStrippedString = strip_tags(html_entity_decode($String));
243
244 # if the string contained no HTML tags, we can just treat it as text
245 if ($String == $TagStrippedString)
246 {
247 $Length = self::strlen($String);
248
249 # if string was short enough, we need not do anything
250 if ($Length <= $MaxLength)
251 {
252 return $String;
253 }
254
255 # if BreakAnywhere is set, just chop at the max length
256 if ($BreakAnywhere)
257 {
258 $BreakPos = $MaxLength;
259 }
260 # otherwise look for an acceptable breakpoint
261 else
262 {
263 $BreakPos = self::strrpos($String, " ", -($Length - $MaxLength));
264
265 # if we couldn't find a breakpoint, just chop at max length
266 if ($BreakPos === FALSE)
267 {
268 $BreakPos = $MaxLength;
269 }
270 }
271
272 $Result = self::substr($String, 0, $BreakPos);
273
274 # tack on the ellipsis
275 $Result .= "...";
276 }
277 # otherwise, we're in an HTML string and we have to account for
278 # how many characters are actually displayed when the string will be
279 # rendered
280 else
281 {
282 # if there aren't enough non-whitespace displayed characters to
283 # exceed the max length, bail because we don't need to do
284 # anything
285 if (self::strlen(trim($TagStrippedString)) <= $MaxLength)
286 {
287 return $String;
288 }
289
290 # okay, the hard way -- we have to do some limited parsing
291 # of html and attempt to count the number of printing characters
292 # as we're doing that. to accomplish this, we'll build a
293 # little state machine and iterate over the characters one at a
294 # time
295
296 # split the string into characters (annoyingly, split()/mb_split()
297 # cannot do this, so we have to use preg_split() in unicode mode)
298 $Tokens = preg_split('%%u', $String, -1, PREG_SPLIT_NO_EMPTY);
299
300 # define our states
301 $S_Text = 0;
302 $S_MaybeTag = 1;
303 $S_MaybeEntity = 2;
304 $S_Tag = 3;
305 $S_Quote = 4;
306
307 # max length of an HTML Entity
308 $MaxEntityLength = 8;
309
310 # track how much we have displayed
311 $DisplayedLength = 0;
312
313 $Buffer = ""; # for characters we're not sure about
314 $BufLen = 0; # count of buffered characters
315 $Result = ""; # for characters we've included
316 $QuoteChar =""; # quote character in use
317
318 # start in the 'text state'
319 $State = $S_Text;
320
321 # iterate over all our tokens
322 foreach ($Tokens as $Token)
323 {
324 switch ($State)
325 {
326 # text state handles words that will be displayed
327 case $S_Text:
328 switch($Token)
329 {
330 # look for characters that can end a word
331 case "<":
332 case "&":
333 case " ":
334 # if we've buffered up a word
335 if ($BufLen > 0)
336 {
337 # and if displaying that word exceeds
338 # our length, then we're done
339 if ($DisplayedLength + $BufLen > $MaxLength)
340 {
341 break 3;
342 }
343
344 # otherwise, add the buffered word to our display
345 $Result .= $Buffer;
346 $DisplayedLength += $BufLen;
347 }
348
349 # buffer this character
350 $Buffer = $Token;
351 $BufLen = 1;
352
353 # if it could have been the start of a tag or an entity,
354 # change state appropriately
355 if ($Token != " ")
356 {
357 $State = ($Token == "<") ? $S_MaybeTag :
358 $S_MaybeEntity;
359 }
360 break;
361
362 # for characters that can't break a word, just buffer them
363 default:
364 $Buffer .= $Token;
365 $BufLen++;
366 break;
367 }
368 break;
369
370 # MaybeTag state checks if a < began a tag or not
371 case $S_MaybeTag:
372 # tags start with alpha characters (like <b>)
373 # or a slash (like </b>)
374 if (ctype_alpha($Token) || $Token == "/")
375 {
376 # if this was a tag, output it, output it,
377 # clear our buffer, and move to the Tag state
378 $Result .= $Buffer.$Token;
379 $Buffer = "";
380 $BufLen = 0;
381 $State = $S_Tag;
382 }
383 else
384 {
385 # otherwise, check if displaying this character would
386 # exceed our length. bail if so
387 if ($DisplayedLength + 1 > $MaxLength)
388 {
389 break 2;
390 }
391 # if not, output the characters, clear our buffer,
392 # move to the Text state
393 $Result .= $Buffer.$Token;
394 $DisplayedLength++;
395 $Buffer = "";
396 $BufLen = 0;
397 $State = $S_Text;
398 }
399 break;
400
401 # Tag state processes the contents of a tag
402 case $S_Tag:
403 # always output tag contents
404 $Result .= $Token;
405
406 # check if this is the beginning of a quoted string,
407 # changing state appropriately if so
408 if ($Token == "\"" || $Token == "'")
409 {
410 $QuoteChar = $Token;
411 $State = $S_Quote;
412 }
413 # if this is the end of the tag, go back to Text state
414 elseif ($Token == ">")
415 {
416 $State = $S_Text;
417 }
418 break;
419
420 # Quote state processes quoted attributes
421 case $S_Quote:
422 # always output quote contents
423 $Result .= $Token;
424
425 # if we've found the matching quote character,
426 # return to the Tag state
427 if ($Token == $QuoteChar)
428 {
429 $State = $S_Tag;
430 }
431 break;
432
433 # MaybeEntity decides if we're enjoying an HTML entity
434 # or just an ampersand
435 case $S_MaybeEntity:
436 # buffer this token
437 $Buffer.= $Token;
438 $BufLen++;
439
440 # if it was a space, then we're not in an entity
441 # as they cannot contain spaces
442 if ($Token == " ")
443 {
444 # check if we should be fone
445 if ($DisplayedLength + $BufLen > $MaxLength)
446 {
447 break 2;
448 }
449 # if not, output the buffer, clear it, and return to Text
450 $Result .= $Buffer;
451 $DisplayedLength += $BufLen;
452 $Buffer = "";
453 $BufLen = 0;
454 $State = $S_Text;
455 }
456 # if we have &xxxx; then count that as a single character entity,
457 # output it, clear the buffer, and return to Text
458 elseif ($Token == ";")
459 {
460 $Result .= $Buffer;
461 $DisplayedLength++;
462 $Buffer = "";
463 $BufLen = 0;
464 $State = $S_Text;
465 }
466 # if this has been too many characters without a ;
467 # for it to be an entity, return to text
468 elseif ($BufLen > 8)
469 {
470 $State = $S_Text;
471 }
472
473 break;
474 }
475 }
476
477 # tack on the ellipsis
478 $Result .= "...";
479
480 # if our string contained HTML tags that we may need to close
481 if (preg_match_all("%<(/?[a-z]+)[^>]*>%", $Result, $Matches))
482 {
483 # pull out matches for the names of tags
484 $Matches = $Matches[1];
485
486 # build up an array of open tags
487 $Tags = array();
488 while ( ($Tag = array_shift($Matches)) !== NULL )
489 {
490 # if this was not a close tag, prepend it to our array
491 if (self::substr($Tag, 0, 1) != "/")
492 {
493 array_unshift($Tags, $Tag);
494 }
495 # if this tag is not self-closing, append it to our list of open tags
496 elseif (self::substr($Tag, -1) != "/")
497 {
498 # if this was a close tag, look to see if this tag was open
499 $Tgt = array_search(self::substr($Tag, 1), $Tags);
500 if ($Tgt !== FALSE)
501 {
502 # if so, remove this tag from our list
503 unset($Tags[$Tgt]);
504 }
505 }
506 }
507
508 # iterate over open tags, closing them as we go
509 while ( ($Tag = array_shift($Tags)) !== NULL)
510 {
511 $Result .= "</".$Tag.">";
512 }
513 }
514 }
515
516 return $Result;
517 }
518
523 public static function substr()
524 {
525 return self::CallMbStringFuncIfAvailable(__FUNCTION__, func_get_args(), 3);
526 }
527
532 public static function strpos()
533 {
534 return self::CallMbStringFuncIfAvailable(__FUNCTION__, func_get_args(), 3);
535 }
536
541 public static function strrpos()
542 {
543 return self::CallMbStringFuncIfAvailable(__FUNCTION__, func_get_args(), 3);
544 }
545
550 public static function strlen()
551 {
552 return self::CallMbStringFuncIfAvailable(__FUNCTION__, func_get_args(), 1);
553 }
554
563 public static function EncodeStringForCdata($String)
564 {
565 return "<![CDATA[".str_replace("]]>", "]]]]><![CDATA[>", $String)."]]>";
566 }
567
575 public static function SortCompare($A, $B)
576 {
577 if ($A == $B)
578 {
579 return 0;
580 }
581 else
582 {
583 return ($A < $B) ? -1 : 1;
584 }
585 }
586
599 public static function GetLatLngForZipCode($Zip)
600 {
601 static $ZipCache = array();
602
603 # if we don't have a cached value for this zip, look one up
604 if (!isset($ZipCache[$Zip]))
605 {
606 # try to open our zip code database
607 $FHandle = fopen(dirname(__FILE__)."/StdLib--ZipCodeCoords.txt", "r");
608
609 # if we couldn't open the file, we can't look up a value
610 if ($FHandle === FALSE)
611 {
612 throw new Exception("Unable to open zip code coordinates file");
613 }
614
615 # iterate over our database until we find the desired zip
616 # or run out of database
617 while (($Line = fgetcsv($FHandle, 0, "\t")) !== FALSE)
618 {
619 if ($Line[0] == $Zip)
620 {
621 $ZipCache[$Zip] = array(
622 "Lat" => $Line[1], "Lng" => $Line[2]);
623 break;
624 }
625 }
626
627 # if we've scanned the entire file and have no coords for
628 # this zip, cache a failure
629 if (!isset($ZipCache[$Zip]))
630 {
631 $ZipCache[$Zip] = FALSE;
632 }
633 }
634
635 # hand back cached value
636 return $ZipCache[$Zip];
637 }
638
646 public static function ZipCodeDistance($ZipA, $ZipB)
647 {
648
649 $FirstPoint = self::GetLatLngForZipCode($ZipA);
650 $SecondPoint = self::GetLatLngForZipCode($ZipB);
651
652 # if we scanned the whole file and lack data for either of our
653 # points, return NULL
654 if ($FirstPoint === FALSE || $SecondPoint === FALSE)
655 {
656 return FALSE;
657 }
658
659 return self::ComputeGreatCircleDistance(
660 $FirstPoint["Lat"], $FirstPoint["Lng"],
661 $SecondPoint["Lat"], $SecondPoint["Lng"]);
662 }
663
673 public static function ComputeGreatCircleDistance($LatSrc, $LonSrc,
674 $LatDst, $LonDst)
675 {
676 # See http://en.wikipedia.org/wiki/Great-circle_distance
677
678 # Convert it all to Radians
679 $Ps = deg2rad($LatSrc);
680 $Ls = deg2rad($LonSrc);
681 $Pf = deg2rad($LatDst);
682 $Lf = deg2rad($LonDst);
683
684 # Compute the central angle
685 return 3958.756 * atan2(
686 sqrt( pow(cos($Pf)*sin($Lf-$Ls), 2) +
687 pow(cos($Ps)*sin($Pf) -
688 sin($Ps)*cos($Pf)*cos($Lf-$Ls), 2)),
689 sin($Ps)*sin($Pf)+cos($Ps)*cos($Pf)*cos($Lf-$Ls));
690
691 }
692
702 public static function ComputeBearing($LatSrc, $LonSrc,
703 $LatDst, $LonDst)
704 {
705 # See http://mathforum.org/library/drmath/view/55417.html
706
707 # Convert angles to radians
708 $Ps = deg2rad($LatSrc);
709 $Ls = deg2rad($LonSrc);
710 $Pf = deg2rad($LatDst);
711 $Lf = deg2rad($LonDst);
712
713 return rad2deg(atan2(sin($Lf-$Ls)*cos($Pf),
714 cos($Ps)*sin($Pf)-sin($Ps)*cos($Pf)*cos($Lf-$Ls)));
715 }
716
724 public static function ArrayPermutations($Items, $Perms = array())
725 {
726 if (empty($Items))
727 {
728 $Result = array($Perms);
729 }
730 else
731 {
732 $Result = array();
733 for ($Index = count($Items) - 1; $Index >= 0; --$Index)
734 {
735 $NewItems = $Items;
736 $NewPerms = $Perms;
737 list($Segment) = array_splice($NewItems, $Index, 1);
738 array_unshift($NewPerms, $Segment);
739 $Result = array_merge($Result,
740 self::ArrayPermutations($NewItems, $NewPerms));
741 }
742 }
743 return $Result;
744 }
745
752 public static function GetUsStatesList()
753 {
754 return array(
755 "AL" => "Alabama",
756 "AK" => "Alaska",
757 "AZ" => "Arizona",
758 "AR" => "Arkansas",
759 "CA" => "California",
760 "CO" => "Colorado",
761 "CT" => "Connecticut",
762 "DE" => "Delaware",
763 "DC" => "District of Columbia",
764 "FL" => "Florida",
765 "GA" => "Georgia",
766 "HI" => "Hawaii",
767 "ID" => "Idaho",
768 "IL" => "Illinois",
769 "IN" => "Indiana",
770 "IA" => "Iowa",
771 "KS" => "Kansas",
772 "KY" => "Kentucky",
773 "LA" => "Louisiana",
774 "ME" => "Maine",
775 "MD" => "Maryland",
776 "MA" => "Massachusetts",
777 "MI" => "Michigan",
778 "MN" => "Minnesota",
779 "MS" => "Mississippi",
780 "MO" => "Missouri",
781 "MT" => "Montana",
782 "NE" => "Nebraska",
783 "NV" => "Nevada",
784 "NH" => "New Hampshire",
785 "NJ" => "New Jersey",
786 "NM" => "New Mexico",
787 "NY" => "New York",
788 "NC" => "North Carolina",
789 "ND" => "North Dakota",
790 "OH" => "Ohio",
791 "OK" => "Oklahoma",
792 "OR" => "Oregon",
793 "PA" => "Pennsylvania",
794 "RI" => "Rhode Island",
795 "SC" => "South Carolina",
796 "SD" => "South Dakota",
797 "TN" => "Tennessee",
798 "TX" => "Texas",
799 "UT" => "Utah",
800 "VT" => "Vermont",
801 "VA" => "Virginia",
802 "WA" => "Washington",
803 "WV" => "West Virginia",
804 "WI" => "Wisconsin",
805 "WY" => "Wyoming",
806 );
807 }
808
817 public static function AdjustHexColor($Color, $LAdjust, $SAdjust = 0)
818 {
819 # split into RGB components
820 $Color = str_replace("#", "", $Color);
821 $Pieces = str_split($Color, 2);
822 $R = hexdec($Pieces[0]);
823 $G = hexdec($Pieces[1]);
824 $B = hexdec($Pieces[2]);
825
826 # convert RGB to HSL
827 list($H, $S, $L) = array_values(self::RgbToHsl($R, $G, $B));
828
829 # adjust luminance and saturation
830 $L = $L + ($L * ($LAdjust / 100));
831 $S = $S + ($S * ($SAdjust / 100));
832
833 # convert HSL to RGB
834 extract(self::HslToRgb($H, $S, $L));
835
836 # assemble RGB components back into hex color string
837 $FormFunc = function ($Comp) {
838 return str_pad(strtoupper(dechex($Comp)), 2, "0", STR_PAD_LEFT);
839 };
840 $NewColor = "#".$FormFunc($R).$FormFunc($G).$FormFunc($B);
841
842 # return new color to caller
843 return $NewColor;
844 }
845
856 public static function GetConstantName($ClassName, $Value, $Prefix = NULL)
857 {
858 # retrieve all constants for class
859 if (is_object($ClassName))
860 {
861 $ClassName = get_class($ClassName);
862 }
863 $Reflect = new ReflectionClass($ClassName);
864 $Constants = $Reflect->getConstants();
865
866 # for each constant
867 foreach ($Constants as $CName => $CValue)
868 {
869 # if value matches and prefix (if supplied) matches
870 if (($CValue == $Value)
871 && (($Prefix === NULL) || (strpos($CName, $Prefix) === 0)))
872 {
873 # return name to caller
874 return $CName;
875 }
876 }
877
878 # report to caller that no matching constant was found
879 return NULL;
880 }
881
888 public static function HexToRgba($Hex, $Opacity = 1)
889 {
890 $Hex = preg_replace('/[^a-fA-F0-9]/', "", $Hex);
891
892 if (strlen($Hex) == 6)
893 {
894 $Color["R"] = hexdec($Hex{0}.$Hex{1});
895 $Color["G"] = hexdec($Hex{2}.$Hex{3});
896 $Color["B"] = hexdec($Hex{4}.$Hex{5});
897 }
898 else if (strlen($Hex) == 3)
899 {
900 $Color["R"] = hexdec($Hex{0});
901 $Color["G"] = hexdec($Hex{1});
902 $Color["B"] = hexdec($Hex{2});
903 }
904
905 return "rgba("
906 .$Color["R"].","
907 .$Color["G"].","
908 .$Color["B"].","
909 .$Opacity.")";
910 }
911
913 const SQL_DATE_FORMAT = "Y-m-d H:i:s";
914
915
916 # ---- PRIVATE INTERFACE -------------------------------------------------
917
918 private static $PluralizePatterns = array(
919 '/(quiz)$/i' => "$1zes",
920 '/^(ox)$/i' => "$1en",
921 '/([m|l])ouse$/i' => "$1ice",
922 '/(matr|vert|ind)ix|ex$/i' => "$1ices",
923 '/(x|ch|ss|sh)$/i' => "$1es",
924 '/([^aeiouy]|qu)y$/i' => "$1ies",
925 '/(hive)$/i' => "$1s",
926 '/(?:([^f])fe|([lr])f)$/i' => "$1$2ves",
927 '/(shea|lea|loa|thie)f$/i' => "$1ves",
928 '/sis$/i' => "ses",
929 '/([ti])um$/i' => "$1a",
930 '/(tomat|potat|ech|her|vet)o$/i'=> "$1oes",
931 '/(bu)s$/i' => "$1ses",
932 '/(alias)$/i' => "$1es",
933 '/(octop)us$/i' => "$1i",
934 '/(ax|test)is$/i' => "$1es",
935 '/(us)$/i' => "$1es",
936 '/s$/i' => "s",
937 '/$/' => "s"
938 );
939 private static $SingularizePatterns = array(
940 '/(quiz)zes$/i' => "$1",
941 '/(matr)ices$/i' => "$1ix",
942 '/(vert|ind)ices$/i' => "$1ex",
943 '/^(ox)en$/i' => "$1",
944 '/(alias)es$/i' => "$1",
945 '/(octop|vir)i$/i' => "$1us",
946 '/(cris|ax|test)es$/i' => "$1is",
947 '/(shoe)s$/i' => "$1",
948 '/(o)es$/i' => "$1",
949 '/(bus)es$/i' => "$1",
950 '/([m|l])ice$/i' => "$1ouse",
951 '/(x|ch|ss|sh)es$/i' => "$1",
952 '/(m)ovies$/i' => "$1ovie",
953 '/(s)eries$/i' => "$1eries",
954 '/([^aeiouy]|qu)ies$/i' => "$1y",
955 '/([lr])ves$/i' => "$1f",
956 '/(tive)s$/i' => "$1",
957 '/(hive)s$/i' => "$1",
958 '/(li|wi|kni)ves$/i' => "$1fe",
959 '/(shea|loa|lea|thie)ves$/i'=> "$1f",
960 '/(^analy)ses$/i' => "$1sis",
961 '/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' => "$1$2sis",
962 '/([ti])a$/i' => "$1um",
963 '/(n)ews$/i' => "$1ews",
964 '/(h|bl)ouses$/i' => "$1ouse",
965 '/(corpse)s$/i' => "$1",
966 '/(us)es$/i' => "$1",
967 '/s$/i' => ""
968 );
969 private static $IrregularWords = array(
970 'move' => 'moves',
971 'foot' => 'feet',
972 'goose' => 'geese',
973 'sex' => 'sexes',
974 'child' => 'children',
975 'man' => 'men',
976 'tooth' => 'teeth',
977 'person' => 'people'
978 );
979 private static $UncountableWords = array(
980 'sheep',
981 'fish',
982 'deer',
983 'series',
984 'species',
985 'money',
986 'rice',
987 'information',
988 'equipment'
989 );
990
998 private static function CallMbStringFuncIfAvailable(
999 $Func,
1000 $Args,
1001 $NumPlainArgs)
1002 {
1003 if (function_exists("mb_".$Func))
1004 {
1005 $FuncToCall = "mb_".$Func;
1006 }
1007 else
1008 {
1009 if (count($Args) > $NumPlainArgs)
1010 {
1011 throw new Exception(
1012 "Function mb_".$Func."() required but not available.");
1013 }
1014 $FuncToCall = $Func;
1015 }
1016 return call_user_func_array($FuncToCall, $Args);
1017 }
1018
1030 private static function RgbToHsl($R, $G, $B)
1031 {
1032 # ensure incoming values are within range
1033 $R = max(min($R, 255), 0);
1034 $G = max(min($G, 255), 0);
1035 $B = max(min($B, 255), 0);
1036
1037 # scale RGB values to range of 0-1
1038 $R /= 255;
1039 $G /= 255;
1040 $B /= 255;
1041
1042 # determine RGB range
1043 $MinVal = min($R, $G, $B);
1044 $MaxVal = max($R, $G, $B);
1045 $MaxDif = $MaxVal - $MinVal;
1046
1047 # calculate luminance
1048 $L = ($MinVal + $MaxVal) / 2.0;
1049 $L *= 100;
1050
1051 # if RGB are all equal
1052 if ($MinVal == $MaxVal)
1053 {
1054 # no hue or saturation
1055 $S = 0;
1056 $H = 0;
1057 }
1058 else
1059 {
1060 # calculate saturation
1061 if ($L < 50)
1062 {
1063 $S = $MaxDif / ($MinVal + $MaxVal);
1064 }
1065 else
1066 {
1067 $S = $MaxDif / ((2.0 - $MaxVal) - $MinVal);
1068 }
1069 $S *= 100;
1070
1071 # calculate hue
1072 switch ($MaxVal)
1073 {
1074 case $R:
1075 $H = ($G - $B) / $MaxDif;
1076 break;
1077
1078 case $G:
1079 $H = ($B - $R) / $MaxDif;
1080 $H += 2.0;
1081 break;
1082
1083 case $B:
1084 $H = ($R - $G) / $MaxDif;
1085 $H += 4.0;
1086 break;
1087 }
1088 $H *= 60;
1089 if ($H < 0)
1090 {
1091 $H += 360;
1092 }
1093 }
1094
1095 # return calculated values to caller
1096 return [ "H" => $H, "S" => $S, "L" => $L ];
1097 }
1098
1109 private static function HslToRgb($H, $S, $L)
1110 {
1111 # ensure incoming values are within range
1112 $H = max(min($H, 360), 0);
1113 $S = max(min($S, 100), 0);
1114 $L = max(min($L, 100), 0);
1115
1116 # scale HSL to range of 0-1
1117 $H /= 360;
1118 $S /= 100;
1119 $L /= 100;
1120
1121 # if no saturation
1122 if ($S == 0)
1123 {
1124 # result is greyscale, with equal RGB values based on luminance
1125 $R = $L * 255;
1126 $G = $R;
1127 $B = $R;
1128 }
1129 else
1130 {
1131 # calculate RGB
1132 $R = self::HslToRgbComponent(($H + (1 / 3)), $S, $L) * 255;
1133 $G = self::HslToRgbComponent($H, $S, $L) * 255;
1134 $B = self::HslToRgbComponent(($H - (1 / 3)), $S, $L) * 255;
1135 }
1136
1137 # return calculated values to caller
1138 return [ "R" => $R, "G" => $G, "B" => $B ];
1139 }
1140
1151 private static function HslToRgbComponent($H, $S, $L)
1152 {
1153 # ensure hue is in the range 0-1
1154 if ($H < 0) { $H += 1; }
1155 elseif ($H > 1) { $H -= 1; }
1156
1157 # calculate saturation/luminance adjustments
1158 if ($L < 0.5)
1159 {
1160 $Adj1 = (1 + $S) * $L;
1161 }
1162 else
1163 {
1164 $Adj1 = ($S + $L) - ($S * $L);
1165 }
1166 $Adj2 = ($L * 2) - $Adj1;
1167
1168 # calculate RGB component and return it to caller
1169 if (($H * 6) < 1)
1170 {
1171 return (($Adj1 - $Adj2) * $H * 6) + $Adj2;
1172 }
1173 elseif (($H * 2) < 1)
1174 {
1175 return $Adj1;
1176 }
1177 elseif (($H * 3) < 2)
1178 {
1179 return (($Adj1 - $Adj2) * ((2 / 3) - $H) * 6) + $Adj2;
1180 }
1181 else
1182 {
1183 return $Adj2;
1184 }
1185 }
1186 }
StdLib\AdjustHexColor
static AdjustHexColor($Color, $LAdjust, $SAdjust=0)
Adjust hexadecimal RGB color by specified amount.
Definition: StdLib.php:817
StdLib\strrpos
static strrpos()
Multibyte-aware (if supported in PHP) version of strrpos().
Definition: StdLib.php:541
StdLib\CheckMyCaller
static CheckMyCaller($DesiredCaller, $ExceptionMsg=NULL)
Check the caller of the current function.
Definition: StdLib.php:69
StdLib\SortCompare
static SortCompare($A, $B)
Perform compare and return value appropriate for sort function callbacks.
Definition: StdLib.php:575
StdLib\ZipCodeDistance
static ZipCodeDistance($ZipA, $ZipB)
Compute the distance between two US ZIP codes.
Definition: StdLib.php:646
StdLib\EncodeStringForCdata
static EncodeStringForCdata($String)
Encode string to be written out in XML as CDATA.
Definition: StdLib.php:563
StdLib\strpos
static strpos()
Multibyte-aware (if supported in PHP) version of strpos().
Definition: StdLib.php:532
StdLib\GetMyCaller
static GetMyCaller()
Get string with file and line number for call to current function.
Definition: StdLib.php:44
StdLib\Pluralize
static Pluralize($Word)
Pluralize an English word.
Definition: StdLib.php:164
StdLib\GetLatLngForZipCode
static GetLatLngForZipCode($Zip)
Look up the GPS coordinates for a US ZIP code.
Definition: StdLib.php:599
StdLib
Standard utility library.
Definition: StdLib.php:14
StdLib\ArrayPermutations
static ArrayPermutations($Items, $Perms=array())
Return all possible permutations of a given array.
Definition: StdLib.php:724
StdLib\substr
static substr()
Multibyte-aware (if supported in PHP) version of substr().
Definition: StdLib.php:523
PHP
PHP
Definition: RSSClient--Test.php:12
StdLib\GetBacktraceAsString
static GetBacktraceAsString($IncludeArgs=TRUE)
Get backtrace as a string.
Definition: StdLib.php:130
StdLib\GetConstantName
static GetConstantName($ClassName, $Value, $Prefix=NULL)
Get name (string) for constant.
Definition: StdLib.php:856
StdLib\ComputeGreatCircleDistance
static ComputeGreatCircleDistance($LatSrc, $LonSrc, $LatDst, $LonDst)
Computes the distance in kilometers between two points, assuming a spherical earth.
Definition: StdLib.php:673
StdLib\ComputeBearing
static ComputeBearing($LatSrc, $LonSrc, $LatDst, $LonDst)
Computes the initial angle on a course connecting two points, assuming a spherical earth...
Definition: StdLib.php:702
StdLib\SQL_DATE_FORMAT
const SQL_DATE_FORMAT
Format to feed to date() to get SQL-compatible date/time string.
Definition: StdLib.php:913
StdLib\NeatlyTruncateString
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
StdLib\HexToRgba
static HexToRgba($Hex, $Opacity=1)
Convert a hex color string (e.g., #FF00FF") to a css &#39;rgba(&#39; format color.
Definition: StdLib.php:888
StdLib\strlen
static strlen()
Multibyte-aware (if supported in PHP) version of strlen().
Definition: StdLib.php:550
StdLib\Singularize
static Singularize($Word)
Singularize an English word.
Definition: StdLib.php:200
StdLib\GetUsStatesList
static GetUsStatesList()
Get an array of US state names with their two-letter abbreviations as the index.
Definition: StdLib.php:752
StdLib\GetCallerInfo
static GetCallerInfo($Element=NULL)
Get info about call to current function.
Definition: StdLib.php:26
$Items
$Items
Definition: RSSClient--Test.php:26