CWIS Developer Documentation
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 
23  public static function GetMyCaller()
24  {
25  $Trace = version_compare(PHP_VERSION, "5.4.0", ">=")
26  ? debug_backtrace(FALSE, 2) : debug_backtrace(FALSE);
27  $Caller = basename($Trace[1]["file"]).":".$Trace[1]["line"];
28  return $Caller;
29  }
30 
47  public static function CheckMyCaller($DesiredCaller, $ExceptionMsg = NULL)
48  {
49  # retrieve caller info
50  $Trace = version_compare(PHP_VERSION, "5.4.0", ">=")
51  ? debug_backtrace(FALSE, 3) : debug_backtrace(FALSE);
52  $FullFile = $Trace[1]["file"];
53  $File = basename($FullFile);
54  $Line = $Trace[1]["line"];
55  $Class = isset($Trace[2]["class"]) ? $Trace[2]["class"] : "";
56  $Function = isset($Trace[2]["function"]) ? $Trace[2]["function"] : "";
57 
58  # if caller does not match desired caller
59  if (($DesiredCaller != $Class)
60  && ($DesiredCaller != $Class."::".$Function)
61  && ($DesiredCaller != $Class.$Function)
62  && ($DesiredCaller != $File)
63  && ($DesiredCaller != $File.":".$Line))
64  {
65  # if exception message supplied
66  if ($ExceptionMsg !== NULL)
67  {
68  # make any needed substitutions in exception message
69  $Msg = str_replace(
70  array(
71  "%FILE%",
72  "%LINE%",
73  "%FULLFILE%",
74  "%CLASS%",
75  "%FUNCTION%",
76  "%METHOD%"),
77  array(
78  $File,
79  $Line,
80  $FullFile,
81  $Class,
82  $Function,
83  $Class."::".$Function),
84  $ExceptionMsg);
85 
86  # throw exception
87  throw new Exception($Msg);
88  }
89  else
90  {
91  # report to our caller that their caller was not the desired one
92  return FALSE;
93  }
94  }
95 
96  # report to our caller that their caller was not the desired one
97  return TRUE;
98  }
99 
105  public static function Pluralize($Word)
106  {
107  # return word unchanged if singular and plural are the same
108  if (in_array(strtolower($Word), self::$UncountableWords))
109  {
110  return $Word;
111  }
112 
113  # check for irregular singular forms
114  foreach (self::$IrregularWords as $Pattern => $Result)
115  {
116  $Pattern = '/'.$Pattern.'$/i';
117  if (preg_match($Pattern, $Word))
118  {
119  return preg_replace($Pattern, $Result, $Word);
120  }
121  }
122 
123  # check for matches using regular expressions
124  foreach (self::$PluralizePatterns as $Pattern => $Result)
125  {
126  if (preg_match($Pattern, $Word))
127  {
128  return preg_replace($Pattern, $Result, $Word);
129  }
130  }
131 
132  # return word unchanged if we could not process it
133  return $Word;
134  }
135 
141  public static function Singularize($Word)
142  {
143  # return word unchanged if singular and plural are the same
144  if (in_array(strtolower($Word), self::$UncountableWords))
145  {
146  return $Word;
147  }
148 
149  # check for irregular plural forms
150  foreach (self::$IrregularWords as $Result => $Pattern)
151  {
152  $Pattern = '/'.$Pattern.'$/i';
153  if (preg_match($Pattern, $Word))
154  {
155  return preg_replace($Pattern, $Result, $Word);
156  }
157  }
158 
159  # check for matches using regular expressions
160  foreach (self::$SingularizePatterns as $Pattern => $Result)
161  {
162  if (preg_match($Pattern, $Word))
163  {
164  return preg_replace($Pattern, $Result, $Word);
165  }
166  }
167 
168  # return word unchanged if we could not process it
169  return $Word;
170  }
171 
179  public static function SortCompare($A, $B)
180  {
181  if ($A == $B)
182  {
183  return 0;
184  }
185  else
186  {
187  return ($A < $B) ? -1 : 1;
188  }
189  }
190 
191 
204  public static function GetLatLngForZipCode($Zip)
205  {
206  static $ZipCache = array();
207 
208  # if we don't have a cached value for this zip, look one up
209  if (!isset($ZipCache[$Zip]))
210  {
211  # try to open our zip code database
212  $FHandle = fopen(dirname(__FILE__)."/StdLib--ZipCodeCoords.txt", "r");
213 
214  # if we couldn't open the file, we can't look up a value
215  if ($FHandle === FALSE)
216  {
217  throw new Exception("Unable to open zip code coordinates file");
218  }
219 
220  # iterate over our database until we find the desired zip
221  # or run out of database
222  while (($Line = fgetcsv($FHandle, 0, "\t")) !== FALSE)
223  {
224  if ($Line[0] == $Zip)
225  {
226  $ZipCache[$Zip] = array(
227  "Lat" => $Line[1], "Lng" => $Line[2]);
228  break;
229  }
230  }
231 
232  # if we've scanned the entire file and have no coords for
233  # this zip, cache a failure
234  if (!isset($ZipCache[$Zip]))
235  {
236  $ZipCache[$Zip] = FALSE;
237  }
238  }
239 
240  # hand back cached value
241  return $ZipCache[$Zip];
242  }
243 
251  public static function ZipCodeDistance($ZipA, $ZipB)
252  {
253 
254  $FirstPoint = self::GetLatLngForZipCode($ZipA);
255  $SecondPoint = self::GetLatLngForZipCode($ZipB);
256 
257  # if we scanned the whole file and lack data for either of our
258  # points, return NULL
259  if ($FirstPoint === FALSE || $SecondPoint === FALSE)
260  {
261  return FALSE;
262  }
263 
264  return self::ComputeGreatCircleDistance(
265  $FirstPoint["Lat"], $FirstPoint["Lng"],
266  $SecondPoint["Lat"], $SecondPoint["Lng"]);
267  }
268 
278  public static function ComputeGreatCircleDistance($LatSrc, $LonSrc,
279  $LatDst, $LonDst)
280  {
281  # See http://en.wikipedia.org/wiki/Great-circle_distance
282 
283  # Convert it all to Radians
284  $Ps = deg2rad($LatSrc);
285  $Ls = deg2rad($LonSrc);
286  $Pf = deg2rad($LatDst);
287  $Lf = deg2rad($LonDst);
288 
289  # Compute the central angle
290  return 3958.756 * atan2(
291  sqrt( pow(cos($Pf)*sin($Lf-$Ls), 2) +
292  pow(cos($Ps)*sin($Pf) -
293  sin($Ps)*cos($Pf)*cos($Lf-$Ls), 2)),
294  sin($Ps)*sin($Pf)+cos($Ps)*cos($Pf)*cos($Lf-$Ls));
295 
296  }
297 
307  public static function ComputeBearing($LatSrc, $LonSrc,
308  $LatDst, $LonDst)
309  {
310  # See http://mathforum.org/library/drmath/view/55417.html
311 
312  # Convert angles to radians
313  $Ps = deg2rad($LatSrc);
314  $Ls = deg2rad($LonSrc);
315  $Pf = deg2rad($LatDst);
316  $Lf = deg2rad($LonDst);
317 
318  return rad2deg(atan2(sin($Lf-$Ls)*cos($Pf),
319  cos($Ps)*sin($Pf)-sin($Ps)*cos($Pf)*cos($Lf-$Ls)));
320  }
321 
323  const SQL_DATE_FORMAT = "Y-m-d H:i:s";
324 
325 
326  # ---- PRIVATE INTERFACE -------------------------------------------------
327 
328  private static $PluralizePatterns = array(
329  '/(quiz)$/i' => "$1zes",
330  '/^(ox)$/i' => "$1en",
331  '/([m|l])ouse$/i' => "$1ice",
332  '/(matr|vert|ind)ix|ex$/i' => "$1ices",
333  '/(x|ch|ss|sh)$/i' => "$1es",
334  '/([^aeiouy]|qu)y$/i' => "$1ies",
335  '/(hive)$/i' => "$1s",
336  '/(?:([^f])fe|([lr])f)$/i' => "$1$2ves",
337  '/(shea|lea|loa|thie)f$/i' => "$1ves",
338  '/sis$/i' => "ses",
339  '/([ti])um$/i' => "$1a",
340  '/(tomat|potat|ech|her|vet)o$/i'=> "$1oes",
341  '/(bu)s$/i' => "$1ses",
342  '/(alias)$/i' => "$1es",
343  '/(octop)us$/i' => "$1i",
344  '/(ax|test)is$/i' => "$1es",
345  '/(us)$/i' => "$1es",
346  '/s$/i' => "s",
347  '/$/' => "s"
348  );
349  private static $SingularizePatterns = array(
350  '/(quiz)zes$/i' => "$1",
351  '/(matr)ices$/i' => "$1ix",
352  '/(vert|ind)ices$/i' => "$1ex",
353  '/^(ox)en$/i' => "$1",
354  '/(alias)es$/i' => "$1",
355  '/(octop|vir)i$/i' => "$1us",
356  '/(cris|ax|test)es$/i' => "$1is",
357  '/(shoe)s$/i' => "$1",
358  '/(o)es$/i' => "$1",
359  '/(bus)es$/i' => "$1",
360  '/([m|l])ice$/i' => "$1ouse",
361  '/(x|ch|ss|sh)es$/i' => "$1",
362  '/(m)ovies$/i' => "$1ovie",
363  '/(s)eries$/i' => "$1eries",
364  '/([^aeiouy]|qu)ies$/i' => "$1y",
365  '/([lr])ves$/i' => "$1f",
366  '/(tive)s$/i' => "$1",
367  '/(hive)s$/i' => "$1",
368  '/(li|wi|kni)ves$/i' => "$1fe",
369  '/(shea|loa|lea|thie)ves$/i'=> "$1f",
370  '/(^analy)ses$/i' => "$1sis",
371  '/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' => "$1$2sis",
372  '/([ti])a$/i' => "$1um",
373  '/(n)ews$/i' => "$1ews",
374  '/(h|bl)ouses$/i' => "$1ouse",
375  '/(corpse)s$/i' => "$1",
376  '/(us)es$/i' => "$1",
377  '/s$/i' => ""
378  );
379  private static $IrregularWords = array(
380  'move' => 'moves',
381  'foot' => 'feet',
382  'goose' => 'geese',
383  'sex' => 'sexes',
384  'child' => 'children',
385  'man' => 'men',
386  'tooth' => 'teeth',
387  'person' => 'people'
388  );
389  private static $UncountableWords = array(
390  'sheep',
391  'fish',
392  'deer',
393  'series',
394  'species',
395  'money',
396  'rice',
397  'information',
398  'equipment'
399  );
400 }
static CheckMyCaller($DesiredCaller, $ExceptionMsg=NULL)
Check the caller of the current function.
Definition: StdLib.php:47
static SortCompare($A, $B)
Perform compare and return value appropriate for sort function callbacks.
Definition: StdLib.php:179
static ZipCodeDistance($ZipA, $ZipB)
Compute the distance between two US ZIP codes.
Definition: StdLib.php:251
static GetMyCaller()
Get string with file and line number for call to current function.
Definition: StdLib.php:23
static Pluralize($Word)
Pluralize an English word.
Definition: StdLib.php:105
static GetLatLngForZipCode($Zip)
Look up the GPS coordinates for a US ZIP code.
Definition: StdLib.php:204
Standard utility library.
Definition: StdLib.php:14
static ComputeGreatCircleDistance($LatSrc, $LonSrc, $LatDst, $LonDst)
Computes the distance in kilometers between two points, assuming a spherical earth.
Definition: StdLib.php:278
static ComputeBearing($LatSrc, $LonSrc, $LatDst, $LonDst)
Computes the initial angle on a course connecting two points, assuming a spherical earth...
Definition: StdLib.php:307
const SQL_DATE_FORMAT
Format to feed to date() to get SQL-compatible date/time string.
Definition: StdLib.php:323
static Singularize($Word)
Singularize an English word.
Definition: StdLib.php:141