Tag Archives: 新特性

用 PHP 8 里的枚举特性来优化 API 错误码管理

​在使用 PHP 开发 API 时,经常会遇到需要返回错误信息的场景。前端根据返回的错误码,再进行相应的操作。

发现问题

这里用 ThinkPHP8 举个例子,这是一个用户登录接口的例子:

public function login()
{
    $username = $this->request->param('username');
    $password = $this->request->param('password');

    if (!$username || !$password) {
        $result = ['code' => 1001, 'msg' => '参数不完整'];
    } else if ($username !== 'admin') {
        $result = ['code' => 1002, 'msg' => '用户不存在'];
    } else if ($password !== '123456') {
        $result = ['code' => 1003, 'msg' => '密码错误'];
    } else {
        $result = ['code' => 1, 'msg' => '登录成功'];
    }

    return json($result);
}

上面的代码里,对提交的参数进行一些简单的判断,定义了 $result 数组,并为每个不同的错误都提供了一个错误码和提示信息。最后返回给前端。为了让前端能理解错误码,后端开发人员需要把上面的 code 和 msg 都写在 api 文档的错误码里,以供前端参考。

不过大家是否觉得这种直接在代码里零散地写 code 和 msg 不够直观和优雅(在实际开发过程中,不大可能会如此集中地显示错误信息),而且容易不小心把 code 写错或者重复?或许我们可以换一种方式来表达错误码。

解决方案

我先把调整后的代码贴出:

public function login()
{
    $username = $this->request->param('username');
    $password = $this->request->param('password');

    if (!$username || !$password) {
        $status = LoginStatus::PARAM_INCOMPLETE;
    } else if ($username !== 'admin') {
        $status = LoginStatus::USER_NOT_FOUND;
    } else if ($password !== '123456') {
        $status = LoginStatus::PASSWORD_WRONG;
    } else {
        $status = LoginStatus::SUCCESS;
    }

    return json($status->apiResult());
}

上面代码没有了 code 和 msg 信息,但是却不难理解 $status 所表达的含义,让代码看上去清爽、直观很多。

Continue reading →

PHP8有什么新特征

联合类型(Union Types)

可以声明变量可能的类型。

class Number {
    private int|float $number;
 
    public function setNumber(int|float $number): void {
        $this->number = $number;
    }
 
    public function getNumber(): int|float {
        return $this->number;
    }
}

添加了 WeakMap

允许数组中的 key 放入对象,如:$map[$obj] = 42;

添加了 ValueError 类。

当函数或方法接收到具有正确类型的参数(错误类型应引发 TypeError 但值不合适时,将引发 ValueError 。

类的变更、使用

1、可变参数继承,允许

class A {
    public function method(int $many, string $parameters, $here) {}
}
class B extends A {
    public function method(...$everything) {}
}

2、后期静态绑定(LSB)(有用),对框架级别的封装、一些工厂设计模式有用。

class Test {
    public function create(): static {
          return new static();
     }
}

3、现在可以使用以下方法获取对象的类名称。

$object::class 等价 get_class($object)

4、现在,new 和 instanceof 可以与任意表达式一起使用,使用

new(expression)(... $args)$obj instanceof(expression)

5、现在允许写。

Foo::BAR::$baz

6、添加 Stringable 接口(作用一般,用在视图模板封装)。

只要类实现了__toString,那么这类自动实现了 Stringable 接口。

class Foo{
    public function __toString(): string
    {
        return 'foo';
    }
}
function bar(Stringable $stringable) { 
/* 虽然Foo没有实现Stringable,但是这里正常的。 */
 }
bar(new Foo());
bar('abc');

7、trait 现在可以定义抽象的私有方法。

Continue reading →