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