PHP 错误分为三种:语法错误、运行时错误、逻辑错误
- 语法错误,有编辑器的提示下,在编写程序过程中基本不会遇到,此文略。
- 逻辑错误就是写代码的人想法不正确,跟程序无关,此文略。
- 运行时错误,简单理解就是,程序本身的语法没错,而是在执行时发生错误,通常一个运行时错误如果出现在线上环境,很可能需要遇到特定情况才能让错误重现,比较难排查,常见的有:分母为 0、数组索引错误等。
调试方法
线上环境一般会关闭错误提示,所以调试代码时,第一步就是要开启错误提示:
error_reporting(E_ALL);
ini_set('display_errors', '1');
接下来就是无脑 var_dump die
或者 file_put_contents
了……当然不是!虽然这两个也是最有效的笨办法
最常用的方法是使用 xdebug 调试,xdebug 的使用具体见:在 VsCode 下使用 Xdebug 调试 PHP
如何避免
- 关闭错误提示,即
ini_set('display_errors', '0');
(不要error_reporting
这一行) - 使用 I::get 方法(终于进入正题了?)
I::get
I::get
最初的本意是为了去掉烦人的“数组索引报错”,毕竟这个报错是 PHP 里最最最最常见的,使用步骤如下:
composer 安装 icy2003/php:composer require icy2003/php ~1.0
use icy2003\php\I;
,使用 I::get
,具体代码如下:
<?php
use icy2003\php\I;
$array = [
'name' => 'tom',
'type' => 'cat',
];
include 'vendor/autoload.php';
echo I::get($array, 'color');
输出结果当然什么也没有,即使是开启了错误提示,也不会有任何输出。
意义
有些线上环境,即使关闭了错误提示,还是会看到报错,因为可能那个服务商并不提供修改这种配置的服务,关闭错误提示等于是无效操作。
这样做的意义就在于,当代码处于线上环境时,不会让用户看到代码报错提示,站在用户的角度上看,代码错误会让用户感到恐慌,而非预期的结果(比如这里什么也不输出),只会让用户觉得“是不是我网卡了?”,从体验上来说,就好了很多。即使真的真的真的被用户意识到“是你网站有问题”,也可以让客服以“请您尝试切换网络再重试”的借口,给开发争取紧急补救的时间,防止丢失用户(虽然听起来很扯,但是确实有效果)!
最重要的一点:我很懒
定义
如果 I::get
的作用如果只是这样,我就不会用前面那么长的篇幅来给它铺垫了,这是一个我测试了很多,虽然一些同事对此有争议,但是却让我很舒服的函数,它适用于 PHP 的八大数据类型!而它的含义就是最朴实无华的“get”,即“获取一个值”。
I::get
的定义:public static function get($mixed, $keyString, $defaultValue = null)
其中,mixed
支持八大类型,I::get
的行为会根据 mixed
的类型相应变化,defaultValue
为取值结果 null
时的默认值,下面按照 mixed
的类型来介绍 I::get
的行为:
以下所有行为可以混搭!
数组
对于数组,I::get
会以英文的句号(.)为分隔符,按照层级获取值,直到取值成功或者找不到(返回默认值)。如:
$array = [
[
'name' => 'tom',
'type' => 'cat',
],
[
'name'=>'jerry',
'type'=>'rat'
]
];
echo I::get($array, '0.name');
keyString
为 0.name
,表示“获取 $array
这个索引数组的第 0 个数组键为 name
的值”,结果就是 “tom”
字符串
函数内部实现为字符串截取,还是以 $array
为例子,I::get($array, '0.name.0')
的值为 “t”,也就是 “tom” 的第 0 位字符,因为在 0.name
之后,结果是字符串类型的 “tom” 了,于是后续的取值就变成了字符串截取
也就是说 I::get
逐层取值时,mixed
的数据类型会逐层变化,当然最简单的例子是 I::get('hello', 0)
,结果是 “h”
对象
对象的取值检测顺序为:get
方法,公有属性,各类 PHP 框架的魔术属性,例如:
class Test{
public function getHeLlo(){
return new Test2();
}
}
class Test2{
public $worl_d = [
'name' => 'icy2003',
];
}
$test = new Test();
$res = I::get($test, 'he_llo.worl_d.name.1'); // $res 的值为 'c'
其中,被看作是方法时,下划线会被去掉,而属性则不会,上面的代码很好地说明了这一点
布尔值
等价于三元操作符,当然没有三元操作符好,毕竟多嵌套了一层函数,但 I::get
的意义就是在于“不报错和万能”
匿名函数
取值就是 mixed
位置匿名函数的值,keyString
和 defaultValue
分别表示,当 mixed
值表示“真”和“假”时执行的操作,例子如下:
$i = 1;
// $res 值为 'a'; $i 值为 2;
$res = I::get(function () {
return 'a';
}, function () use (&$i) {
$i++;
}, function () use (&$i) {
$i--;
});
$i = 1;
// $res 值为 ''; $i 值为 0;
$res = I::get(function () {
return '';
}, function () use (&$i) {
$i++;
}, function () use (&$i) {
$i--;
});
这个行为看似很奇怪,我举个例子就很清楚了:尝试下载一个文件,下载成功以及失败时,后续执行一些操作
资源和 null
mixed
类型为资源和 null
时,只有 defaultValue
有效,目的是为了容错