分类目录归档:PHP

php swoole 队列、异步、协程并发curl请求

 public function curls()
    {
        cli_set_process_title(__FILE__ . ':curls');
        $redis = $this->redis;
        $file_mtime = $this->file_mtime();
        $st = time();

        error_reporting(E_ALL);
        ini_set('swoole.display_errors', 'On');

        \Swoole\Coroutine::set(['hook_flags' => SWOOLE_HOOK_ALL]);
        \Swoole\Coroutine\run(function () use ($st, $file_mtime, $redis) {
            for ($i = 0; $i < 5; $i++) {
                \Swoole\Coroutine::create(function () use ($i, $st, $file_mtime, $redis) {
                    while (true) {
                        $task = $redis->blPop('curl_queue', 5);
                        if (!empty($task)) {
                            [$key, $val] = $task;
                            $json = json_decode($val, true);
                            if ($json) {
                                $method = $json['method'];
                                $uri = $json['uri'];
                                $headers = $json['headers'] ?? [];
                                $body = $json['body'] ?? null;

                                $ch = curl_init();
                                if ($method === 'POST') {
                                    curl_setopt($ch, CURLOPT_POST, true);
                                    curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
                                }
                                if (count($headers)) {
                                    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
                                }
                                curl_setopt($ch, CURLOPT_URL, $uri);
                                curl_setopt($ch, CURLOPT_HEADER, false);
                                curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
                                $result = curl_exec($ch);
                                curl_close($ch);

                                $content = date('Y-m-d H:i:s') . ": \n";
                                $content .= "url: $method $uri\n";
                                $content .= "headers: " . json_encode($headers) . "\n";
                                $content .= "body: $body\n";
                                $content .= "result: $result\n";
                                $content .= "\n";
                                recordlog($content, 'curls');
                            }
                        } else {
                            \Swoole\Coroutine::sleep(0.001);
                        }
                        if ($file_mtime != $this->file_mtime()) break;
                        if (time() - $st > 3600) break;
                    }
                });
            }
        });
    }

微信PHP解密

openssl 实现:

        $result = openssl_decrypt(base64_decode($data),
            "AES-128-CBC",
            base64_decode($key),
            OPENSSL_RAW_DATA,
            base64_decode($iv));
        var_dump($result);

mcrypt 实现


        $module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
        //用密钥key、初始化向量初始化
        mcrypt_generic_init($module, base64_decode($key), base64_decode($iv));
        //**执行解密**(得到带有PKCS#7填充的半原文,所以要去除填充)
        $result = mdecrypt_generic($module, base64_decode($data));
        //清理工作与关闭解密
        mcrypt_generic_deinit($module);
        mcrypt_module_close($module);
        //去除填充
        $lastByte = substr($result, -1);
        $result = substr($result, 0, strlen($result) - ord($lastByte));
        var_dump($result);

PHP单机文件排队锁


class Locker
{

    private static array $lockers = [];

    /**
     * 获取锁
     * @param string $key 锁的唯一标识
     * @param int $timeout 超时时间(秒),0表示无限等待
     * @return bool 是否获取到锁
     */
    public static function wait(string $key, int $timeout = 0): bool
    {
        $file = sys_get_temp_dir() . "/.lock_" . md5($key) . ".tmp";
        $start_time = time();

        while (true) {
            // 检查是否超时
            if ($timeout > 0 && (time() - $start_time) >= $timeout) {
                return false;
            }

            // 尝试打开文件
            $fp = @fopen($file, "c+");
            if (!$fp) {
                usleep(10000); // 等待10毫秒
                continue;
            }

            // 尝试获取锁
            if (flock($fp, LOCK_EX | LOCK_NB)) {
                // 获取锁成功
                self::$lockers[$key] = $fp;
                return true;
            } else {
                // 获取锁失败,关闭文件句柄
                fclose($fp);
                usleep(10000); // 等待10毫秒后重试
            }
        }
    }

    /**
     * 释放锁
     * @param string $key 锁的唯一标识
     * @return bool 是否成功释放
     */
    public static function release(string $key): bool
    {
        if (!isset(self::$lockers[$key])) {
            return false;
        }

        $fp = self::$lockers[$key];
        if (is_resource($fp)) {
            flock($fp, LOCK_UN); // 释放文件锁
            fclose($fp);         // 关闭文件句柄
        }

        unset(self::$lockers[$key]);
        return true;
    }

    /**
     * 检查是否持有某个锁
     * @param string $key 锁的唯一标识
     * @return bool 是否持有锁
     */
    public static function isLocked(string $key): bool
    {
        return isset(self::$lockers[$key]) && is_resource(self::$lockers[$key]);
    }

    /**
     * 析构时释放所有锁
     */
    public function __destruct()
    {
        foreach (array_keys(self::$lockers) as $key) {
            self::release($key);
        }
    }
}

使用:
        if (!Locker::wait(md5("key"), 3)) {
            recordlog("3秒内获取不到锁返回提示 请勿重复请求  ");
            # 3秒内获取不到锁返回提示
            exit_json(0, '请勿重复请求');
        }

编译php7.1 openssl3.x版本过高?

下载:

wget https://openssl.org/source/openssl-1.1.1u.tar.gz tar -xzvf openssl-1.1.1u.tar.gz cd openssl-1.1.1u

编译安装:

./config --prefix=/usr/local/openssl1.1 --openssldir=/usr/local/openssl1.1/ssl
make -j$(nproc)
make install

临时改环境变量:

export PATH=/usr/local/openssl1.1/bin:$PATH
export LD_LIBRARY_PATH=/usr/local/openssl1.1/lib:$LD_LIBRARY_PATH

configure php:

./configure --with-mysqli --with-pdo-mysql --enable-mbstring --enable-fpm --with-gd=/usr --with-curl --with-mcrypt --with-openssl --with-freetype-dir --with-zlib --with-jpeg-dir --with-png-dir --with-xpm-dir --enable-pcntl --with-gmp --enable-zip --enable-bcmath --with-openssl=/usr/local/openssl1.1/ --with-openssl-dir=/usr/local/openssl1.1/

编译安装:(略)

php switch 不加 break 会怎么样?

在PHP中,switch语句如果不加break会导致”case穿透”现象。具体表现如下:

当匹配到某个case后,如果没有break语句,代码会继续执行后续所有case中的语句,直到遇到break或switch结束

这种特性有时会被故意利用来实现多个case共享同一段代码的逻辑

示例说明:

switch ($value) {
  case 1:
    echo "这是1";
    // 这里没有break
  case 2:
    echo "这是2";
    break;
  case 3:
    echo "这是3";
    break;
}

当$value=1时,输出会是:
“这是1这是2”

因为匹配到case 1后,没有break阻止,所以继续执行了case 2的代码。

建议:除非有特殊需求,否则每个case后都应该加上break语句以避免意外行为。

PHP安全的获取ip

    public static function getIpX(): ?string {
        [$ip] = self::getIp();
        return $ip;
    }

    public static function getIp(): array {
        $ip0 = $ip = $_SERVER['REMOTE_ADDR'] ?? null;
        if (in_array($ip, [
            '10.29.185.7', '127.0.0.1', '172.17.0.1', '172.31.242.237', # 可信IP列表
        ])) {
            $ip1 = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? null;
            if ($ip1) {
                $ip0 = $ip1;
                $ip = explode(',', $ip1)[0];
            }
        }
        if (!$ip) {
            $ip = $_SERVER['REMOTE_ADDR'] ?? null;
        }
        return [$ip, $ip0];
    }

PHP简易排队限流实现

$fp = fopen(sprintf("tmp/wk_exam_examination.%d.lock", $userid % 10), "w");
if (!flock($fp, LOCK_EX | LOCK_NB)) {
    ?>
  <style>
    #info {
      text-align: center;
      margin: 50px 0;
    }

    #info td {
      font-size: 36px;
      color: seagreen;
    }
  </style>
  <div id='info'></div>
  <script>
    let sec = 5000
    setTimeout(function () {
      window.location.reload()
    }, sec)
    setInterval(function () {
      sec -= 100
      if (sec >= 0) {
        let ok = sec / 1000
        document.getElementById('info').innerHTML = `<table style="margin: 0 auto;">
        <tr><td style='width: 50%; text-align: right;'>页面排队中...</td>
        <td style="width: 2em; text-align: center;">${ok}</td><td>秒后将重试!</td></tr></table>`;
      }
    }, 100)
  </script><?php
    die;
}