Tag Archives: php

用 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 →

基于ThinkPHP里模型搜索器的高效数据查询解决方案

在项目开发时,特别是后台管理功能里,数据搜索几乎是无处不在的。

发现问题

​例如我们在开发一个项目时,需要在后台增加一个用户搜索的功能。以ThinkPHP(下面简称tp)为例,如果在没有使用开源的后台开发框架或者追求快速开发时,我们可能会采用下面这段代码:

public function index()
    {
        //姓名关键字搜索
        $name = $this->request->get('name');
        //手机号搜索
        $mobile = $this->request->get('mobile');
        $paginate = User::where('mobile',$mobile)
            ->where('name', 'like', "%{$name}%")
            ->paginate();
        return json($paginate->toArray());
    }

这种解决方法的弊端就是一个查询页面就要写一段代码。写的多了,你就会发现,你的编码时间被这些不断重复的代码所占据,枯燥且乏味。于是你肯定会想办法把这个查询功能提取出来,统一写成一个方法。

于是乎,我们就想到了再新建一个控制器父类,写一个统一的index方法,让控制器子类继承:

class MyController extends BaseController
{
    // 模型名称
    protected Model $model;

    public function index()
    {
        //搜索参数
        $params = $this->request->get('params/a', []);
        //搜索操作符
        $operators = $this->request->get('operators/a', []);
        //排序
        $order = $this->request->get('order/a', []);

        //构建查询
        $query = $this->model->where(function ($query) use ($params, $operators) {
            //遍历搜索参数
            foreach ($params as $field => $value) {
                $op = $operators[$field] ?? '=';
                if ('like' === $op) {
                    $value = "%{$value}%";
                }
                //搜索条件
                $query->where($field, $op, $value);
            }
        });

        //排序
        if ($order) {
            $query->order($order['field'], $order['sort'] ?? 'asc');
        }

        //搜索数据并分页
        $paginate = $query->paginate();
        return json($paginate->toArray());
    }
}

在这个index方法里,我们约定了查询相关参数,params数组表示准备查询的字段名和查询值,operators数组表示字段查询方式(例如:like,in等),order数组表示排序。

Continue reading →

利用 PHP 8 的注解特性来实现依赖注入

在使用 ThinkPHP(以下简称 tp)开发时,我们经常会遇到需要依赖注入的场景。比如在控制器类方法里注入 Request 类:

public function index(Request $request)
{
    // todo
}

但是,当我们需要一个类为整个控制器服务时,上述方法就不适用了。这种方式只适合某一个方法内使用。如果多个方法都要使用这个类,那么就需要将该类作为控制器的成员变量来使用。

class Index {
    private Request $request;
}

既然作为成员变量使用,那么我们需要在系统调用控制器方法前实例化 Request,比如放在构造函数里实例化:

public function __construct()
{
    // tp6/8 里 app 函数可以实例化 Request 等类
    $this->request = app('request');
}

当然,我们也可以利用 tp 的自动注入机制来实例化:

public function __construct(Request $request)
{
    $this->request = $request;
}

这种方式在一个或两个控制器里使用时还是方便的。但是,当项目里有较多控制器时,这种方法显得繁琐。

Continue reading →

微信小程序——磨锋短视频

磨锋图文是基于微擎开发的微信短视频小程序。

随着5G越来越普及,短视频也呈现爆发式发展,小程序+短视频模式必然是引流蕞佳组合,所以磨锋短视频小程序出现了,通过微信内分享视频小程序,从而获得大量的访客流量,利用这些流量即可实现变现。磨锋短视频提供了多种类型的小程序流量主广告变现以及拼多多商品佣金变现。

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 →