树莓派编译安装PHP7.4

首先安装依赖

apt install -y libxml2 libxml2-dev libjpeg-dev libcurl4-openssl-dev libreadline-dev libzip-dev libfreetype6-dev libssl-dev libsqlite3-dev libonig-dev

下载源码并编译安装

wget https://www.php.net/distributions/php-7.4.15.tar.gz
tar zxf php-7.4.15.tar.gz
cd php-7.4.15

./configure \
--prefix=/usr/local/php \
--with-curl --with-freetype --with-gettext --with-iconv-dir --with-jpeg --with-libdir=lib64 --with-openssl --with-pear --with-readline --with-xmlrpc --with-zip --with-zlib \
--with-mysqli=mysqlnd --with-pdo-mysql=mysqlnd \
--enable-bcmath --enable-calendar --enable-exif --enable-fpm --enable-ftp --enable-gd --enable-intl --enable-mbstring --enable-mysqlnd --enable-pcntl \
--enable-soap --enable-sockets
make -j8 && make install

配置文件

cp php.ini-production /usr/local/php/lib/php.ini
sed -i 's/post_max_size = 8M/post_max_size = 50M/g' /usr/local/php/lib/php.ini
sed -i 's/upload_max_filesize = 2M/upload_max_filesize = 50M/g' /usr/local/php/lib/php.ini
sed -i 's/;date.timezone =/date.timezone = PRC/g' /usr/local/php/lib/php.ini
sed -i 's/expose_php = On/expose_php = Off/g' /usr/local/php/lib/php.ini

树莓派自动解析IPV6地址

自动解析树莓派的 IPV6 地址到某个域名

<?php

// 调试模式
define('DebugModel', false);

// 域名
define('Domain', 'device.raspberrypi.org');

// Clouflare API 地址
define('ClouflareApiBase', 'https://api.cloudflare.com/client/v4');

// Clouflare API 密钥
define('ClouflareAuthKey', 'ClouflareAuthKey');

/**
 *  获取本机 IPV6 地址
 */
function getIPV6()
{
    if (!file_exists('/proc/net/if_inet6')) {
        throw new Exception("Get IPV6 Failed!");
    }
    $_ipv6 = explode(" ", file_get_contents('/proc/net/if_inet6'))[0];
    debug('Getting IPV6');
    return implode(':', str_split($_ipv6, 4));
}

/**
 *  获取顶级域名
 */
function getMainDomain($domain)
{
    $_domain = explode('.', $domain);
    $domain = [];
    $domain[] = array_pop($_domain);
    $domain[] = array_pop($_domain);
    debug('Getting Main Domain');
    return $domain[1] . '.' . $domain[0];
}

/**
 *  获取域名 Zone ID
 */
function getZoneID($domain)
{
    $mainDomain = getMainDomain($domain);
    $url = sprintf('%s/zones', ClouflareApiBase);
    debug('Send ZoneID Request');
    $dns = json_decode(httpRequest('GET', $url));
    debug('Get ZoneID Response');
    if (isset($dns->result)) {
        foreach ($dns->result as $v) {
            if ($v->name === $mainDomain) {
                return $v->id;
            }
        }
    } else {
        throw new Exception("Get ZoneID Failed!");
    }
}

/**
 *  获取域名 Record
 */
function getRecord($zoneID, $domain, $type = 'A')
{
    $url = sprintf('%s/zones/%s/dns_records', ClouflareApiBase, $zoneID);
    debug('Send RecordID Request');
    $dns = json_decode(httpRequest('GET', $url));
    debug('Get RecordID Response');
    if (isset($dns->result)) {
        foreach ($dns->result as $v) {
            if ($v->name === $domain && $v->type === $type) {
                return $v;
            }
        }
    }
    return false;
}

/**
 *  新增域名解析
 */
function setRecord($zoneID, $domain, $ipv6)
{
    $data = array(
        'name' => $domain,
        'type' => 'AAAA',
        'content' => $ipv6,
        'ttl' => 120
    );
    $url = sprintf('%s/zones/%s/dns_records', ClouflareApiBase, $zoneID);
    $response = json_decode(httpRequest('POST', $url, json_encode($data)));
    if (isset($response->result->id)) {
        return true;
    } else {
        return false;
    }
}

/**
 *  更新域名解析
 */
function updateRecord($zoneID, $recordID, $domain, $ipv6)
{
    $data = array(
        'name' => $domain,
        'type' => 'AAAA',
        'content' => $ipv6,
        'ttl' => 120
    );
    $url = sprintf('%s/zones/%s/dns_records/%s', ClouflareApiBase, $zoneID, $recordID);
    $response = json_decode(httpRequest('PUT', $url, json_encode($data)));
    if (isset($response->result->id)) {
        return true;
    } else {
        return false;
    }
}

/**
 *  发起 HTTP 请求
 */
function httpRequest($method = 'GET', $url, $post = '')
{
    $curl = curl_init();
    curl_setopt($curl, CURLOPT_URL, $url);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($curl, CURLOPT_ENCODING, 'gzip');
    curl_setopt($curl, CURLOPT_HTTPHEADER, ['Authorization: Bearer ' . ClouflareAuthKey, 'Content-Type: application/json']);

    if ($method !== 'GET') {
        curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method);
        curl_setopt($curl, CURLOPT_POSTFIELDS, $post);
    }

    $data = curl_exec($curl);
    $error = curl_error($curl);
    curl_close($curl);

    if ($error) {
        throw new Exception($error);
    } else {
        return $data;
    }
}

function debug($log)
{
    if (DebugModel) {
        $file = '/tmp/dns.log';
        $log = date('Y-m-d H:i:s') . "\t" . $log . "\n";
        echo $log;
        file_put_contents($file, $log, FILE_APPEND);
    }
}

function main($domain)
{
    $ipv6 = getIPV6();
    $zoneID = getZoneID($domain);
    $record = getRecord($zoneID, $domain, 'AAAA');
    if (isset($record->id)) {
        if ($record->content !== $ipv6) {
            debug('Update IPV6 Record');
            $update = updateRecord($zoneID, $record->id, $domain, $ipv6);
        } else {
            return debug('No Change IPV6 Record');
        }
    } else {
        debug('New IPV6 Record');
        $update = setRecord($zoneID, $domain, $ipv6);
    }
    if ($update) {
        debug('DNS Record Update Success!');
    } else {
        debug('DNS Record Update Failed!');
    }
}

main(Domain);

Hyperf 环境配置和安装

安装 PHP 环境

# PHP 7.3.19
# Swoole 4.5.2
# Composer 1.10.10

配置阿里云镜像源

composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/

安装 Hyperf

composer create-project hyperf/hyperf-skeleton

Docker 方式安装

docker run -v D:\wamp\www\html\hyperf:/hyperf-skeleton -p 9501:9501 -it --entrypoint /bin/sh hyperf/hyperf:latest
发布于
归类为php

Guzzle 使用文档

安装

composer require guzzlehttp/guzzle:7.0.1

发起 Get 请求

use GuzzleHttp\Client as guzzleClient;

$guzzleClient = new guzzleClient([
    'timeout' => 2.0
]);

// 同步请求方式
$response = $guzzleClient->get('http://api.org/get', ['headers' => ['User-Agent' => 'Guzzle'], 'http_errors' => false]);
$code = $response->getStatusCode();
$body = $response->getBody();
$content = $body->getContents();
// 异步请求方式
$promise = $guzzleClient->getAsync('http://api.org/get');
$promise->then(
    function (ResponseInterface $res) {
        echo $res->getStatusCode() . "\n";
    },
    function (RequestException $e) {
        echo $e->getMessage() . "\n";
        echo $e->getRequest()->getMethod();
    }
);

发起 Post 请求

use GuzzleHttp\Client as guzzleClient;

$guzzleClient = new guzzleClient([
    'timeout' => 2.0
]);

// 原始类型
$response = $guzzleClient->post('http://api.org/post', [
    'body' => 'raw data'
]);

// json 类型 application/json
$response = $guzzleClient->post('http://api.org/post', [
    'json' => ['name' => 'admin']
]);

// 表单类型 application/x-www-form-urlencoded
$response = $guzzleClient->post('http://api.org/post', [
    'form_params' => [
        'username' => 'abc',
        'password' => '123'
    ]
]);

// 上传文件 multipart/form-data
$response = $guzzleClient->post('http://api.org/post', [
    'multipart' => [
        [
            'name'     => 'user',
            'contents' => 'admin'
        ],
        [
            'name'     => 'file',
            'contents' => fopen('/path/to/file', 'r')
        ],
    ]
]);
发布于
归类为php

EasySwoole环境和框架安装

安装 PHP 环境

# PHP 7.3.19
# Swoole 4.5.2
# Composer 1.10.10

配置阿里云镜像源

composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/

安装 EasySwoole

composer require easyswoole/easyswoole=3.3.7
php vendor/easyswoole/easyswoole/bin/easyswoole install
composer dump-autoload

安装 RPC 组件

composer require easyswoole/rpc=4.0.10

启动 EasySwoole

php easyswoole start
发布于
归类为php

PHP实现Base16编码算法

我们经常使用的 Base64 编码用来对字符串或者字节数组进行编码转换,以方便存储。可是Base64 编码的结果可能会带有特殊字符,应用场景有限。

而 Base16 编码生成的结果是用[0-9A-F]十六个字符表示,可以用来生成字符索引和文件名等,应用范围比 Base64 要大的多。

缺点的话 Base16 编码的结果字符串比 Base64 编码的结果字符串要长, Base64 大概是1.3倍长,而 Base16 是原长度的两倍。

Base16 编码的原理是先获取字符串每个字节的二进制值,高位用0补充到8位。然后每4比特分割成一组,每组的二进制值转换成十进制,然后再对应到[0-9A-F],对应的结果就是编码的值。

PHP 官方并没有 Base16 的编码实现,使用下面的函数就可以实现Base16的编码和解码。

function base16_encode($string)
{
    $encode = '';
    $chars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'];
    for ($i = 0; $i < strlen($string); $i++) {
        $encode .= $chars[(ord($string[$i]) & 0b11110000) >> 4] . $chars[ord($string[$i]) & 0b00001111];
    }
    return $encode;
}

function base16_decode($encode)
{
    $result = '';
    for ($i = 0; $i < strlen($encode) / 2; $i++) {
        $result .= chr(intval(substr($encode, $i * 2, 2), 16));
    }
    return $result;
}
发布于
归类为php

生成TOTP临时密码

生成二次验证的临时密码,兼容谷歌验证器

function base32Decode($in)
{
    $l = strlen($in);
    $n = $bs = 0;
    for ($i = 0; $i < $l; $i++) {
        $n <<= 5;
        $n += stripos('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567', $in[$i]);
        $bs = ($bs + 5) % 8;
        $out .= $bs < 5 ? chr(($n & (255 << $bs)) >> $bs) : null;
    }
    return $out;
}

function getOTP($secret)
{
    $seed = base32Decode($secret);
    $time = str_pad(pack('N', intval(0 + time() / 30)), 8, "\x00", STR_PAD_LEFT);
    $hash = hash_hmac('sha1', $time, $seed, false);
    $otp = (hexdec(substr($hash, hexdec($hash[39]) * 2, 8)) & 0x7fffffff) % pow(10, 6);
    return sprintf("%'06u", $otp);
}

echo getOTP('secret_key');
发布于
归类为php

列出腾讯云存储的文件

使用腾讯云 COS API 查看存储里的所有文件(最多1000个)

define('SecretId', 'SecretId');
define('SecretKey', 'SecretKey');

function listBucketFiles($region, $bucket)
{

    $host = $bucket . '.cos.' . $region . '.myqcloud.com';
    $path = '/';
    $url = 'https://' . $host . $path;
    $header = array(
        'Authorization: ' . requestSign($host, 'GET', $path),
        'Date: ' . gmdate('D, d M Y H:i:s T'),
    );

    $curl = curl_init();
    curl_setopt($curl, CURLOPT_URL, $url);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
    curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
    $response = curl_exec($curl);
    curl_close($curl);

    $files = [];
    $list = simplexml_load_string($response);
    foreach ($list->Contents as $v) {
        $files[] = (String) $v->Key;
    }

    return $files;
}

function requestSign($host, $method, $path)
{
    $signTime = (string) (time() - 60) . ';' . (string) (time() + 1200);
    $httpString = sprintf("%s\n%s\n\nhost=%s\n", strtolower($method), $path, $host);
    $stringToSign = sprintf("sha1\n%s\n%s\n", $signTime, sha1($httpString));
    $signKey = hash_hmac('sha1', $signTime, SecretKey);
    $signature = hash_hmac('sha1', $stringToSign, $signKey);
    return sprintf('q-sign-algorithm=sha1&q-ak=%s&q-sign-time=%s&q-key-time=%s&q-header-list=host&q-url-param-list=&q-signature=%s', SecretId, $signTime, $signTime, $signature);
}
发布于
归类为php

使用阿里云API管理域名解析


define('AccessKeyId', 'AccessKeyId');
define('AccessKeySecret', 'AccessKeySecret');

date_default_timezone_set('UTC');

/**
 *  获取请求签名
 */
function getSign($add)
{
    $nonce = time() . rand(11111, 99999);
    $time = date('Y-m-d') . 'T' . date('H:i:s') . 'Z';
    $data = array(
        'AccessKeyId' => AccessKeyId,
        'Format' => 'json',
        'SignatureMethod' => 'HMAC-SHA1',
        'SignatureNonce' => $nonce,
        'SignatureVersion' => '1.0',
        'Timestamp' => $time,
        'Version' => '2015-01-09',
    );
    $data = array_merge($data, $add);
    ksort($data);
    $format = http_build_query($data);
    $signUrl = 'GET&%2F&' . urlencode($format);
    $sign = urlencode(base64_encode(hash_hmac('sha1', $signUrl, AccessKeySecret . '&', true)));
    return array('url' => $format, 'sign' => $sign);
}

/**
 *  获取域名列表
 */
function getDomains()
{
    $sign = getSign(['Action' => 'DescribeDomains']);
    $url = 'https://alidns.aliyuncs.com/?' . $sign['url'] . '&Signature=' . $sign['sign'];
    $data = json_decode(httpRequest($url));
    if (isset($data->Domains)) {
        return $data;
    } else {
        return false;
    }
}

/**
 *  获取域名解析记录
 */
function getDomainRecords($domain)
{
    $sign = getSign(['Action' => 'DescribeDomainRecords', 'DomainName' => $domain]);
    $url = 'https://alidns.aliyuncs.com/?' . $sign['url'] . '&Signature=' . $sign['sign'];
    $data = json_decode(httpRequest($url));
    if (isset($data->DomainRecords)) {
        return $data;
    } else {
        return false;
    }
}

/**
 *  获取子域名解析记录
 */
function getSubDomainRecords($domain)
{
    $sign = getSign(['Action' => 'DescribeSubDomainRecords', 'SubDomain' => $domain]);
    $url = 'https://alidns.aliyuncs.com/?' . $sign['url'] . '&Signature=' . $sign['sign'];
    $dns = json_decode(httpRequest($url));
    if (isset($dns->DomainRecords)) {
        return $dns;
    } else {
        return false;
    }
}

/**
 *  更新域名解析
 */
function updateSubDomainRecords($id, $name, $ip)
{
    $data = array(
        'Action' => 'UpdateDomainRecord',
        'RecordId' => $id,
        'RR' => $name,
        'Type' => 'A',
        'Value' => $ip,
    );
    $sign = getSign($data);
    $url = 'https://alidns.aliyuncs.com/?' . $sign['url'] . '&Signature=' . $sign['sign'];
    return httpRequest($url);
}

/**
 *  发起 HTTP 请求
 */
function httpRequest($url)
{
    $curl = curl_init();
    curl_setopt($curl, CURLOPT_URL, $url);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
    $data = curl_exec($curl);
    $error = curl_error($curl);
    curl_close($curl);
    if ($error) {
        return $error;
    } else {
        return $data;
    }
}
发布于
归类为php

使用Google API获取谷歌相册的内容

首先要申请一个谷歌 Oauth2 应用的 ID 和密钥,然后用浏览器访问下面的地址获取授权码 code

https://accounts.google.com/o/oauth2/v2/auth?redirect_uri=http://localhost&response_type=code&client_id={ClientID}&scope=https://www.googleapis.com/auth/photoslibrary+https://www.googleapis.com/auth/drive&access_type=offline

获取到 code 以后,请求谷歌服务器换取 refresh_token

define('Client_ID', 'client_id');
define('Client_Secrec', 'client_secrec');

function getRefreshToken($code) {
    $params = ['grant_type' => 'authorization_code', 'code' => $code, 'client_id' => Client_ID, 'client_secret' => Client_Secrec, 'redirect_uri' => 'http://localhost'];
    $curl = curl_init();
    curl_setopt($curl, CURLOPT_URL, 'https://www.googleapis.com/oauth2/v4/token');
    curl_setopt($curl, CURLOPT_HTTPHEADER, ['Content-Type: application/x-www-form-urlencoded']);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
    curl_setopt($curl, CURLOPT_POST, true);
    curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($params));
    $data = curl_exec($curl);
    curl_close($curl);
    return $data;
}

用 refresh_token 获取 access_token

function getAccessToken($refresh_token) {
    $params = ['grant_type' => 'refresh_token', 'client_id' => 'client_id', 'client_secret' => 'client_secre', 'refresh_token' => $refresh_token];
    $curl = curl_init();
    curl_setopt($curl, CURLOPT_URL, 'https://www.googleapis.com/oauth2/v4/token');
    curl_setopt($curl, CURLOPT_HTTPHEADER, ['Content-Type: application/x-www-form-urlencoded']);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
    curl_setopt($curl, CURLOPT_POST, true);
    curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($params));
    $data = curl_exec($curl);
    curl_close($curl);
    return $data;
}
发布于
归类为php