1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Class I |
4
|
|
|
* |
5
|
|
|
* @link https://www.icy2003.com/ |
6
|
|
|
* @author icy2003 <[email protected]> |
7
|
|
|
* @copyright Copyright (c) 2017, icy2003 |
8
|
|
|
*/ |
9
|
|
|
namespace icy2003\php; |
10
|
|
|
|
11
|
|
|
use Exception; |
12
|
|
|
use icy2003\php\ihelpers\Strings; |
13
|
|
|
use ReflectionClass; |
14
|
|
|
|
15
|
|
|
/** |
16
|
|
|
* I 类 |
17
|
|
|
*/ |
18
|
|
|
class I |
19
|
|
|
{ |
20
|
|
|
|
21
|
|
|
/** |
22
|
|
|
* 获取值 |
23
|
|
|
* |
24
|
|
|
* 支持类型:数组对象和 null、数字和字符串、布尔值、回调函数,依据数据类型有不同的含义(但是都很合理) |
25
|
|
|
* |
26
|
|
|
* @param mixed $mixed 混合类型 |
27
|
|
|
* - 当 $mixed 为**数组或对象**时,此方法用于按照层级获取值,用法如下: |
28
|
|
|
* 1. 对于一个多维数组 $array 来说,a.b.cd_ef 会拿 $array['a']['b']['cd_ef'] 的值 |
29
|
|
|
* 2. 如果 $array['a'] 是对象,则先检查 getB 方法,然后检查 b 属性 |
30
|
|
|
* 3. 如果 $array['a']['b'] 是对象,则检查 getCdEf 方法,然后检查 cd_ef 属性 |
31
|
|
|
* - 当 $mixed 为**布尔值**(即表达式)时,等价于三元操作符,例如 I::get(1 > 2, '真', '假') |
32
|
|
|
* - 当 $mixed 为**字符串**时,等价于 Strings::sub,截取字符串 |
33
|
|
|
* - 当 $mixed 为 **null** 时,含义可被描述为:在使用 I::get($array, 'a.b', 1),$array 意外的是 null,返回 1 是理所当然的 |
34
|
|
|
* - 当 $mixed 为**回调函数**,$mixed 的执行结果将作为 I::get 的返回值 |
35
|
|
|
* @param mixed $keyString 取决于 $mixed 的类型: |
36
|
|
|
* - 当 $mixed 为**数组或对象**时,$keyString 表示:点(.)分割代表层级的字符串,下划线用于对象中转化成驼峰方法,支持数组和对象嵌套 |
37
|
|
|
* - 当 $mixed 为**布尔值**(即表达式)时,$keyString 表示:$mixed 为 true 时返回的值 |
38
|
|
|
* - 当 $mixed 为**字符串**时,$keyString 强制转为整型,表示:截取 $mixed 时,子串的起始位置 |
39
|
|
|
* - 当 $mixed 为 **null** 时,此参数无效 |
40
|
|
|
* - 当 $mixed 为**回调函数**,如果 $mixed 的返回值代表 true(如:1),则执行此回调 |
41
|
|
|
* @param mixed $defaultValue 取决于 $mixed 的类型: |
42
|
|
|
* - 当 $mixed 为**数组或对象**时,$defaultValue 表示:拿不到值时会直接返回该默认值 |
43
|
|
|
* - 当 $mixed 为**布尔值**(即表达式)时,$defaultValue 表示:$mixed 为 false 时返回的值 |
44
|
|
|
* - 当 $mixed 为**字符串**时,$defaultValue 表示:截取 $mixed 时,子串的长度,null 时表示长度为 1 |
45
|
|
|
* - 当 $mixed 为 **null** 时,返回 $defaultValue |
46
|
|
|
* - 当 $mixed 为**回调函数**,如果 $mixed 的返回值代表 false(如:0),则执行此回调 |
47
|
|
|
* |
48
|
|
|
* @return mixed |
49
|
|
|
*/ |
50
|
50 |
|
public static function get($mixed, $keyString, $defaultValue = null) |
51
|
|
|
{ |
52
|
50 |
|
if (is_bool($mixed)) { // 布尔类型 |
53
|
1 |
|
return true === $mixed ? $keyString : $defaultValue; |
54
|
50 |
|
} elseif (is_callable($mixed)) { // 回调 |
55
|
1 |
|
$result = self::call($mixed); |
56
|
1 |
|
if ($result) { |
57
|
1 |
|
self::call($keyString); |
58
|
|
|
} else { |
59
|
1 |
|
self::call($defaultValue); |
60
|
|
|
} |
61
|
1 |
|
return $result; |
62
|
50 |
|
} elseif (is_array($mixed) || is_object($mixed)) { // 数组和对象 |
63
|
50 |
|
if (false === is_string($keyString) && is_callable($keyString)) { |
64
|
|
|
$mixed = self::call($keyString, [$mixed]); |
65
|
|
|
} else { |
66
|
50 |
|
// 如果是个常规数组,直接取值,只支持一维数组 |
67
|
50 |
|
if (is_array($mixed) && array_key_exists($keyString, $mixed)) { |
68
|
50 |
|
return $mixed[$keyString]; |
69
|
50 |
|
} |
70
|
49 |
|
$keyArray = explode('.', $keyString); |
71
|
|
|
foreach ($keyArray as $key) { |
72
|
50 |
|
if (is_array($mixed)) { |
73
|
|
|
if (array_key_exists($key, $mixed) && null !== $mixed[$key]) { |
74
|
2 |
|
$mixed = $mixed[$key]; |
75
|
2 |
|
} else { |
76
|
2 |
|
return $defaultValue; |
77
|
1 |
|
} |
78
|
2 |
|
} elseif (is_object($mixed)) { |
79
|
2 |
|
$method = 'get' . ucfirst(Strings::toCamel($key)); |
80
|
|
|
if (method_exists($mixed, $method)) { |
81
|
|
|
$mixed = $mixed->$method(); |
82
|
1 |
|
} elseif (property_exists($mixed, $key) && null !== $mixed->$key) { |
83
|
|
|
$mixed = $mixed->$key; |
84
|
|
|
} else { |
85
|
|
|
try { |
86
|
1 |
|
$mixed = $mixed->$key; |
87
|
2 |
|
if (null === $mixed) { |
88
|
|
|
return $defaultValue; |
89
|
|
|
} |
90
|
|
|
} catch (Exception $e) { |
|
|
|
|
91
|
|
|
return $defaultValue; |
92
|
1 |
|
} |
93
|
|
|
|
94
|
|
|
} |
95
|
|
|
} else { |
96
|
49 |
|
return self::get($mixed, $key, $defaultValue); |
97
|
1 |
|
} |
98
|
1 |
|
} |
99
|
1 |
|
} |
100
|
1 |
|
return $mixed; |
101
|
1 |
|
} elseif (is_string($mixed) || is_numeric($mixed)) { // 字符串或数字 |
102
|
1 |
|
$pos = (int) $keyString; |
103
|
|
|
$length = null === $defaultValue ? 1 : (int) $defaultValue; |
104
|
1 |
|
return Strings::sub($mixed, $pos, $length); |
105
|
|
|
} elseif (null === $mixed) { // null |
106
|
|
|
return $defaultValue; |
107
|
|
|
} else { // 资源 |
108
|
|
|
return $defaultValue; |
109
|
|
|
} |
110
|
|
|
} |
111
|
|
|
|
112
|
|
|
/** |
113
|
|
|
* 设置值 |
114
|
|
|
* |
115
|
|
|
* @param array|object $mixed 对象或数组 |
116
|
|
|
* @param string $key 键 |
117
|
|
|
* @param mixed $value 值 |
118
|
1 |
|
* @param boolean $overWrite 如果对应的值存在,是否用给定的值覆盖,默认 true,即:是 |
119
|
|
|
* |
120
|
1 |
|
* @return mixed |
121
|
1 |
|
*/ |
122
|
1 |
|
public static function set(&$mixed, $key, $value, $overWrite = true) |
123
|
1 |
|
{ |
124
|
1 |
|
$get = self::get($mixed, $key); |
125
|
1 |
|
if (null === $get || true === $overWrite) { |
126
|
1 |
|
if (is_array($mixed)) { |
127
|
1 |
|
$mixed[$key] = $value; |
128
|
1 |
|
} elseif (is_object($mixed)) { |
129
|
1 |
|
$method = 'set' . ucfirst(Strings::toCamel($key)); |
130
|
|
|
if (method_exists($mixed, $method)) { |
131
|
1 |
|
$mixed->$method($value); |
132
|
|
|
} elseif (property_exists($mixed, $key)) { |
133
|
|
|
$mixed->$key = $value; |
134
|
1 |
|
} else { |
135
|
|
|
throw new Exception('无法设置值'); |
136
|
1 |
|
} |
137
|
|
|
} |
138
|
|
|
return $value; |
139
|
|
|
} |
140
|
|
|
return $get; |
141
|
|
|
} |
142
|
|
|
|
143
|
|
|
/** |
144
|
|
|
* 触发回调 |
145
|
|
|
* |
146
|
10 |
|
* @param callback $callback 回调函数 |
147
|
|
|
* @param array $params 回调参数 |
148
|
10 |
|
* @return mixed |
149
|
10 |
|
*/ |
150
|
10 |
|
public static function call($callback, $params = []) |
151
|
|
|
{ |
152
|
|
|
$result = false; |
153
|
|
|
is_callable($callback) && $result = call_user_func_array($callback, $params); |
154
|
|
|
return $result; |
155
|
|
|
} |
156
|
|
|
|
157
|
|
|
/** |
158
|
|
|
* 定义一个常量 |
159
|
|
|
* |
160
|
|
|
* @param string $constant 常量名 |
161
|
1 |
|
* @param mixed $value 值 |
162
|
|
|
* |
163
|
1 |
|
* @return void |
164
|
1 |
|
*/ |
165
|
|
|
public static function def($constant, $value) |
166
|
|
|
{ |
167
|
|
|
defined($constant) || define($constant, $value); |
168
|
|
|
} |
169
|
|
|
|
170
|
|
|
/** |
171
|
|
|
* 让 empty 支持函数调用 |
172
|
|
|
* |
173
|
|
|
* 注意:此函数并不比 empty 好,只是为了让 empty 支持函数调用 |
174
|
|
|
* |
175
|
|
|
* 例如:empty($array[0]) 就不能用此函数代替,另外,empty 是语法结构,性能明显比函数高 |
176
|
|
|
* |
177
|
|
|
* @see http://php.net/manual/zh/function.empty.php |
178
|
5 |
|
* |
179
|
|
|
* @param mixed $data |
180
|
5 |
|
* @return boolean |
181
|
|
|
*/ |
182
|
|
|
public static function isEmpty($data) |
183
|
|
|
{ |
184
|
|
|
return empty($data); |
185
|
|
|
} |
186
|
|
|
|
187
|
|
|
/** |
188
|
|
|
* 获取 php.ini 配置值 |
189
|
|
|
* |
190
|
|
|
* @param string $key 配置名 |
191
|
2 |
|
* @param mixed $default 默认值 |
192
|
|
|
* |
193
|
2 |
|
* @return mixed |
194
|
|
|
*/ |
195
|
|
|
public static function phpini($key, $default = null) |
196
|
|
|
{ |
197
|
|
|
return false !== ($ini = ini_get($key)) ? $ini : (false !== ($ini = get_cfg_var($key)) ? $ini : $default); |
198
|
|
|
} |
199
|
|
|
|
200
|
|
|
/** |
201
|
|
|
* 显示 PHP 错误 |
202
|
|
|
* |
203
|
1 |
|
* @param boolean $show 是否显示,默认是 |
204
|
|
|
* |
205
|
1 |
|
* @return void |
206
|
1 |
|
*/ |
207
|
1 |
|
public static function displayErrors($show = true) |
208
|
|
|
{ |
209
|
|
|
ini_set("display_errors", true === $show ? 'On' : 'Off'); |
210
|
|
|
true === $show && error_reporting(E_ALL | E_STRICT); |
211
|
|
|
} |
212
|
|
|
|
213
|
|
|
/** |
214
|
|
|
* 别名列表 |
215
|
|
|
* |
216
|
|
|
* @var array |
217
|
|
|
*/ |
218
|
|
|
public static $aliases = [ |
219
|
|
|
'@vendor' => __DIR__ . '/../../../../vendor', |
220
|
|
|
'@icy2003/php_tests' => __DIR__ . '/../tests', |
221
|
|
|
'@icy2003/php_runtime' => __DIR__ . '/../runtime', |
222
|
|
|
'@icy2003/php' => __DIR__, |
223
|
|
|
]; |
224
|
|
|
|
225
|
|
|
/** |
226
|
|
|
* 用别名获取真实路径 |
227
|
|
|
* |
228
|
50 |
|
* @param string $alias 别名 |
229
|
|
|
* |
230
|
50 |
|
* @return string|boolean |
231
|
50 |
|
*/ |
232
|
18 |
|
public static function getAlias($alias) |
233
|
|
|
{ |
234
|
|
|
$alias = Strings::replace($alias, ["\\" => '/']); |
235
|
50 |
|
if (strncmp($alias, '@', 1)) { |
236
|
50 |
|
return $alias; |
237
|
50 |
|
} |
238
|
50 |
|
|
239
|
50 |
|
$pos = 0; |
240
|
50 |
|
while (true) { |
241
|
50 |
|
$pos = strpos($alias, '/', $pos); |
242
|
|
|
$root = $pos === false ? $alias : substr($alias, 0, $pos); |
243
|
|
|
if (isset(static::$aliases[$root])) { |
244
|
|
|
if (is_string(static::$aliases[$root])) { |
245
|
|
|
return $pos === false ? static::$aliases[$root] : static::$aliases[$root] . substr($alias, $pos); |
246
|
|
|
} elseif (is_array(static::$aliases[$root])) { |
247
|
|
|
foreach (static::$aliases[$root] as $name => $path) { |
248
|
|
|
if (strpos($alias . '/', $name . '/') === 0) { |
249
|
|
|
return $path . substr($alias, strlen($name)); |
250
|
|
|
} |
251
|
|
|
} |
252
|
50 |
|
} else { |
253
|
|
|
return false; |
254
|
|
|
} |
255
|
50 |
|
} |
256
|
|
|
if ($root == $alias) { |
257
|
|
|
break; |
258
|
|
|
} |
259
|
|
|
$pos++; |
260
|
|
|
} |
261
|
|
|
// 对 Yii2 的支持 |
262
|
|
|
if ($result = self::call(['\Yii', 'getAlias'], [$alias])) { |
263
|
|
|
self::setAlias($alias, $result); |
264
|
|
|
return $result; |
265
|
|
|
} |
266
|
|
|
|
267
|
|
|
return false; |
268
|
|
|
} |
269
|
|
|
|
270
|
|
|
/** |
271
|
|
|
* 是否是 Yii2 项目 |
272
|
|
|
* |
273
|
|
|
* @return boolean |
274
|
|
|
*/ |
275
|
|
|
public static function isYii2() |
276
|
|
|
{ |
277
|
|
|
return method_exists('\Yii', 'getVersion'); |
278
|
|
|
} |
279
|
|
|
|
280
|
|
|
/** |
281
|
|
|
* 设置别名 |
282
|
|
|
* |
283
|
|
|
* @param string $alias 别名 |
284
|
1 |
|
* @param string|null $path 路径 |
285
|
|
|
* |
286
|
|
|
* @return void |
287
|
|
|
*/ |
288
|
1 |
|
public static function setAlias($alias, $path) |
289
|
|
|
{ |
290
|
|
|
// 对 Yii2 的支持 |
291
|
|
|
try { |
292
|
1 |
|
self::call(['\Yii', 'getAlias'], [$alias]); |
293
|
|
|
} catch (Exception $e) { |
294
|
|
|
self::call(['\Yii', 'setAlias'], [$alias, $path]); |
295
|
1 |
|
} |
296
|
1 |
|
if (strncmp($alias, '@', 1)) { |
297
|
1 |
|
$alias = '@' . $alias; |
298
|
1 |
|
} |
299
|
1 |
|
$pos = strpos($alias, '/'); |
300
|
1 |
|
$root = $pos === false ? $alias : substr($alias, 0, $pos); |
301
|
1 |
|
if ($path !== null) { |
302
|
|
|
$path = strncmp($path, '@', 1) ? rtrim($path, '\\/') : static::getAlias($path); |
303
|
1 |
|
if (!isset(static::$aliases[$root])) { |
304
|
|
|
if ($pos === false) { |
305
|
|
|
static::$aliases[$root] = $path; |
306
|
|
|
} else { |
307
|
|
|
static::$aliases[$root] = [$alias => $path]; |
308
|
|
|
} |
309
|
|
|
} elseif (is_string(static::$aliases[$root])) { |
310
|
|
|
if ($pos === false) { |
311
|
|
|
static::$aliases[$root] = $path; |
312
|
|
|
} else { |
313
|
|
|
static::$aliases[$root] = [ |
314
|
|
|
$alias => $path, |
315
|
|
|
$root => static::$aliases[$root], |
316
|
1 |
|
]; |
317
|
|
|
} |
318
|
|
|
} else { |
319
|
|
|
static::$aliases[$root][$alias] = $path; |
320
|
|
|
krsort(static::$aliases[$root]); |
321
|
|
|
} |
322
|
|
|
} elseif (isset(static::$aliases[$root])) { |
323
|
|
|
if (is_array(static::$aliases[$root])) { |
324
|
|
|
unset(static::$aliases[$root][$alias]); |
325
|
1 |
|
} elseif ($pos === false) { |
326
|
|
|
unset(static::$aliases[$root]); |
327
|
|
|
} |
328
|
|
|
} |
329
|
|
|
} |
330
|
|
|
|
331
|
|
|
/** |
332
|
|
|
* 判断给定选项值里是否设置某选项 |
333
|
|
|
* |
334
|
|
|
* @param integer $flags 选项值 |
335
|
4 |
|
* @param integer $flag 待判断的选项值 |
336
|
|
|
* |
337
|
4 |
|
* @return boolean |
338
|
|
|
*/ |
339
|
|
|
public static function hasFlag($flags, $flag) |
340
|
|
|
{ |
341
|
|
|
return $flags === ($flag | $flags); |
342
|
|
|
} |
343
|
|
|
|
344
|
|
|
/** |
345
|
|
|
* 创建一个对象 |
346
|
|
|
* |
347
|
|
|
* @param array|string $params |
348
|
|
|
* - 字符串:该字符串将被作为类名转成数组处理 |
349
|
|
|
* - 数组: |
350
|
|
|
* 1. class:表示类名 |
351
|
|
|
* 2. 其他:该类的属性,初始化这些属性或者调用相应的 set 方法 |
352
|
|
|
* @param array $config |
353
|
|
|
* - 构造函数传参 |
354
|
|
|
* |
355
|
|
|
* @return object |
356
|
|
|
* @throws Exception |
357
|
|
|
*/ |
358
|
|
|
public static function obj($params, $config = []) |
359
|
|
|
{ |
360
|
|
|
if (is_string($params)) { |
361
|
|
|
$params = ['class' => $params]; |
362
|
|
|
} |
363
|
|
|
if (is_array($params) && isset($params['class'])) { |
364
|
|
|
try { |
365
|
|
|
$class = $params['class']; |
366
|
|
|
unset($params['class']); |
367
|
|
|
$reflection = new ReflectionClass($class); |
368
|
|
|
$object = $reflection->newInstanceArgs($config); |
369
|
|
|
foreach ($params as $name => $value) { |
370
|
|
|
self::set($object, $name, $value); |
371
|
|
|
} |
372
|
|
|
return $object; |
373
|
|
|
} catch (Exception $e) { |
374
|
|
|
throw new Exception('初始化 ' . $class . ' 失败', $e->getCode(), $e); |
375
|
|
|
} |
376
|
|
|
} |
377
|
|
|
throw new Exception('必须带 class 键来指定一个类'); |
378
|
|
|
} |
379
|
|
|
|
380
|
|
|
/** |
381
|
|
|
* 静态配置 |
382
|
|
|
* |
383
|
|
|
* @var array |
384
|
|
|
*/ |
385
|
|
|
public static $ini = [ |
386
|
|
|
'USE_CUSTOM' => false, |
387
|
|
|
'EXT_LOADED' => true, |
388
|
|
|
]; |
389
|
|
|
|
390
|
|
|
/** |
391
|
|
|
* 读取或设置一个全局配置 |
392
|
|
|
* |
393
|
|
|
* - 该配置是利用静态类进行存储的 |
394
|
|
|
* - 如果给定 $value,则为设置,不给则为获取 |
395
|
|
|
* - 可选默认配置有: |
396
|
|
|
* 1. USE_CUSTOM:默认 false,即尝试使用 php 原生函数的实现,如果此参数为 true,则使用 icy2003/php 的实现 |
397
|
|
|
* 2. EXT_LOADED:默认 true,即尝试检测是否有扩展,如果为 false,直接认为没有该扩展 |
398
|
|
|
* |
399
|
|
|
* @param string $key |
400
|
3 |
|
* @param mixed $value |
401
|
|
|
* |
402
|
3 |
|
* @return void|mixed |
403
|
3 |
|
*/ |
404
|
|
|
public static function ini($key, $value = null) |
405
|
2 |
|
{ |
406
|
|
|
if (null !== $value) { |
407
|
3 |
|
self::$ini[$key] = $value; |
408
|
|
|
} else { |
409
|
|
|
return self::get(self::$ini, $key); |
410
|
|
|
} |
411
|
|
|
} |
412
|
|
|
|
413
|
|
|
/** |
414
|
|
|
* 是否有加载 PHP 扩展 |
415
|
|
|
* |
416
|
|
|
* @param string $extName |
417
|
|
|
* |
418
|
|
|
* @return boolean |
419
|
|
|
*/ |
420
|
|
|
public static function isExt($extName) |
421
|
|
|
{ |
422
|
|
|
if (false === extension_loaded($extName) || false === self::ini('EXT_LOADED')) { |
423
|
|
|
return false; |
424
|
|
|
} |
425
|
|
|
return true; |
426
|
|
|
} |
427
|
|
|
|
428
|
|
|
/** |
429
|
|
|
* 判断当前操作系统是不是 windows |
430
|
|
|
* |
431
|
|
|
* @return boolean |
432
|
|
|
*/ |
433
|
|
|
public static function isWin() |
434
|
|
|
{ |
435
|
|
|
return 'WIN' === strtoupper(substr(PHP_OS, 0, 3)); |
436
|
|
|
} |
437
|
|
|
} |
438
|
|
|
|
This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.
Unreachable code is most often the result of
return
,die
orexit
statements that have been added for debug purposes.In the above example, the last
return false
will never be executed, because a return statement has already been met in every possible execution path.