1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* @author Todd Burry <[email protected]> |
4
|
|
|
* @copyright 2009-2014 Vanilla Forums Inc. |
5
|
|
|
* @license MIT |
6
|
|
|
*/ |
7
|
|
|
|
8
|
|
|
/** |
9
|
|
|
* This file is part of the array_column library |
10
|
|
|
* |
11
|
|
|
* For the full copyright and license information, please view the LICENSE |
12
|
|
|
* file that was distributed with this source code. |
13
|
|
|
* |
14
|
|
|
* @copyright Copyright (c) 2013 Ben Ramsey <http://benramsey.com> |
15
|
|
|
* @license http://opensource.org/licenses/MIT MIT |
16
|
|
|
*/ |
17
|
|
|
|
18
|
|
|
/** |
19
|
|
|
* Returns the values from a single column of the input array, identified by the $columnKey. |
20
|
|
|
* |
21
|
|
|
* Optionally, you may provide an $indexKey to index the values in the returned |
22
|
|
|
* array by the values from the $indexKey column in the input array. |
23
|
|
|
* |
24
|
|
|
* @param array $array A multi-dimensional array (record set) from which to pull a column of values. |
25
|
|
|
* @param int|string|null $columnKey The column of values to return. |
26
|
|
|
* This value may be the integer key of the column you wish to retrieve, or it |
27
|
|
|
* may be the string key name for an associative array. |
28
|
|
|
* @param mixed $indexKey The column to use as the index/keys for the returned array. |
29
|
|
|
* This value may be the integer key of the column, or it may be the string key name. |
30
|
|
|
* @return array Returns an array of values representing a single column from the input array. |
31
|
|
|
* @category Array Functions |
32
|
|
|
*/ |
33
|
|
|
function array_column_php(array $array, $columnKey = null, $indexKey = null) { |
34
|
46 |
View Code Duplication |
if (!is_int($columnKey) |
35
|
46 |
|
&& !is_float($columnKey) |
36
|
46 |
|
&& !is_string($columnKey) |
37
|
46 |
|
&& $columnKey !== null |
38
|
46 |
|
&& !(is_object($columnKey) && method_exists($columnKey, '__toString')) |
39
|
46 |
|
) { |
40
|
2 |
|
trigger_error('array_column(): The column key should be either a string or an integer', E_USER_WARNING); |
41
|
|
|
} |
42
|
|
|
|
43
|
44 |
View Code Duplication |
if (isset($indexKey) |
44
|
44 |
|
&& !is_int($indexKey) |
45
|
44 |
|
&& !is_float($indexKey) |
46
|
44 |
|
&& !is_string($indexKey) |
47
|
44 |
|
&& !(is_object($indexKey) && method_exists($indexKey, '__toString')) |
48
|
44 |
|
) { |
49
|
1 |
|
trigger_error('array_column(): The index key should be either a string or an integer', E_USER_WARNING); |
50
|
|
|
} |
51
|
|
|
|
52
|
43 |
|
$paramsColumnKey = ($columnKey !== null) ? (string)$columnKey : null; |
53
|
|
|
|
54
|
43 |
|
$paramsIndexKey = null; |
55
|
43 |
|
if (isset($indexKey)) { |
56
|
2 |
|
if (is_float($indexKey) || is_int($indexKey)) { |
57
|
1 |
|
$paramsIndexKey = (int)$indexKey; |
58
|
1 |
|
} else { |
59
|
1 |
|
$paramsIndexKey = (string)$indexKey; |
60
|
|
|
} |
61
|
2 |
|
} |
62
|
|
|
|
63
|
43 |
|
$resultArray = array(); |
64
|
|
|
|
65
|
43 |
|
foreach ($array as $row) { |
66
|
|
|
|
67
|
43 |
|
$key = $value = null; |
68
|
43 |
|
$keySet = $valueSet = false; |
69
|
|
|
|
70
|
43 |
|
if ($paramsIndexKey !== null && array_key_exists($paramsIndexKey, $row)) { |
71
|
2 |
|
$keySet = true; |
72
|
2 |
|
$key = (string)$row[$paramsIndexKey]; |
73
|
2 |
|
} |
74
|
|
|
|
75
|
43 |
|
if ($paramsColumnKey === null) { |
76
|
1 |
|
$valueSet = true; |
77
|
1 |
|
$value = $row; |
78
|
43 |
|
} elseif (is_array($row) && array_key_exists($paramsColumnKey, $row)) { |
79
|
43 |
|
$valueSet = true; |
80
|
43 |
|
$value = $row[$paramsColumnKey]; |
81
|
43 |
|
} |
82
|
|
|
|
83
|
43 |
|
if ($valueSet) { |
84
|
43 |
|
if ($keySet) { |
85
|
2 |
|
$resultArray[$key] = $value; |
86
|
2 |
|
} else { |
87
|
42 |
|
$resultArray[] = $value; |
88
|
|
|
} |
89
|
43 |
|
} |
90
|
|
|
|
91
|
43 |
|
} |
92
|
|
|
|
93
|
43 |
|
return $resultArray; |
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
if (!function_exists('array_column')) { |
97
|
|
|
/** |
98
|
|
|
* A custom implementation of array_column for older versions of php. |
99
|
|
|
* |
100
|
|
|
* @param array $array The dataset to test. |
101
|
|
|
* @param int|string $columnKey The column of values to return. |
102
|
|
|
* @param int|string|null $indexKey The column to use as the index/keys for the returned array. |
103
|
|
|
* @return array Returns the columns from the {@link $input} array. |
104
|
|
|
*/ |
105
|
|
|
function array_column($array, $columnKey, $indexKey = null) { |
106
|
40 |
|
return array_column_php($array, $columnKey, $indexKey); |
107
|
|
|
} |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
/** |
111
|
|
|
* Converts a quick array into a key/value form. |
112
|
|
|
* |
113
|
|
|
* @param array $array The array to work on. |
114
|
|
|
* @param mixed $default The default value for unspecified keys. |
115
|
|
|
* @return array Returns the array converted to long syntax. |
116
|
|
|
*/ |
117
|
|
View Code Duplication |
function array_quick(array $array, $default) { |
|
|
|
|
118
|
35 |
|
$result = []; |
119
|
35 |
|
foreach ($array as $key => $value) { |
120
|
35 |
|
if (is_int($key)) { |
121
|
35 |
|
$result[$value] = $default; |
122
|
35 |
|
} else { |
123
|
|
|
$result[$key] = $value; |
124
|
|
|
} |
125
|
35 |
|
} |
126
|
35 |
|
return $result; |
127
|
|
|
} |
128
|
|
|
|
129
|
|
|
/** |
130
|
|
|
* Converts a quick array into a key/value form using a callback to convert the short items. |
131
|
|
|
* |
132
|
|
|
* @param array $array The array to work on. |
133
|
|
|
* @param callable $callback The callback used to generate the default values. |
134
|
|
|
* @return array Returns the array converted to long syntax. |
135
|
|
|
*/ |
136
|
|
View Code Duplication |
function array_uquick(array $array, callable $callback) { |
|
|
|
|
137
|
|
|
$result = []; |
138
|
|
|
foreach ($array as $key => $value) { |
139
|
|
|
if (is_int($key)) { |
140
|
|
|
$result[$value] = $callback($value); |
141
|
|
|
} else { |
142
|
|
|
$result[$key] = $value; |
143
|
|
|
} |
144
|
|
|
} |
145
|
|
|
return $result; |
146
|
|
|
} |
147
|
|
|
|
148
|
|
|
/** |
149
|
|
|
* Load configuration data from a file into an array. |
150
|
|
|
* |
151
|
|
|
* @param string $path The path to load the file from. |
152
|
|
|
* @param string $php_var The name of the php variable to load from if using the php file type. |
153
|
|
|
* @return array The configuration data. |
154
|
|
|
* @throws InvalidArgumentException Throws an exception when the file type isn't supported. |
155
|
|
|
* |
156
|
|
|
* @category Array Functions |
157
|
|
|
*/ |
158
|
|
|
function array_load($path, $php_var = 'config') { |
159
|
11 |
|
if (!file_exists($path)) { |
160
|
5 |
|
return false; |
161
|
|
|
} |
162
|
|
|
|
163
|
|
|
// Get the extension of the file, but allow for .ini.php, .json.php etc. |
164
|
6 |
|
$ext = strstr(basename($path), '.'); |
165
|
|
|
|
166
|
|
|
switch ($ext) { |
167
|
|
|
// case '.ini': |
168
|
|
|
// case '.ini.php': |
169
|
|
|
// $loaded = parse_ini_file($path, false, INI_SCANNER_RAW); |
170
|
|
|
// break; |
171
|
6 |
|
case '.json': |
172
|
6 |
|
case '.json.php': |
173
|
2 |
|
$loaded = json_decode(file_get_contents($path), true); |
174
|
2 |
|
break; |
175
|
4 |
|
case '.php': |
176
|
1 |
|
include $path; |
177
|
1 |
|
$loaded = $$php_var; |
178
|
1 |
|
break; |
179
|
3 |
|
case '.ser': |
180
|
3 |
|
case '.ser.php': |
181
|
2 |
|
$loaded = unserialize(file_get_contents($path)); |
182
|
2 |
|
break; |
183
|
1 |
|
case '.yml': |
184
|
1 |
|
case '.yml.php': |
185
|
|
|
$loaded = yaml_parse_file($path); |
186
|
|
|
break; |
187
|
1 |
|
default: |
188
|
1 |
|
throw new InvalidArgumentException("Invalid config extension $ext on $path.", 500); |
189
|
1 |
|
} |
190
|
5 |
|
return $loaded; |
191
|
|
|
} |
192
|
|
|
|
193
|
|
|
/** |
194
|
|
|
* Save an array of data to a specified path. |
195
|
|
|
* |
196
|
|
|
* @param array $data The data to save. |
197
|
|
|
* @param string $path The path to save to. |
198
|
|
|
* @param string $php_var The name of the php variable to load from if using the php file type. |
199
|
|
|
* @return bool Returns true if the save was successful or false otherwise. |
200
|
|
|
* @throws InvalidArgumentException Throws an exception when the file type isn't supported. |
201
|
|
|
* |
202
|
|
|
* @category Array Functions |
203
|
|
|
*/ |
204
|
|
|
function array_save($data, $path, $php_var = 'config') { |
205
|
7 |
|
if (!is_array($data)) { |
206
|
|
|
throw new \InvalidArgumentException('Config::saveArray(): Argument #1 is not an array.', 500); |
207
|
|
|
} |
208
|
|
|
|
209
|
|
|
// Get the extension of the file, but allow for .ini.php, .json.php etc. |
210
|
7 |
|
$ext = strstr(basename($path), '.'); |
211
|
|
|
|
212
|
|
|
switch ($ext) { |
213
|
|
|
// case '.ini': |
214
|
|
|
// case '.ini.php': |
215
|
|
|
// $ini = static::iniEncode($config); |
216
|
|
|
// $result = file_put_contents_safe($path, $ini); |
217
|
|
|
// break; |
218
|
7 |
|
case '.json': |
219
|
7 |
|
case '.json.php': |
220
|
3 |
|
if (defined('JSON_PRETTY_PRINT')) { |
221
|
3 |
|
$json = json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); |
222
|
3 |
|
} else { |
223
|
|
|
$json = json_encode($data); |
224
|
|
|
} |
225
|
3 |
|
$result = file_put_contents_safe($path, $json); |
226
|
3 |
|
break; |
227
|
4 |
|
case '.php': |
228
|
1 |
|
$php = "<?php\n".php_encode($data, $php_var)."\n"; |
229
|
1 |
|
$result = file_put_contents_safe($path, $php); |
230
|
1 |
|
break; |
231
|
3 |
|
case '.ser': |
232
|
3 |
|
case '.ser.php': |
233
|
2 |
|
$ser = serialize($data); |
234
|
2 |
|
$result = file_put_contents_safe($path, $ser); |
235
|
2 |
|
break; |
236
|
1 |
|
case '.yml': |
237
|
1 |
|
case '.yml.php': |
238
|
|
|
$yml = yaml_emit($data, YAML_UTF8_ENCODING, YAML_LN_BREAK); |
239
|
|
|
$result = file_put_contents_safe($path, $yml); |
240
|
|
|
break; |
241
|
1 |
|
default: |
242
|
1 |
|
throw new \InvalidArgumentException("Invalid config extension $ext on $path.", 500); |
243
|
1 |
|
} |
244
|
6 |
|
return $result; |
245
|
|
|
} |
246
|
|
|
|
247
|
|
|
/** |
248
|
|
|
* Search an array for a value with a user-defined comparison function. |
249
|
|
|
* |
250
|
|
|
* @param mixed $needle The value to search for. |
251
|
|
|
* @param array $haystack The array to search. |
252
|
|
|
* @param callable $cmp The comparison function to use in the search. |
253
|
|
|
* @return mixed|false Returns the found value or false if the value is not found. |
254
|
|
|
*/ |
255
|
|
|
function array_usearch($needle, array $haystack, callable $cmp) { |
256
|
32 |
|
$found = array_uintersect($haystack, [$needle], $cmp); |
257
|
|
|
|
258
|
32 |
|
if (empty($found)) { |
259
|
2 |
|
return false; |
260
|
|
|
} else { |
261
|
30 |
|
return array_pop($found); |
262
|
|
|
} |
263
|
|
|
} |
264
|
|
|
|
265
|
|
|
/** |
266
|
|
|
* Select the first non-empty value from an array. |
267
|
|
|
* |
268
|
|
|
* @param array $keys An array of keys to try. |
269
|
|
|
* @param array $array The array to select from. |
270
|
|
|
* @param mixed $default The default value if non of the keys exist. |
271
|
|
|
* @return mixed Returns the first non-empty value of {@link $default} if none are found. |
272
|
|
|
* @category Array Functions |
273
|
|
|
*/ |
274
|
|
|
function array_select(array $keys, array $array, $default = null) { |
275
|
70 |
|
foreach ($keys as $key) { |
276
|
70 |
|
if (isset($array[$key]) && $array[$key]) { |
277
|
69 |
|
return $array[$key]; |
278
|
|
|
} |
279
|
70 |
|
} |
280
|
3 |
|
return $default; |
281
|
|
|
} |
282
|
|
|
|
283
|
|
|
/** |
284
|
|
|
* Make sure that a key exists in an array. |
285
|
|
|
* |
286
|
|
|
* @param string|int $key The array key to ensure. |
287
|
|
|
* @param array &$array The array to modify. |
288
|
|
|
* @param mixed $default The default value to set if key does not exist. |
289
|
|
|
* @category Array Functions |
290
|
|
|
*/ |
291
|
|
|
function array_touch($key, &$array, $default) { |
292
|
46 |
|
if (!array_key_exists($key, $array)) { |
293
|
45 |
|
$array[$key] = $default; |
294
|
45 |
|
} |
295
|
46 |
|
} |
296
|
|
|
|
297
|
|
|
/** |
298
|
|
|
* Take all of the items in an array and make a new array with them specified by mappings. |
299
|
|
|
* |
300
|
|
|
* @param array $array The input array to translate. |
301
|
|
|
* @param array $mappings The mappings to translate the array. |
302
|
|
|
* @return array |
303
|
|
|
* |
304
|
|
|
* @category Array Functions |
305
|
|
|
*/ |
306
|
|
|
function array_translate($array, $mappings) { |
307
|
49 |
|
$array = (array)$array; |
308
|
49 |
|
$result = array(); |
309
|
49 |
|
foreach ($mappings as $index => $value) { |
310
|
49 |
|
if (is_numeric($index)) { |
311
|
32 |
|
$key = $value; |
312
|
32 |
|
$newKey = $value; |
313
|
32 |
|
} else { |
314
|
35 |
|
$key = $index; |
315
|
35 |
|
$newKey = $value; |
316
|
|
|
} |
317
|
49 |
|
if (isset($array[$key])) { |
318
|
49 |
|
$result[$newKey] = $array[$key]; |
319
|
49 |
|
} else { |
320
|
47 |
|
$result[$newKey] = null; |
321
|
|
|
} |
322
|
49 |
|
} |
323
|
49 |
|
return $result; |
324
|
|
|
} |
325
|
|
|
|
326
|
|
|
/** |
327
|
|
|
* Returns the average rating based in the Wilson score interval. |
328
|
|
|
* |
329
|
|
|
* |
330
|
|
|
* @param int $positive The number of positive ratings. |
331
|
|
|
* @param int $total The total number of ratings. |
332
|
|
|
* @param float $confidence Your confidence level. |
333
|
|
|
* @return int |
334
|
|
|
* |
335
|
|
|
* @see http://stackoverflow.com/questions/9478741/mysql-php-for-wilson-score-interval-with-time-gravity |
336
|
|
|
* @see http://evanmiller.org/how-not-to-sort-by-average-rating.html |
337
|
|
|
*/ |
338
|
|
|
//function averageRating($positive, $total, $confidence = 0.95) { |
339
|
|
|
// if ($total == 0) |
340
|
|
|
// return 0; |
341
|
|
|
// |
342
|
|
|
// if ($confidence == 0.95) |
343
|
|
|
// $z = 1.96; |
344
|
|
|
// else |
345
|
|
|
// $z = pnormaldist(1 - (1 - $confidence) / 2, 0, 1); |
346
|
|
|
// $p = 1.0 * $positive / $total; |
347
|
|
|
// $s = ($p + $z * $z / (2 * $total) - $z * sqrt(($p * (1 - $p) + $z * $z / (4 * $total)) / $total)) / (1 + $z * $z / $total); |
348
|
|
|
// return $s; |
349
|
|
|
//} |
350
|
|
|
|
351
|
|
|
/** |
352
|
|
|
* Base64 Encode a string, but make it suitable to be passed in a url. |
353
|
|
|
* |
354
|
|
|
* @param string $str The string to encode. |
355
|
|
|
* @return string Returns the encoded string. |
356
|
|
|
* @category String Functions |
357
|
|
|
* @see base64_urldecode() |
358
|
|
|
* @see base64_encode() |
359
|
|
|
*/ |
360
|
|
|
function base64url_encode($str) { |
361
|
5 |
|
return trim(strtr(base64_encode($str), '+/', '-_'), '='); |
362
|
|
|
} |
363
|
|
|
|
364
|
|
|
/** |
365
|
|
|
* Decode a string that was encoded using {@link base64_urlencode()}. |
366
|
|
|
* |
367
|
|
|
* @param string $str The encoded string. |
368
|
|
|
* @return string The decoded string. |
369
|
|
|
* @category String Functions |
370
|
|
|
* @see base64_urldecode() |
371
|
|
|
* @see base64_decode() |
372
|
|
|
*/ |
373
|
|
|
function base64url_decode($str) { |
374
|
5 |
|
return base64_decode(strtr($str, '-_', '+/')); |
375
|
|
|
} |
376
|
|
|
|
377
|
|
|
/** |
378
|
|
|
* An alias of {@link config()}. |
379
|
|
|
* |
380
|
|
|
* @param string $key The config key. |
381
|
|
|
* @param string $default The default value if the config setting isn't available. |
382
|
|
|
* @return string The config value. |
383
|
|
|
* @see config() |
384
|
|
|
*/ |
385
|
|
|
function c($key, $default) { |
386
|
|
|
return config($key, $default); |
387
|
|
|
} |
388
|
|
|
|
389
|
|
|
//function checkRoute($className, $methodName, &$routed) { |
390
|
|
|
// if ($routed) |
391
|
|
|
// return false; |
392
|
|
|
// if (class_exists($className) && method_exists($className, $methodName)) |
393
|
|
|
// return $routed = true; |
394
|
|
|
// return $routed = false; |
395
|
|
|
//} |
396
|
|
|
|
397
|
|
|
/** |
398
|
|
|
* Get a value from the config. |
399
|
|
|
* |
400
|
|
|
* @param string $key The config key. |
401
|
|
|
* @param mixed $default The default value if the config setting isn't available. |
402
|
|
|
* @return mixed The config value. |
403
|
|
|
*/ |
404
|
|
|
function config($key, $default = null) { |
405
|
|
|
return Garden\Config::get($key, $default); |
406
|
|
|
} |
407
|
|
|
|
408
|
|
|
/** |
409
|
|
|
* Compare two dates formatted as either timestamps or strings. |
410
|
|
|
* |
411
|
|
|
* @param mixed $date1 The first date to compare expressed as an integer timestamp or a string date. |
412
|
|
|
* @param mixed $date2 The second date to compare expressed as an integer timestamp or a string date. |
413
|
|
|
* @return int Returns `1` if {@link $date1} > {@link $date2}, `-1` if {@link $date1} > {@link $date2}, |
414
|
|
|
* or `0` if the two dates are equal. |
415
|
|
|
* @category Date/Time Functions |
416
|
|
|
*/ |
417
|
|
|
function datecmp($date1, $date2) { |
418
|
1 |
|
if (is_numeric($date1)) { |
419
|
1 |
|
$timestamp1 = $date1; |
420
|
1 |
|
} else { |
421
|
1 |
|
$timestamp1 = strtotime($date1); |
422
|
|
|
} |
423
|
|
|
|
424
|
1 |
|
if (is_numeric($date2)) { |
425
|
1 |
|
$timestamp2 = $date2; |
426
|
1 |
|
} else { |
427
|
1 |
|
$timestamp2 = strtotime($date2); |
428
|
|
|
} |
429
|
|
|
|
430
|
1 |
|
if ($timestamp1 == $timestamp2) { |
431
|
1 |
|
return 0; |
432
|
1 |
|
} elseif ($timestamp1 > $timestamp2) { |
433
|
1 |
|
return 1; |
434
|
|
|
} else { |
435
|
1 |
|
return -1; |
436
|
|
|
} |
437
|
|
|
} |
438
|
|
|
|
439
|
|
|
/** |
440
|
|
|
* Mark something as deprecated. |
441
|
|
|
* |
442
|
|
|
* When passing the {@link $name} argument, try using the following naming convention for names. |
443
|
|
|
* |
444
|
|
|
* - Functions: function_name() |
445
|
|
|
* - Classes: ClassName |
446
|
|
|
* - Static methods: ClassName::methodName() |
447
|
|
|
* - Instance methods: ClassName->methodName() |
448
|
|
|
* |
449
|
|
|
* @param string $name The name of the deprecated function. |
450
|
|
|
* @param string $newname The name of the new function that should be used instead. |
451
|
|
|
*/ |
452
|
|
|
function deprecated($name, $newname = '') { |
453
|
|
|
$msg = $name.' is deprecated.'; |
454
|
|
|
if ($newname) { |
455
|
|
|
$msg .= " Use $newname instead."; |
456
|
|
|
} |
457
|
|
|
|
458
|
|
|
trigger_error($msg, E_USER_DEPRECATED); |
459
|
|
|
} |
460
|
|
|
|
461
|
|
|
/** |
462
|
|
|
* A version of file_put_contents() that is multi-thread safe. |
463
|
|
|
* |
464
|
|
|
* @param string $filename Path to the file where to write the data. |
465
|
|
|
* @param mixed $data The data to write. Can be either a string, an array or a stream resource. |
466
|
|
|
* @param int $mode The permissions to set on a new file. |
467
|
|
|
* @return boolean |
468
|
|
|
* @category Filesystem Functions |
469
|
|
|
* @see http://php.net/file_put_contents |
470
|
|
|
*/ |
471
|
|
|
function file_put_contents_safe($filename, $data, $mode = 0644) { |
472
|
7 |
|
$temp = tempnam(dirname($filename), 'atomic'); |
473
|
|
|
|
474
|
7 |
|
if (!($fp = @fopen($temp, 'wb'))) { |
475
|
|
|
$temp = dirname($filename).DIRECTORY_SEPARATOR.uniqid('atomic'); |
476
|
|
|
if (!($fp = @fopen($temp, 'wb'))) { |
477
|
|
|
trigger_error("file_put_contents_safe() : error writing temporary file '$temp'", E_USER_WARNING); |
478
|
|
|
return false; |
479
|
|
|
} |
480
|
|
|
} |
481
|
|
|
|
482
|
7 |
|
fwrite($fp, $data); |
483
|
7 |
|
fclose($fp); |
484
|
|
|
|
485
|
7 |
|
if (!@rename($temp, $filename)) { |
486
|
1 |
|
@unlink($filename); |
|
|
|
|
487
|
1 |
|
@rename($temp, $filename); |
|
|
|
|
488
|
1 |
|
} |
489
|
|
|
|
490
|
7 |
|
@chmod($filename, $mode); |
|
|
|
|
491
|
7 |
|
return true; |
492
|
|
|
} |
493
|
|
|
|
494
|
|
|
/** |
495
|
|
|
* Force a value into a boolean. |
496
|
|
|
* |
497
|
|
|
* @param mixed $value The value to force. |
498
|
|
|
* @return boolean Returns the boolean value of {@link $value}. |
499
|
|
|
* @category Type Functions |
500
|
|
|
*/ |
501
|
|
|
function force_bool($value) { |
502
|
12 |
|
if (is_string($value)) { |
503
|
12 |
|
switch (strtolower($value)) { |
504
|
12 |
|
case 'disabled': |
505
|
12 |
|
case 'false': |
506
|
12 |
|
case 'no': |
507
|
12 |
|
case 'off': |
508
|
12 |
|
case '': |
509
|
6 |
|
return false; |
510
|
7 |
|
} |
511
|
7 |
|
return true; |
512
|
|
|
} |
513
|
2 |
|
return (bool)$value; |
514
|
|
|
} |
515
|
|
|
|
516
|
|
|
/** |
517
|
|
|
* Force a string to look like an ip address (v4). |
518
|
|
|
* |
519
|
|
|
* @param string $ip The ip string to look at. |
520
|
|
|
* @return string|null The ipv4 address or null if {@link $ip} is empty. |
521
|
|
|
*/ |
522
|
|
|
function force_ipv4($ip) { |
523
|
1 |
|
if (!$ip) { |
524
|
|
|
return null; |
525
|
|
|
} |
526
|
|
|
|
527
|
1 |
|
if (strpos($ip, ',') !== false) { |
528
|
|
|
$ip = substr($ip, 0, strpos($ip, ',')); |
529
|
|
|
} |
530
|
|
|
|
531
|
|
|
// Make sure we have a valid ip. |
532
|
1 |
|
if (preg_match('`(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})`', $ip, $m)) { |
533
|
1 |
|
$ip = $m[1]; |
534
|
1 |
|
} elseif ($ip === '::1') { |
535
|
|
|
$ip = '127.0.0.1'; |
536
|
|
|
} else { |
537
|
|
|
$ip = '0.0.0.0'; // unknown ip |
538
|
|
|
} |
539
|
1 |
|
return $ip; |
540
|
|
|
} |
541
|
|
|
|
542
|
|
|
/** |
543
|
|
|
* Force a value to be an integer. |
544
|
|
|
* |
545
|
|
|
* @param mixed $value The value to force. |
546
|
|
|
* @return int Returns the integer value of {@link $value}. |
547
|
|
|
* @category Type Functions |
548
|
|
|
*/ |
549
|
|
|
function force_int($value) { |
550
|
4 |
|
if (is_string($value)) { |
551
|
4 |
|
switch (strtolower($value)) { |
552
|
4 |
|
case 'disabled': |
553
|
4 |
|
case 'false': |
554
|
4 |
|
case 'no': |
555
|
4 |
|
case 'off': |
556
|
4 |
|
case '': |
557
|
1 |
|
return 0; |
558
|
4 |
|
case 'enabled': |
559
|
4 |
|
case 'true': |
560
|
4 |
|
case 'yes': |
561
|
4 |
|
case 'on': |
562
|
1 |
|
return 1; |
563
|
4 |
|
} |
564
|
4 |
|
} |
565
|
4 |
|
return intval($value); |
566
|
|
|
} |
567
|
|
|
|
568
|
|
|
function garden_error_handler($number, $message, $file, $line, $args) { |
569
|
6 |
|
$error_reporting = error_reporting(); |
570
|
|
|
// Ignore errors that are below the current error reporting level. |
571
|
6 |
|
if (($error_reporting & $number) != $number) { |
572
|
1 |
|
return false; |
573
|
|
|
} |
574
|
|
|
|
575
|
5 |
|
$backtrace = debug_backtrace(); |
576
|
|
|
|
577
|
5 |
|
throw new Garden\Exception\ErrorException($message, $number, $file, $line, $args, $backtrace); |
578
|
|
|
} |
579
|
|
|
|
580
|
|
|
/** |
581
|
|
|
* Like {@link implode()}, but joins array keys and values. |
582
|
|
|
* |
583
|
|
|
* @param string $elemglue The string that separates each element of the array. |
584
|
|
|
* @param string $keyglue The string that separates keys and values. |
585
|
|
|
* @param array $pieces The array of strings to implode. |
586
|
|
|
* @return string Returns the imploded array as a string. |
587
|
|
|
* |
588
|
|
|
* @category Array Functions |
589
|
|
|
* @category String Functions |
590
|
|
|
*/ |
591
|
|
|
function implode_assoc($elemglue, $keyglue, $pieces) { |
592
|
32 |
|
$result = ''; |
593
|
|
|
|
594
|
32 |
|
foreach ($pieces as $key => $value) { |
595
|
32 |
|
if ($result) { |
596
|
32 |
|
$result .= $elemglue; |
597
|
32 |
|
} |
598
|
|
|
|
599
|
32 |
|
$result .= $key.$keyglue.$value; |
600
|
32 |
|
} |
601
|
32 |
|
return $result; |
602
|
|
|
} |
603
|
|
|
|
604
|
|
|
/** |
605
|
|
|
* Whether or not a string is a url in the form http://, https://, or //. |
606
|
|
|
* |
607
|
|
|
* @param string $str The string to check. |
608
|
|
|
* @return bool |
609
|
|
|
* |
610
|
|
|
* @category String Functions |
611
|
|
|
* @category Internet Functions |
612
|
|
|
*/ |
613
|
|
|
function is_url($str) { |
614
|
7 |
|
if (!$str) { |
615
|
1 |
|
return false; |
616
|
|
|
} |
617
|
6 |
|
if (substr($str, 0, 2) == '//') { |
618
|
1 |
|
return true; |
619
|
|
|
} |
620
|
5 |
|
if (strpos($str, '://', 1) !== false) { |
621
|
2 |
|
return true; |
622
|
|
|
} |
623
|
3 |
|
return false; |
624
|
|
|
} |
625
|
|
|
|
626
|
|
|
/** |
627
|
|
|
* Strip a substring from the beginning of a string. |
628
|
|
|
* |
629
|
|
|
* @param string $mainstr The main string to look at (the haystack). |
630
|
|
|
* @param string $substr The substring to search trim (the needle). |
631
|
|
|
* @return string |
632
|
|
|
* |
633
|
|
|
* @category String Functions |
634
|
|
|
*/ |
635
|
|
|
function ltrim_substr($mainstr, $substr) { |
636
|
14 |
|
if (strncasecmp($mainstr, $substr, strlen($substr)) === 0) { |
637
|
14 |
|
return substr($mainstr, strlen($substr)); |
638
|
|
|
} |
639
|
1 |
|
return $mainstr; |
640
|
|
|
} |
641
|
|
|
|
642
|
|
|
/** |
643
|
|
|
* Get the file extension from a mime-type. |
644
|
|
|
* |
645
|
|
|
* @param string $mime The mime type. |
646
|
|
|
* @param string $ext If this argument is specified then this extension will be added to the list of known types. |
647
|
|
|
* @return string The file extension without the dot. |
648
|
|
|
* @category Internet Functions |
649
|
|
|
* @category String Functions |
650
|
|
|
*/ |
651
|
|
|
function mime2ext($mime, $ext = null) { |
652
|
2 |
|
static $known = array('text/plain' => '.txt', 'image/jpeg' => '.jpg', 'application/rss+xml' => '.rss'); |
653
|
2 |
|
$mime = strtolower($mime); |
654
|
|
|
|
655
|
2 |
|
if ($ext !== null) { |
656
|
1 |
|
$known[$mime] = '.'.ltrim($ext, '.'); |
657
|
1 |
|
} |
658
|
|
|
|
659
|
2 |
|
if (array_key_exists($mime, $known)) { |
660
|
2 |
|
return $known[$mime]; |
661
|
|
|
} |
662
|
|
|
|
663
|
|
|
// We don't know the mime type so we need to just return the second part as the extension. |
664
|
1 |
|
$result = trim(strrchr($mime, '/'), '/'); |
665
|
|
|
|
666
|
1 |
|
if (substr($result, 0, 2) === 'x-') { |
667
|
1 |
|
$result = substr($result, 2); |
668
|
1 |
|
} |
669
|
|
|
|
670
|
1 |
|
return '.'.$result; |
671
|
|
|
} |
672
|
|
|
|
673
|
|
|
/** |
674
|
|
|
* Encode a php array nicely. |
675
|
|
|
* |
676
|
|
|
* @param array $data The data to encode. |
677
|
|
|
* @param string $php_var The name of the php variable. |
678
|
|
|
* @return string Returns a string of the encoded data. |
679
|
|
|
* |
680
|
|
|
* @category Array Functions |
681
|
|
|
*/ |
682
|
|
|
function php_encode($data, $php_var = 'config') { |
683
|
1 |
|
if (is_array($data)) { |
684
|
1 |
|
$result = ''; |
685
|
1 |
|
$lastHeading = ''; |
686
|
1 |
|
foreach ($data as $key => $value) { |
687
|
|
|
// Figure out the heading. |
688
|
1 |
|
if (($pos = strpos($key, '.')) !== false) { |
689
|
|
|
$heading = str_replace(array("\n", "\r"), ' ', substr($key, 0, $pos)); |
690
|
|
|
} else { |
691
|
1 |
|
$heading = substr($key, 0, 1); |
692
|
|
|
} |
693
|
|
|
|
694
|
1 |
|
if ($heading !== $lastHeading) { |
695
|
1 |
|
if (strlen($heading) === 1) { |
696
|
|
|
// Don't emit single letter headings, but space them out. |
697
|
1 |
|
$result .= "\n"; |
698
|
1 |
|
} else { |
699
|
|
|
$result .= "\n// ".$heading."\n"; |
700
|
|
|
} |
701
|
1 |
|
$lastHeading = $heading; |
702
|
1 |
|
} |
703
|
|
|
|
704
|
1 |
|
$result .= '$'.$php_var.'['.var_export($key, true).'] = '.var_export($value, true).";\n"; |
705
|
1 |
|
} |
706
|
1 |
|
} else { |
707
|
|
|
$result = "\$$php_var = ".var_export($data, true).";\n"; |
708
|
|
|
} |
709
|
1 |
|
return $result; |
710
|
|
|
} |
711
|
|
|
|
712
|
|
|
/** |
713
|
|
|
* Reflect the arguments on a callback and returns them as an associative array. |
714
|
|
|
* |
715
|
|
|
* @param callable $callback A callback to the function. |
716
|
|
|
* @param array $args An array of arguments. |
717
|
|
|
* @param array $get An optional other array of arguments. |
718
|
|
|
* @return array The arguments in an associative array, in order ready to be passed to call_user_func_array(). |
719
|
|
|
* @throws Exception Throws an exception when {@link callback} isn't a valid callback. |
720
|
|
|
* @category Type Functions |
721
|
|
|
*/ |
722
|
|
|
function reflect_args(callable $callback, $args, $get = null) { |
723
|
12 |
|
if (is_array($get)) { |
724
|
1 |
|
$args = array_merge($get, $args); |
725
|
1 |
|
} |
726
|
12 |
|
$args = array_change_key_case($args); |
727
|
|
|
|
728
|
12 |
|
if (is_string($callback) || (is_object($callback) && $callback instanceof Closure)) { |
729
|
11 |
|
$meth = new ReflectionFunction($callback); |
730
|
11 |
|
$meth_name = $meth; |
731
|
11 |
|
} else { |
732
|
1 |
|
$meth = new ReflectionMethod($callback[0], $callback[1]); |
733
|
1 |
|
if (is_string($callback[0])) { |
734
|
1 |
|
$meth_name = $callback[0].'::'.$meth->getName(); |
735
|
1 |
|
} else { |
736
|
1 |
|
$meth_name = get_class($callback[0]).'->'.$meth->getName(); |
737
|
|
|
} |
738
|
|
|
} |
739
|
|
|
|
740
|
12 |
|
$meth_params = $meth->getParameters(); |
741
|
|
|
|
742
|
12 |
|
$call_args = array(); |
743
|
12 |
|
$missing_args = array(); |
744
|
|
|
|
745
|
|
|
// Set all of the parameters. |
746
|
12 |
|
foreach ($meth_params as $index => $meth_param) { |
747
|
8 |
|
$param_name = $meth_param->getName(); |
748
|
8 |
|
$param_namel = strtolower($param_name); |
749
|
|
|
|
750
|
8 |
|
if (isset($args[$param_namel])) { |
751
|
8 |
|
$param_value = $args[$param_namel]; |
752
|
8 |
|
} elseif (isset($args[$index])) { |
753
|
1 |
|
$param_value = $args[$index]; |
754
|
3 |
|
} elseif ($meth_param->isDefaultValueAvailable()) { |
755
|
2 |
|
$param_value = $meth_param->getDefaultValue(); |
756
|
2 |
|
} else { |
757
|
|
|
$param_value = null; |
758
|
|
|
$missing_args[] = '$'.$param_name; |
759
|
|
|
} |
760
|
|
|
|
761
|
8 |
|
$call_args[$param_name] = $param_value; |
762
|
12 |
|
} |
763
|
|
|
|
764
|
|
|
// Add optional parameters so that methods that use get_func_args() will still work. |
765
|
12 |
|
for ($index = count($call_args); array_key_exists($index, $args); $index++) { |
766
|
|
|
$call_args[$index] = $args[$index]; |
767
|
|
|
} |
768
|
|
|
|
769
|
12 |
|
if (count($missing_args) > 0) { |
770
|
|
|
trigger_error("$meth_name() expects the following parameters: ".implode(', ', $missing_args).'.', E_USER_NOTICE); |
771
|
|
|
} |
772
|
|
|
|
773
|
12 |
|
return $call_args; |
774
|
|
|
} |
775
|
|
|
|
776
|
|
|
/** |
777
|
|
|
* Strip a substring rom the end of a string. |
778
|
|
|
* |
779
|
|
|
* @param string $mainstr The main string to search (the haystack). |
780
|
|
|
* @param string $substr The substring to trim (the needle). |
781
|
|
|
* @return string Returns the trimmed string or {@link $mainstr} if {@link $substr} was not found. |
782
|
|
|
* @category String Functions |
783
|
|
|
*/ |
784
|
|
|
function rtrim_substr($mainstr, $substr) { |
785
|
2 |
|
if (strcasecmp(substr($mainstr, -strlen($substr)), $substr) === 0) { |
786
|
1 |
|
return substr($mainstr, 0, -strlen($substr)); |
787
|
|
|
} |
788
|
2 |
|
return $mainstr; |
789
|
|
|
} |
790
|
|
|
|
791
|
|
|
/** |
792
|
|
|
* Returns whether or not a string begins with another string. |
793
|
|
|
* |
794
|
|
|
* This function is not case-sensitive. |
795
|
|
|
* |
796
|
|
|
* @param string $haystack The string to test. |
797
|
|
|
* @param string $needle The substring to test against. |
798
|
|
|
* @return bool Whether or not `$string` begins with `$with`. |
799
|
|
|
* @category String Functions |
800
|
|
|
*/ |
801
|
|
|
function str_begins($haystack, $needle) { |
802
|
1 |
|
return strncasecmp($haystack, $needle, strlen($needle)) === 0; |
803
|
|
|
} |
804
|
|
|
|
805
|
|
|
/** |
806
|
|
|
* Returns whether or not a string ends with another string. |
807
|
|
|
* |
808
|
|
|
* This function is not case-sensitive. |
809
|
|
|
* |
810
|
|
|
* @param string $haystack The string to test. |
811
|
|
|
* @param string $needle The substring to test against. |
812
|
|
|
* @return bool Whether or not `$string` ends with `$with`. |
813
|
|
|
* @category String Functions |
814
|
|
|
*/ |
815
|
|
|
function str_ends($haystack, $needle) { |
816
|
109 |
|
return strcasecmp(substr($haystack, -strlen($needle)), $needle) === 0; |
817
|
|
|
} |
818
|
|
|
|
819
|
|
|
$translations = []; |
820
|
|
|
|
821
|
|
|
/** |
822
|
|
|
* Translate a string. |
823
|
|
|
* |
824
|
|
|
* @param string $code The translation code. |
825
|
|
|
* @param string $default The default if the translation is not found. |
826
|
|
|
* @return string The translated string. |
827
|
|
|
* |
828
|
|
|
* @category String Functions |
829
|
|
|
* @category Localization Functions |
830
|
|
|
*/ |
831
|
|
|
function t($code, $default = null) { |
832
|
34 |
|
global $translations; |
833
|
|
|
|
834
|
34 |
|
if (substr($code, 0, 1) === '@') { |
835
|
2 |
|
return substr($code, 1); |
836
|
34 |
|
} elseif (isset($translations[$code])) { |
837
|
2 |
|
return $translations[$code]; |
838
|
33 |
|
} elseif ($default !== null) { |
839
|
1 |
|
return $default; |
840
|
|
|
} else { |
841
|
33 |
|
return $code; |
842
|
|
|
} |
843
|
|
|
} |
844
|
|
|
|
845
|
|
|
/** |
846
|
|
|
* A version of {@link sprintf()} That translates the string format. |
847
|
|
|
* |
848
|
|
|
* @param string $formatCode The format translation code. |
849
|
|
|
* @param mixed $arg1 The arguments to pass to {@link sprintf()}. |
850
|
|
|
* @return string The translated string. |
851
|
|
|
*/ |
852
|
|
|
function sprintft($formatCode, $arg1 = null) { |
853
|
33 |
|
$args = func_get_args(); |
854
|
33 |
|
$args[0] = t($formatCode); |
855
|
33 |
|
return call_user_func_array('sprintf', $args); |
856
|
|
|
} |
857
|
|
|
|
858
|
|
|
/** |
859
|
|
|
* Make sure that a directory exists. |
860
|
|
|
* |
861
|
|
|
* @param string $dir The name of the directory. |
862
|
|
|
* @param int $mode The file permissions on the folder if it's created. |
863
|
|
|
* @throws Exception Throws an exception when {@link $dir} is a file. |
864
|
|
|
* @category Filesystem Functions |
865
|
|
|
*/ |
866
|
|
|
function touchdir($dir, $mode = 0777) { |
867
|
1 |
|
if (!file_exists($dir)) { |
868
|
1 |
|
mkdir($dir, $mode, true); |
869
|
1 |
|
} elseif (!is_dir($dir)) { |
870
|
|
|
throw new Exception("The specified directory already exists as a file. ($dir)", 400); |
871
|
|
|
} |
872
|
1 |
|
} |
873
|
|
|
|
874
|
|
|
/** |
875
|
|
|
* Safely get a value out of an array. |
876
|
|
|
* |
877
|
|
|
* This function will always return a value even if the array key doesn't exist. |
878
|
|
|
* The val() function is one of the biggest workhorses of Vanilla and shows up a lot throughout other code. |
879
|
|
|
* It's much preferable to use this function if your not sure whether or not an array key exists rather than |
880
|
|
|
* using @ error suppression. |
881
|
|
|
* |
882
|
|
|
* This function uses optimizations found in the [facebook libphputil library](https://github.com/facebook/libphutil). |
883
|
|
|
* |
884
|
|
|
* @param string|int $key The array key. |
885
|
|
|
* @param array|object $array The array to get the value from. |
886
|
|
|
* @param mixed $default The default value to return if the key doesn't exist. |
887
|
|
|
* @return mixed The item from the array or `$default` if the array key doesn't exist. |
888
|
|
|
* @category Array Functions |
889
|
|
|
*/ |
890
|
|
|
function val($key, $array, $default = null) { |
891
|
189 |
|
if (is_array($array)) { |
892
|
|
|
// isset() is a micro-optimization - it is fast but fails for null values. |
893
|
187 |
|
if (isset($array[$key])) { |
894
|
180 |
|
return $array[$key]; |
895
|
|
|
} |
896
|
|
|
|
897
|
|
|
// Comparing $default is also a micro-optimization. |
898
|
154 |
|
if ($default === null || array_key_exists($key, $array)) { |
899
|
104 |
|
return null; |
900
|
|
|
} |
901
|
124 |
|
} elseif (is_object($array)) { |
902
|
1 |
|
if (isset($array->$key)) { |
903
|
1 |
|
return $array->$key; |
904
|
|
|
} |
905
|
|
|
|
906
|
1 |
|
if ($default === null || property_exists($array, $key)) { |
907
|
1 |
|
return null; |
908
|
|
|
} |
909
|
1 |
|
} |
910
|
|
|
|
911
|
124 |
|
return $default; |
912
|
|
|
} |
913
|
|
|
|
914
|
|
|
/** |
915
|
|
|
* Return the value from an associative array. |
916
|
|
|
* |
917
|
|
|
* This function differs from val() in that $key can be an array that will be used to walk a nested array. |
918
|
|
|
* |
919
|
|
|
* @param array|string $keys The keys or property names of the value. This can be an array or dot-seperated string. |
920
|
|
|
* @param array|object $array The array or object to search. |
921
|
|
|
* @param mixed $default The value to return if the key does not exist. |
922
|
|
|
* @return mixed The value from the array or object. |
923
|
|
|
* @category Array Functions |
924
|
|
|
*/ |
925
|
|
|
function valr($keys, $array, $default = null) { |
926
|
12 |
|
if (is_string($keys)) { |
927
|
3 |
|
$keys = explode('.', $keys); |
928
|
3 |
|
} |
929
|
|
|
|
930
|
12 |
|
$value = $array; |
931
|
12 |
|
for ($i = 0; $i < count($keys); ++$i) { |
|
|
|
|
932
|
12 |
|
$SubKey = $keys[$i]; |
933
|
|
|
|
934
|
12 |
|
if (is_array($value) && isset($value[$SubKey])) { |
935
|
9 |
|
$value = $value[$SubKey]; |
936
|
12 |
|
} elseif (is_object($value) && isset($value->$SubKey)) { |
937
|
2 |
|
$value = $value->$SubKey; |
938
|
2 |
|
} else { |
939
|
6 |
|
return $default; |
940
|
|
|
} |
941
|
10 |
|
} |
942
|
10 |
|
return $value; |
943
|
|
|
} |
944
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.