hyperf框架 model casts 自定义类 php

<?php
namespace App\Transformers;
use Hyperf\Contract\CastsAttributes;
class AmountTransformer implements CastsAttributes
{
    public function get($model, $key, $value, $attributes) :float
    {
        // 在这里对amount字段的值进行自定义转换
        // 例如,保留两位小数
        return (float) $value;
    }

    public function set($model, $key, $value, $attributes)
    {
        return $value;
    }
}

实现CastsAttributes接口

调用

<?php

declare(strict_types=1);

namespace App\Model;

use App\Transformers\AmountTransformer;

class CollectionModel extends Model
{
    protected ?string $table = 'collection';

    protected array $casts = ["amount" => AmountTransformer::class];
}

xiang 发布于  2024-4-9 14:10 

hyperf基于jsonrpc协议微服务用apifox调用 php

{
  "jsonrpc": "2.0",
  "method": "calculator/add",
  "params": [1, 2],
  "id": 1
}

对应服务端的代码

#[RpcService(name: "CalculatorService", protocol: "jsonrpc-http", server: "jsonrpc-http",publishTo: "nacos")]
class CalculatorService implements CalculatorServiceInterface
{
    // 实现一个加法方法,这里简单的认为参数都是 int 类型
    public function add(int $a, int $b): int
    {
        $ss="22";
        // 这里是服务方法的具体实现
        return $a + $b;
    }
}

xiang 发布于  2024-4-9 08:43 

面向对象设计原则 Dependency Inversion Principle 依赖倒置原则 简单版

Dependency Inversion Principle 依赖倒置原则 dip

dip有两个原则
1.高层模块不应该依赖于底层模块,二者都应该依赖抽象
2.抽象不应该依赖细节,细节应该依赖于抽象

举一个例子

后端项目,写一个数据列表的接口。

返回数据的格式如下:

{
  "code": 0,
  "message": "成功",
  "data": {
    "conllection": [
      {
        "id": 76,
        "pid": 0,
        "type": "menu",
        "title": "",
        "name": "",
        "path": "",
        "icon": "local-logo",
        "menu_type": null,
        "url": "",
        "component": "",
        "keepalive": 0,
        "extend": "none",
        "remark": "",
        "weigh": 0,
        "status": "1",
        "update_time": null,
        "create_time": null
      }
    ],
    "current_page": 1,
    "per_page": 1,
    "total": 1
  }
}

把返回的数据字段和类型都定义好的话,这个接口就可以跟前端对接。所以先把接口字段定义好。是一个有效率的做法。

现在项目分三层,控制层->服务层(获取数据)->数据仓库层->模型层等等。从高到低。如果遵循依赖倒置原则,我现在只要定义好数据响应类就是以上的json生成的class.

和写好一个接口来定义控制层和服务层使用什么参数和返回值来作数据交换就可以。

定义admin_role类

public class AdminRole {
    private int id;
    private int pid;
    private String type;
    private String title;
    private String name;
    private String path;
    private String icon;
    private String menuType;
    private String url;
    private String component;
    private int keepalive;
    private String extend;
    private String remark;
    private int weigh;
    private String status;
    private String update_time;
    private String create_time;

    // 省略getter和setter方法
}

xiang 发布于  2024-3-26 15:53 

面向对象设计原则 Dependency Inversion Principle 依赖倒置原则 php实现 面向对象

Dependency Inversion Principle 依赖倒置原则 dip

dip有两个原则
1.高层模块不应该依赖于底层模块,二者都应该依赖抽象
2.抽象不应该依赖细节,细节应该依赖于抽象

举一个例子

php后端项目,写一个列表数据的接口,带分页功能

输入的参数 有两个 page和per_page,

输出接口的字段有
total 数据的总数
per_page 每页数
current_page 当前页数
collection 具体的数据集合

以下是mysql的表结构

CREATE TABLE ba_admin_rule (
id int unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
pid int unsigned NOT NULL DEFAULT '0' COMMENT '上级菜单',
type enum('menu_dir','menu','button') COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'menu' COMMENT '类型:menu_dir=菜单目录,menu=菜单项,button=页面按钮',
title varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '标题',
name varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '规则名称',
path varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '路由路径',
icon varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '图标',
menu_type enum('tab','link','iframe') COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '菜单类型:tab=选项卡,link=链接,iframe=Iframe',
url varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT 'Url',
component varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '组件路径',
keepalive tinyint unsigned NOT NULL DEFAULT '0' COMMENT '缓存:0=关闭,1=开启',
extend enum('none','add_rules_only','add_menu_only') COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'none' COMMENT '扩展属性:none=无,add_rules_only=只添加为路由,add_menu_only=只添加为菜单',
remark varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '备注',
weigh int NOT NULL DEFAULT '0' COMMENT '权重',
status enum('0','1') COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '1' COMMENT '状态:0=禁用,1=启用',
update_time bigint unsigned DEFAULT NULL COMMENT '更新时间',
create_time bigint unsigned DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (id),
KEY pid (pid)
) ENGINE=InnoDB AUTO_INCREMENT=91 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='菜单和权限规则表';

生成的pojo类

<?php

namespace App\Http\Pojo;

class AdminRulePojo
{
    private int $id;
    private int $pid = 0;
    private string $type = 'menu';
    private string $title = '';
    private string $name = '';
    private string $path = '';
    private string $icon = '';
    private ?string $menu_type = null;
    private string $url = '';
    private string $component = '';
    private int $keepalive = 0;
    private string $extend = 'none';
    private string $remark = '';

    private int $weigh = 0;
    private string $status = '1';
    private ?int $update_time = null;
    private ?int $create_time = null;

    public function getId(): int
    {
        return $this->id;
    }

    public function setId(int $id): void
    {
        $this->id = $id;
    }

    public function getPid(): int
    {
        return $this->pid;
    }

    public function setPid(int $pid): void
    {
        $this->pid = $pid;
    }

    public function getType(): string
    {
        return $this->type;
    }

    public function setType(string $type): void
    {
        $this->type = $type;
    }

    public function getTitle(): string
    {
        return $this->title;
    }

    public function setTitle(string $title): void
    {
        $this->title = $title;
    }

    public function getName(): string
    {
        return $this->name;
    }

    public function setName(string $name): void
    {
        $this->name = $name;
    }

    public function getPath(): string
    {
        return $this->path;
    }

    public function setPath(string $path): void
    {
        $this->path = $path;
    }

    public function getIcon(): string
    {
        return $this->icon;
    }

    public function setIcon(string $icon): void
    {
        $this->icon = $icon;
    }

    public function getMenuType(): ?string
    {
        return $this->menu_type;
    }

    public function setMenuType(?string $menu_type): void
    {
        $this->menu_type = $menu_type;
    }

    public function getUrl(): string
    {
        return $this->url;
    }

    public function setUrl(string $url): void
    {
        $this->url = $url;
    }

    public function getComponent(): string
    {
        return $this->component;
    }

    public function setComponent(string $component): void
    {
        $this->component = $component;
    }

    public function getKeepalive(): int
    {
        return $this->keepalive;
    }

    public function setKeepalive(int $keepalive): void
    {
        $this->keepalive = $keepalive;
    }

    public function getExtend(): string
    {
        return $this->extend;
    }

    public function setExtend(string $extend): void
    {
        $this->extend = $extend;
    }

    public function getRemark(): string
    {
        return $this->remark;
    }

    public function setRemark(string $remark): void
    {
        $this->remark = $remark;
    }

    public function getWeigh(): int
    {
        return $this->weigh;
    }

    public function setWeigh(int $weigh): void
    {
        $this->weigh = $weigh;
    }

    public function getStatus(): string
    {
        return $this->status;
    }

    public function setStatus(string $status): void
    {
        $this->status = $status;
    }

    public function getUpdateTime(): ?int
    {
        return $this->update_time;
    }

    public function setUpdateTime(?int $update_time): void
    {
        $this->update_time = $update_time;
    }

    public function getCreateTime(): ?int
    {
        return $this->create_time;
    }

    public function setCreateTime(?int $create_time): void
    {
        $this->create_time = $create_time;
    }
}

先声明接口的字段,这样可以快速跟前端对接。

{
  "code": 0,
  "message": "成功",
  "data": {
    "conllection": [
      {
        "id": 76,
        "pid": 0,
        "type": "menu",
        "title": "",
        "name": "",
        "path": "",
        "icon": "local-logo",
        "menu_type": null,
        "url": "",
        "component": "",
        "keepalive": 0,
        "extend": "none",
        "remark": "",
        "weigh": 0,
        "status": "1",
        "update_time": null,
        "create_time": null
      }
    ],
    "current_page": 1,
    "per_page": 1,
    "total": 1
  }
}

这个是定义好的分页数据返回类,

<?php

namespace App\Http\Pagination;

use App\Http\Pojo\AdminRulePojo;

class AdminRulePagination
{
    // use Pagination;

    protected int $current_page;

    protected int $per_page;

    protected int $total;
    protected array $conllection = [];

    public function getConllection(): array
    {
        return $this->conllection;
    }

    public function addConllection(AdminRulePojo $adminRulePojo): void
    {
        $this->conllection[] = $adminRulePojo;
    }

    public function getCurrentPage(): int
    {
        return $this->current_page;
    }

    public function setCurrentPage(int $current_page): void
    {
        $this->current_page = $current_page;
    }

    public function getPerPage(): int
    {
        return $this->per_page;
    }

    public function setPerPage(int $per_page): void
    {
        $this->per_page = $per_page;
    }

    public function getTotal(): int
    {
        return $this->total;
    }

    public function setTotal(int $total): void
    {
        $this->total = $total;
    }
}

因为php不支持泛型。所以用以下的方法来实现同一对象集合

 protected array $conllection = [];

    public function getConllection(): array
    {
        return $this->conllection;
    }

    public function addConllection(AdminRulePojo $adminRulePojo): void
    {
        $this->conllection[] = $adminRulePojo;
    }

然后定义list返回数据的接口,定义好接收的参数和返回的类型.

<?php

namespace App\Http\Service;

use App\Http\Pagination\AdminRulePagination;

interface IAdminRuleService
{
    public function list(int $per_page) :AdminRulePagination;
}

接口实现类

<?php

namespace App\Http\Service\impl;

use App\Http\Pagination\AdminRulePagination;
use App\Http\Pojo\AdminRulePojo;
use App\Http\Service\IAdminRuleService;
use App\Models\AdminRuleModel;

class AdminRuleService implements IAdminRuleService
{
    private AdminRuleModel $adminRuleModel;

    private AdminRulePagination $adminRulePagination;

    public function __construct(AdminRuleModel $adminRuleModel,AdminRulePagination $adminRulePagination)
    {
        $this->adminRuleModel=$adminRuleModel;
        $this->adminRulePagination=$adminRulePagination;
    }

    public function list(int $per_page) :AdminRulePagination
    {
        $res = $this->adminRuleModel::where('status', 1)->paginate($per_page)->toArray();

        if (!empty($res['data'])) {
            foreach ($res['data'] as $item) {
                $AdminRulePojo = new AdminRulePojo();
                $AdminRulePojo->setComponent($item['component']);
                $AdminRulePojo->setId($item['id']);
                $AdminRulePojo->setIcon($item['icon']);
                $this->adminRulePagination->addConllection($AdminRulePojo);
            }
        }

        $this->adminRulePagination->setCurrentPage($res['current_page']);
        $this->adminRulePagination->setTotal($res['total']);
        $this->adminRulePagination->setPerPage($res['per_page']);

        return $this->adminRulePagination;
    }
}

使用laravel的服务容器,控制反转,来绑定接口和实现类。

$this->app->bind(IAdminRuleService::class, AdminRuleService::class);

定义好接口

之后的实现类。可以换成原生sql查询的。其他数据库查询的。

比如mangodb获取数据的。

<?php

namespace App\Http\Service\impl;

use App\Http\Pagination\AdminRulePagination;
use App\Http\Pojo\AdminRulePojo;
use App\Http\Service\IAdminRuleService;
use App\Models\AdminRuleModel;
use Illuminate\Support\Facades\DB;

class MangoDbAdminRuleService implements IAdminRuleService
{
    private AdminRulePagination $adminRulePagination;

    public function __construct(AdminRulePagination $adminRulePagination)
    {
        $this->adminRulePagination=$adminRulePagination;
    }

    public function list(int $per_page) :AdminRulePagination
    {

    }
}

在服务容器那里改一下绑定就可以了。

 $this->app->bind(IAdminRuleService::class, MangoDbAdminRuleService::class);

这个是控制器,声明接口变量IAdminRuleService

<?php

namespace App\Http\ServiceController;

use App\Http\Service\IAdminRuleService;

class AdminRule
{
    use ServiceController;

    use Response;

    private IAdminRuleService $adminRuleService;

    public function __construct(IAdminRuleService $adminRuleService)
    {
        $this->adminRuleService = $adminRuleService;
    }

    public function list(int $per_page): array
    {
        $obj = $this->adminRuleService->list($per_page);

        $data = $this->objectToArray($obj);

        $this->setData($data);

        return $this->Success();
    }
}

以上就是这个依赖倒置的例子。

这个php的api接口查询数据的例子。可以随时换框架,随时换数据库操作类。使用orm或者sql查询的都行,也可以换数据库类型。

控制器作为高层模块,不依赖低层service层模块。两者通过接口和定义好的数据类AdminRulePagination来交换数据。

符合了依赖倒置的两大原则

1.高层模块不应该依赖于底层模块,二者都应该依赖抽象

2.抽象不应该依赖细节,细节应该依赖于抽象

抽象不依赖细节,按这个例子的情况就是,控制器层依赖IAdminRuleService接口,而不是依赖具体的实现类。

这个例子有点复杂。说了一些泛型的东西。但是实现这个过程中,会遇到php的缺点和不足。如果以上的例子用java实现。那就会容易一些。


xiang 发布于  2024-3-26 11:35 

php实现同一类型对象集合(泛型) php

java泛型

import java.util.ArrayList;

class Person {
    private String name;

    public Person(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

public class Main {
    public static void main(String[] args) {
        // 创建一个存储Person对象的ArrayList
        ArrayList<Person> personList = new ArrayList<>();
        personList.add(new Person("Alice"));
        personList.add(new Person("Bob"));

        // 遍历Person对象ArrayList
        for (Person person : personList) {
            System.out.println("Person name: " + person.getName());
        }
    }
}

在上面的示例中,我们创建了一个存储Person对象的ArrayList。通过指定泛型类型为Person,我们可以确保ArrayList只能存储Person对象。然后我们添加两个Person对象到ArrayList中,并通过遍历ArrayList来访问存储的Person对象。

泛型的作用

泛型的作用是在编译时提供更严格的类型检查,并且在编译时捕获和消除类型转换错误。使用泛型可以使代码更加灵活和安全,因为它们允许我们在编译时指定要操作的数据类型。这样可以确保数据的类型安全性,并减少在运行时出现的类型转换错误。泛型还可以提高代码的重用性,因为可以编写可以处理多种类型的通用代码。这使得在编写集合类等数据结构时,可以更好地支持不同类型的数据。

总之,泛型的作用是提供编译时类型安全性、代码重用性和更好的类型检查。

php泛型

而php没有泛型,但我又想实现一个功能是,同一个对象的集合,这个集合只允许添加该对象。

以下是php现实,只接受AdminRulePojo类型的参数

<?php

namespace App\Http\Conllection;

use App\Http\Pojo\AdminRulePojo;

class AdminRuleConllection {
    protected array $conllection=[];

    public function getConllection(): array
    {
        return $this->conllection;
    }

    public function addConllection(AdminRulePojo $adminRulePojo): void
    {
        $this->conllection[] = $adminRulePojo;
    }
}

xiang 发布于  2024-3-26 11:27 

ubuntu 安装 shadowsocks-libev

安装

sudo apt install shadowsocks-libev

配置

sudo vim /etc/shadowsocks-libev/config.json

作为客户端启动

ss-local -c /etc/shadowsocks-libev/config.json

后台启动

systemctl start shadowsocks-libev-local@config.service

参考
https://wiki.gulucat.com/doku.php/public/software/ss_install


xiang 发布于  2024-3-24 15:14 

php laravel 依赖注入,自动注入,接口绑定实现类 php

<?php

namespace App\Http\Controllers;

use App\Service\IComputerService;
use App\Validator\IValidator;
use Illuminate\Http\Request;

class IndexController extends Controller
{
    private IComputerService $ComputerService;
    private IValidator $Validator;

    public function __construct(IComputerService $ComputerService, IValidator $Validator)
    {
        $this->ComputerService = $ComputerService;
        $this->Validator = $Validator;
    }

    public function index(Request $request)
    {
        $param = $this->Validator->check($request);

        $res = $this->ComputerService->index($param['per_page']);

        return response()->json($res);
    }
}

以上是一个控制器,

  private IComputerService $ComputerService; 

以上是业务逻辑层,限定IComputerService接口

  <?php

namespace App\Service;

interface IComputerService{
    public function index($per_page);
}

以上是一个IComputerService 业务逻辑层的接口


 public function __construct(IComputerService $ComputerService, IValidator $Validator)
    {
        $this->ComputerService = $ComputerService;
        $this->Validator = $Validator;
    }

以上是把$ComputerService注入到IndexController

那么在哪里自动注入到服务容器呢?

<?php

namespace App\Providers;

use App\Http\Controllers\IndexController;
use App\Service\IComputerService;
use App\Service\impl\ComputerServiceImpl;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     */
    public function register(): void
    {
        //
    //    $this->app->bind(IComputerService::class, ComputerServiceImpl::class);

        $this->app->when(IndexController::class)
            ->needs(IComputerService::class)
            ->give(ComputerServiceImpl::class);
    }

    /**
     * Bootstrap any application services.
     */
    public function boot(): void
    {
        //
    }
}

以上代码是声明IndexController如果需要注入IComputerService接口,会自动调用ComputerServiceImpl实现类.

这样就可以实现代码解偶,符合依赖倒置原则,Dependency inversion principle,抽象不应依赖于细节,细节应依赖于抽象。

控制器如果要替换业务逻辑层即service层,不需要修改控制器的代码,只要新增和替换IComputerService 接口的实现类.

和在服务注册AppServiceProvider里修改一下 对应的实现类就可以了.

这里还有问题要注意.就是IComputerService没有指定返回数据的类型或者对象类型.这样不方便以后的修改或者替换.


xiang 发布于  2024-3-11 15:13 

rabbitmq 定时任务 指定时间发送的队列消息 php

以下是一个使用 RabbitMQ 的延迟队列来实现消息延迟发送的简单示例,使用 PHP 的 AMQP 扩展:

<?php
// 创建连接和channel
$connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
$channel = $connection->channel();

// 定义延迟队列和目标队列
$delayedQueueName = 'delayed_queue';
$targetQueueName = 'target_queue';

// 声明延迟队列
$channel->queue_declare($delayedQueueName, false, true, false, false, false, [
    'x-dead-letter-exchange' => '',
    'x-dead-letter-routing-key' => $targetQueueName
]);

// 声明目标队列
$channel->queue_declare($targetQueueName, false, true, false, false);

// 创建消息
$messageBody = 'Hello, delayed message!';
$message = new AMQPMessage($messageBody, [
    'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT
]);

// 设置消息的 TTL 为 10 秒
$message->set('expiration', 10000);

// 发送消息到延迟队列
$channel->basic_publish($message, '', $delayedQueueName);

echo " [x] Sent 'Hello, delayed message!'\n";

// 关闭连接
$channel->close();
$connection->close();
?>

在这个示例中,我们创建了一个延迟队列 delayed_queue 和一个目标队列 target_queue。我们发送一条消息到延迟队列,并设置消息的 TTL 为 10 秒。当消息到达延迟时间后,会被投递到目标队列中。

请注意,以上示例仅为演示目的,实际应用中可能需要更多的错误处理和边界情况的考虑。


xiang 发布于  2024-2-29 15:11 

xiang 发布于  2024-2-28 17:42