资讯专栏INFORMATION COLUMN

利用php重载和curl的并行方式优雅的封装小米推送sdk

Guakin_Huang / 3346人阅读

摘要:背景前段时间迁移小米推送部分代码,这部分之前是其他人负责的。并行推送端实体端实体推送结果以上就是这些了,通过以上的方式我们就用很少的代码封装了一个小米,目前只实现了按登记别名用户账号标签多标签全体推送。

背景

前段时间迁移小米推送部分代码,这部分之前是其他人负责的。读了代码,发现了两点:

所有接口的实现除了url和传参基本都是一致的

android和ios的设备需要分别推送一次

刚好这段时间了解了一下php的重载概念【动态的创建类属型和方法,不同于java等的参数不同方法名相通的概念,具体的概念可以RTFM】和curl的并发方式,所以

对于以上第一点:是否可以利用php的重载概念,用魔术方法__call()动态的变更接口调用的url参数和请求参数,去实现对小米推送不同接口的调用,也就是说我们所有的调用逻辑都是实现在__call这一个方法中,却对外提供小米推送不同接口的调用方法,这样就会极大的简化代码

对于以上第二点:是否可以利用curl的并行方式一次性推送给android和ios设备,这样理论上我们可以减少一次推送调用的时间,因为我们不需要再去等待给一类设备推送完了,再去推送给另一类设备


具体实现 接下来,我们开始造代码。首先,构造一个mipush的实体,实体的成员属性包括:运行环境、初始化的一些配置参数、所实现的小米接口的信息(接口uri和参数等)
    /**
     * 运行环境 develop:开发环境 product:生产环境
     * @var string
     */
    private $_environment = "develop";

    /**
     * 设备系统类型 android ios
     * @var string
     */
    private $_osType      = "";

    /**
     * 小米推送域名
     * @var string
     */
    private $_host        = "";

    /**
     * 请求头
     * @var string
     */
    private $_headers     = "";

    /**
     * 接口url
     * @var string
     */
    private $_url         = "";

    /**
     * 调用的接口方法名称
     * @var array
     */
    private $_function    = [];

    /**
     * 请求参数
     * @var array
     */
    private $_data        = [];

    /**
     * 小米推送设置
     * @var array
     */
    private $_options = [
        "title"                   => "消息通知自定义title",
        "restricted_package_name" => "",
        "pass_through"            => 0, // 0 通知栏消息 1 透传
        "notify_type"             => -1, // -1:默认所有,1:使用默认提示音提示,2:使用默认震动提示,4:使用默认led灯光提示
        "time_to_send"             => 0, // 定时推送 单位毫秒 默认0
    ];

    /**
     * 运行环境配置
     * @var array
     */
    private static $_environmentConfig = [
        "domain" => [
            "product"  => "https://api.xmpush.xiaomi.com/",
            "develop"  => "https://sandbox.xmpush.xiaomi.com/"
        ],
    ];

    /**
     * 小米推送接口信息定义
     * 
     * url/请求参数
     * @var array
     */
    private $_functionDefine = [
        "regid" => [
            "uri" => "v3/message/regid",
            "arguments" => [
                "registration_id" => [
                    "type" => "array",
                    "must" => "y"
                ],
                "description" => [
                    "type" => "string",
                    "must" => "y"
                ],
                "params" => [//自定义参数
                    "type" => "array",
                    "must" => "n"
                ],
            ]
        ],
        "userAccount" => [
            "uri" => "v2/message/user_account",
            "arguments" => [
                "user_account" => [
                    "type" => "array",
                    "must" => "y"
                ],
                "description" => [
                    "type" => "string",
                    "must" => "y"
                ],
                "params" => [//自定义参数
                    "type" => "array",
                    "must" => "n"
                ],
            ]
        ],
        "alias" => [
            "uri" => "v3/message/alias",
            "arguments" => [
                "alias" => [
                    "type" => "array",
                    "must" => "y"
                ],
                "description" => [
                    "type" => "string",
                    "must" => "y"
                ],
                "params" => [//自定义参数
                    "type" => "array",
                    "must" => "n"
                ],
            ]
        ],
        "topic" => [
            "uri" => "v3/message/topic",
            "arguments" => [
                "topics" => [
                    "type" => "array",
                    "must" => "y"
                ],
                "description" => [
                    "type" => "string",
                    "must" => "y"
                ],
                "params" => [//自定义参数
                    "type" => "array",
                    "must" => "n"
                ],
            ]
        ],
        "multiTopic" => [
            "uri" => "v3/message/multi_topic",
            "arguments" => [
                "topics" => [
                    "type" => "array",
                    "must" => "y"
                ],
                "topics_op" => [// UNION并集,INTERSECTION交集,EXCEPT差集
                    "type" => "string",
                    "must" => "y"
                ],
                "description" => [
                    "type" => "string",
                    "must" => "y"
                ],
                "params" => [//自定义参数
                    "type" => "array",
                    "must" => "n"
                ],
            ]
        ],
        "all" => [
            "uri" => "v3/message/all",
            "arguments" => [
                "description" => [
                    "type" => "string",
                    "must" => "y"
                ],
                "params" => [//自定义参数
                    "type" => "array",
                    "must" => "n"
                ],
            ]
        ],
    ];
mipush实体的构造函数:实现对一系列配置的初始化
/**
     * 初始化配置
     * 
     * @param $string $os_type 系统类型
     * @param $string $config  配置
     * @param array   $options 设置 [
     *                        "title"        => "string,标题", 
     *                        "pass_through" => "tinyint,0通知栏消息,1透传,默认0"
     *                        "notify_type"  => "tinyint,-1,1,2,3,4",
     *                        "time_to_send" => "int, 定时推送, 毫秒"
     *                        ]
     * @param string  $environment 环境
     */
    public function __construct($os_type="", $config=array(), $options=array(), $environment="")
    {
        /* init environment */
        if ($environment) {
            $this->_environment = $environment;
        }
        if ($os_type === "ios") {
            $this->_host     = self::$_environmentConfig["domain"][$this->_environment];// ios
        }else{
            $this->_host     = self::$_environmentConfig["domain"]["product"];// android
        }
        
        /* init option */
        $this->_headers   = [];
        $this->_headers[] = "Authorization: key=" . $config["secret"];
        if ($os_type === "android") {
            $this->_options["restricted_package_name"] = $config["package_name"];
        }
        foreach ($this->_options as $k => &$v) {
            if (in_array($k, $options)) {
                $v = $options[$k];
            }
        }
    }
mipush实体的魔法方法__call:动态实现对小米推送接口的参数验证和调用,以至于我们未来实现新的小米推送接口及实现配置就可以。
/**
     * 魔术方法
     * 
     * 重载对象方法
     * @param  string $name      小米推送方法名称
     * @param  array  $arguments 请求参数
     * @return mixed             void||object
     */
    public function __call($name,$arguments)
    {
        $arguments = $arguments[0];
        $this->_function = $this->_functionDefine[$name];
        $this->_url = $this->_host . $this->_function["uri"];
        $this->dataCheck($arguments);

        switch ($name) {
            case "regid":
                $this->_data["registration_id"] = $arguments["registration_id"];
                break;
            case "userAccount":
                $this->_data["user_account"] = implode(",", $arguments["user_account"]);
                break;
            case "alias":
                $this->_data["alias"]        = implode(",", $arguments["alias"]);
                break;
            case "topic":
                $this->_data["topic"]        = $arguments["topic"];
                break;
            case "multiTopic":
                $this->_data["topics"]       = implode(";$;", $arguments["topics"]);
                $this->_data["topic_op"]     = $arguments["topic_op"];
                break;
            case "all":
                $this->_data["topics"]       = implode(";$;", $topics);
                $this->_data["topic_op"]     = "UNION";
                break;
                
                default:
                throw new Exception("Sorry, This function is useless in this version", 404);
                break;
        }

        $this->_data["description"]  = $arguments["description"];
        if($arguments["params"]) {
            foreach ($arguments["params"] as $k => $v) {
                $this->_data["extra.".$k] = $v;// 自定义参数
            }
        }
        if ($this->_osType === "android") {
            $this->_data = array_merge($this->_data, $this->_options);
        }
    }

定义完小米推送实体后,我们只需要通过mipush这个实体,实例化生产不同设备类型的对象,再由curl并行的方式发起推送即可。

/**
     * 并行推送
     * 
     * @param  Mipush $mipush_ios     ios端实体
     * @param  Mipush $mipush_android android端实体
     * @return array                  推送结果
     */
    private static function curlRequest(Mipush $mipush_ios, Mipush $mipush_android) 
    {
        $ch_ios     = curl_init();
        $ch_android = curl_init();
        curl_setopt($ch_ios, CURLOPT_URL, $mipush_ios->_url);
        curl_setopt($ch_ios, CURLOPT_POST, 1);
        curl_setopt($ch_ios, CURLOPT_POSTFIELDS, $mipush_ios->_data);
        curl_setopt($ch_ios, CURLOPT_HTTPHEADER, $mipush_ios->_headers);
        curl_setopt($ch_ios, CURLOPT_RETURNTRANSFER, 1); 
        
        curl_setopt($ch_android, CURLOPT_URL, $mipush_android->_url);
        curl_setopt($ch_android, CURLOPT_POST, 1);
        curl_setopt($ch_android, CURLOPT_POSTFIELDS, $mipush_android->_data);
        curl_setopt($ch_android, CURLOPT_HTTPHEADER, $mipush_android->_headers);
        curl_setopt($ch_android, CURLOPT_RETURNTRANSFER, 1); 
        
        $mh = curl_multi_init();
        curl_multi_add_handle($mh, $ch_ios);
        curl_multi_add_handle($mh, $ch_android);

        $running=null;
        do {
           curl_multi_exec($mh,$running);
        } while($running > 0);

        $result["ios"]        = json_decode(curl_multi_getcontent($ch_ios), true);
        $result["android"] = json_decode(curl_multi_getcontent($ch_android), true);

        curl_multi_remove_handle($mh, $ch_ios);
        curl_multi_remove_handle($mh, $ch_android);
        curl_multi_close($mh);
        return $result;
    }

以上就是这些了,通过以上的方式我们就用很少的代码封装了一个小米sdk,目前只实现了按regid(登记id),alias(别名),user_account(用户账号),topic(标签), multi_topic(多标签),all(全体)推送。


如何使用?
composer require tigerb/easy-mipush

使用格式:
try {
    Push::init(
        ["secret" => "string,必传,ios密钥"], 
        ["secret" => "string,必传,android密钥", "package_name" => "string,必传,android包名"]
        [   
          "title"        => "string,非必传,消息通知自定义title",
          "pass_through" => "int,非必传,0通知栏消息,1透传,默认0",
          "notify_type"  => "int,非必传,-1:默认所有,1:使用默认提示音提示,2:使用默认震动提示,4:使用默认led灯光提示",
          "time_to_send" => "int,非必传,定时推送,单位毫秒,默认0",
        ],
        "string,develop开发环境,product生产环境, 默认develop"
        );  
    $res = Push::toUse("string,小米push方法名", "array, 该方法对应的参数");
    echo json_encode($res, JSON_UNESCAPED_UNICODE);
} catch (Exception $e) {
    echo $e->getMessage();
}

使用示例:
use MipushPush;

require "./vendor/autoload.php";

try {
    Push::init(
        ["secret" => "test"], 
        ["secret" => "test", "package_name" => "com.test"],
        [   
          "title"        => "test",
          "pass_through" => 0,
          "notify_type"  => -1,
          "time_to_send" => 0,
        ],
        "develop"
        );  
    $res = Push::toUse("userAccount", [
            "user_account" => [1],
            "description"  => "test"
        ]);
    echo json_encode($res, JSON_UNESCAPED_UNICODE);
} catch (Exception $e) {
    echo $e->getMessage();
}

最后,第一次写这种文章,写的不是很好,写的不对的地方谢谢大家留言指证出来,其次,还没验证过这里使用魔术方法的性能消耗,因为之前有看过鸟哥的文章不赞成使用魔术方法,欢迎大家留言指证,谢谢。

完整代码地址: https://github.com/TIGERB/eas...

扫面下方二维码关注我的技术公众号,及时为大家推送我的原创技术分享

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/21940.html

相关文章

  • 小米推送 php 服务端框架中看看代码该怎么写

    摘要:我看可以发现小米推送目录结构是这样的其中是底层的网络库,主要是封装了的和方法请求远程的服务器并利用提供的类来接收服务器返回的数据,代码十分优雅,我们可以看看。 小米推送框架 近期因为有需要稍微看了一下小米推送的 php 版本服务端框架,这个推送平台免费的,我们只需要告诉小米平台我们要推什么设备,它会自己帮我们推,而且还会统计很多信息给我们分析,如抵达率和点击率等等,当然客户端也要用小米...

    loonggg 评论0 收藏0
  • Java字节码修改神器HiBeaver:黑掉你SDK

    摘要:下面我们正式开始尝试小米推送,首先,找出其业务逻辑中的一个节点。因为小米推送是商业产品,这里不便于探索太多内容,但是通过这个插件可以比较方便的进行类似的研究。 前言 有时候我们在Java开发过程中可能有这样的需求:需要研究或者修改工程依赖的Jar包中的一些逻辑,查看代码运行中Jar包代码内部的取值情况(比如了解SDK与其服务器通信的请求报文加密前的情况)。 这个需求类似于Hook。 但...

    Lavender 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<