PHP 8.3 的 json_validate 和类型化类常量,帮我删了一堆代码

2026-08-22 0 824

最近把一个老项目从 PHP 7.4 迁到 PHP 8.3,发现很多以前要写好几行判断的地方,现在直接一个函数就够了。今天不聊那些概念性的东西,就说说让我印象最深的三个特性:json_validate()类型化类常量,以及 #[Override] 属性。都是实战里能用上的,顺带省了时间。

一、json_validate:只验证不解析,省心多了

以前我要判断一个字符串是不是合法的 JSON,总是习惯像下面这样写:

function isJson($string) {
    $data = json_decode($string, true);
    return (json_last_error() === JSON_ERROR_NONE);
}

明明只是想判断“是不是JSON”,结果把整个数据都解析了一遍,浪费内存。而且如果字符串特别大,还会慢悠悠地构建数组,甚至可能因为深度太深报错。

PHP 8.3 新增了 json_validate(),专门干这个事:它只校验语法,不生成数组对象。用法很简单:

if (json_validate($jsonString)) {
    // 是有效 JSON
} else {
    // 不是 JSON,或格式错误
}

有人可能会问:那我要解析 JSON 怎么办?还是用 json_decode。只是在你只需要验证数据的场景下,json_validate 更适合。比如接收异步传送的数据时,我先用 json_validate 快速检查一下,发现不对就直接打回,根本不会执行后面的 json_decode,这样接口的安全性更高。

一个真实例子:读取上传的JSON配置

之前写了一个导入插件配置的功能,用户上传 .json 文件,我需要检查文件内容是否合法。老代码像这样:

$data = json_decode(file_get_contents($_FILES['file']['tmp_name']), true);
if (json_last_error() !== JSON_ERROR_NONE) {
    echo "配置文件格式不对!";
}

换成新方法之后,逻辑就清楚了:

$content = file_get_contents($_FILES['file']['tmp_name']);
if (!json_validate($content)) {
    echo "配置文件格式不对!";
} else {
    $data = json_decode($content, true);
    // 正常处理
}

通过 json_validate 先拦一道,避免了无意义的解码。虽然性能差异在几百KB的文件上几乎感觉不出来,但至少代码表达的意思更准确了。

二、类型化类常量:常量也讲究数据类型

PHP 8.3 还支持给类常量添加类型声明。以前常量就只是一个“简单的值”,你没法规定它是 int 还是 string。现在可以了。比如我们写一个支付回调的状态类,以前是这么写的:

class PaymentStatus {
    const SUCCESS = 0;
    const FAILED = 1;
    const PENDING = 2;
}

如果不小心写成 const SUCCESS = '0'; 也能过,但比较起来就容易踩坑。用类型化常量,就能锁定类型:

class PaymentStatus {
    public const int SUCCESS = 0;
    public const int FAILED = 1;
    public const int PENDING = 2;
}

这样如果哪天代码里试图给 SUCCESS 赋一个字符串值,PHP 引擎直接报错,不需要等你运行到某个诡异的分支才发现问题。

搭配使用:枚举还是常量?

很多人说 PHP 8.1 有枚举,为什么还要用类常量?主要是枚举有时候太“重”,简单的状态标志用类型化常量更轻量。实际项目中,我经常用联合类型和常量结合,比如:

class UserRoles {
    public const string ADMIN = 'admin';
    public const string EDITOR = 'editor';
    public const string VIEWER = 'viewer';
}

然后在方法参数里用:

function checkPermission(string $role): void {
    if ($role === UserRoles::ADMIN) {
        // ...
    }
}

虽然不加类型好像也没啥,但当别人接手代码,一看到 public const string ADMIN 就知道这个常量的值肯定是字符串,不会乱传。

三、#[Override] 属性:重构时不再慌

PHP 8.3 引入了 #[Override] 属性,用来标记一个方法是不是重写了父类或接口的方法。以前我们重写方法全靠自觉,如果哪天父类把方法改名了,子类的“重写”方法就变成了一个普通方法,没人告诉你,直到运行时报错才一头雾水。

用了 #[Override] 后,PHP 会在编译阶段帮你检查:如果父类或接口没有这个名字的方法,就会抛致命错误。相当于给你上了道保险。

拿我最近重构的一段代码来说吧。我有个抽象类 BaseParser

abstract class BaseParser {
    public function parse(string $input) {
        return $this->doParse($input);
    }

    abstract protected function doParse(string $input): array;
}

以前有子类这样写:

class JsonParser extends BaseParser {
    protected function doParse(string $input): array {
        return json_decode($input, true) ?? [];
    }
}

看起来没问题。但如果某天我把抽象方法名改成了 parseData,而 JsonParser 里还是 doParse,那这个方法就不会被调用,而且因为子类里有 doParse,也不会报错。数据就这样悄悄没了。

加了 #[Override] 以后:

class JsonParser extends BaseParser {
    #[Override]
    protected function doParse(string $input): array {
        return json_decode($input, true) ?? [];
    }
}

如果你把父类的方法名改了,PHP 编译器会直接报错“方法 JsonParser::doParse() 不能覆盖任何方法”,你再也不会碰到那种找不到原因的bug了。我现在基本上在重写方法上都加了这一行,成本几乎为零,好处却是实打实的。

四、三个特性结合:一个更干净的API响应处理

光说单个特性没意思,我把它们结合起来,写了一个简单的HTTP响应处理类。这可能是这三样东西最典型的配合场景。

class ApiResponse {
    public const int OK = 200;
    public const int BAD_REQUEST = 400;

    public function __construct(
        private string $rawBody
    ) {}

    #[Override]
    public function __toString(): string {
        return $this->rawBody;
    }

    public function isValidJson(): bool {
        return json_validate($this->rawBody);
    }

    public function toArray(): array {
        if (!$this->isValidJson()) {
            throw new InvalidArgumentException('响应内容不是合法的 JSON');
        }
        return json_decode($this->rawBody, true) ?? [];
    }
}

在控制器里,就这么写:

$response = new ApiResponse($httpResponseBody);
$code = $httpStatusCode;

if ($code === ApiResponse::OK && $response->isValidJson()) {
    $result = $response->toArray();
} else {
    // 记录错误日志
}

代码变得特别直白,而且不依赖额外的判断。类型化常量保证了 OK 一定是 int,不会出现 "200" 这种意外。抽象方法重写检查也让我在改动基类时放心不少。

五、升级要注意的小坑

虽然这些特性很香,但升级到 PHP 8.3 还是有几点要留意:

  • json_validate() 对于 JSON 符号:它严格要求 UTF-8,如果字符串里有非法字符,会返回 false。这点跟 json_decode 的行为也一致,不措手不及。
  • 类型化类常量目前不支持 private?其实支持,但要注意接口常量默认是 public,且在接口中不能用 private。用的时候别搞混。
  • #[Override] 属性只对“重写父类或接口方法”生效。如果你只是在同一个类里定义了一个普通方法,别加这个属性,不然直接报错。

别问我怎么知道的,都是泪。

六、总结

PHP 8.3 没有搞什么惊天动地的大改版,但像 json_validate、类型化常量、#[Override] 这种细节,确实能提高代码质量。尤其是 #[Override],我觉得每个项目都值得加上,毕竟“继承”这种东西,最大的问题就是太隐晦了。

最后还是建议你,如果项目还在旧的 PHP 版本上,趁着不忙,把版本提一提。不光是性能提升,这些语法糖真的能让编码体验好很多。就算暂时不想升,也可以看看新特性的文档,以后写代码多一点点选择。

PHP 8.3 的 json_validate 和类型化类常量,帮我删了一堆代码
收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

版权声明:
本站资源有的来自互联网收集整理,本站纯免费分享提供学习使用,如果侵犯了您的合法权益,请联系本站我们会及时删除。
本站资源仅供研究、学习交流之用,免费开源项目不代表完全可商用,若商业用途请先咨询开发企业能否商用,否则产生的一切后果将由下载用户自行承担。
原创板块未经允许不得转载,否则将追究法律责任。

淘吗网 php PHP 8.3 的 json_validate 和类型化类常量,帮我删了一堆代码 https://www.taomawang.com/server/php/2587.html

下一篇:

已经没有下一篇了!

常见问题

相关文章

猜你喜欢
发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务