普通视图

发现新文章,点击刷新页面。
昨天以前首页

CloudFlare CDNJS:免费加速你的前端资源

2025年4月28日 14:18

我们前面介绍了 CloudFlare 的 R2 服务:Cloudflare R2 对象存储白嫖指南:10G存储+免流量费,打造免费图床,其实 CloudFlare 还有另外一个我们使用比较多的服务,就是 CDNJS 服务。

什么是 CDNJS?

CDNJS 是 Cloudflare 维护的一个免费开源 CDN 服务,专门托管热门的前端库(如 jQuery、React、Vue、Font Awesome 等),开发者只需通过简单的链接引用,就能让全球用户快速加载这些资源,无需自己部署服务器。

它有如下的核心优势:

✅ ​全球加速:依托 Cloudflare 的全球 CDN 网络,资源加载更快。
✅ ​自动同步:与 npm/GitHub 同步,确保使用最新版本。
​完全免费:零成本使用,无需担心带宽费用。
✅ ​广泛兼容:支持传统 JS、ES Modules、WASM 等多种格式。

CDNJS 的发展历程

CDNJS 最初是由 ​Thomas Davis​(前端开发者,当时就职于 Twitter)于 ​2011 年 创建,并联合开源社区共同维护,初期只是托管几十个热门库。

当初发起这个库的最大原因就是为了解决开发者直接托管第三方库(如 jQuery)的痛点:

  • 带宽成本高:小团队或个人开发者自建 CDN 成本高昂。
  • 更新延迟:手动下载和部署库版本效率低下。
  • 全球化需求:当时已有的 CDN(如 Google Hosted Libraries)覆盖节点有限。

2014 年接手 CDNJS 的服务器和网络基础设施,并提供了企业级基础设施和全球 CDN 节点支持。

2016 年引入自动化 npm/GitHub 同步,大幅提升效率。

2020 年开始支持现代 Web 技术(如 WASM、ES Modules)。

为什么需要 CDNJS?

自托管 vs. CDNJS

对比项自托管CDNJS
加载速度依赖自身服务器带宽全球 CDN 加速
维护成本需手动更新版本自动同步最新版
缓存命中率仅限自身用户全球共享缓存,命中率更高
可用性服务器宕机=资源不可用高可用性,99.99% SLA

适用场景

  • 个人博客、小型网站(节省带宽成本)
  • 企业级应用(提升全球访问速度)
  • WordPress 等 CMS(优化前端性能)

如何使用 CDNJS

在 HTML 中直接引用 CDNJS 链接即可:

<!-- 加载 jQuery -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>

<!-- 加载 Font Awesome CSS -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" />

如要增强安全性,可以使用 ​SRI(Subresource Integrity)​ 防止资源被篡改:

<script 
  src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"
  integrity="sha256-kmHvs0M+1Qz9wuZOJ8E6OE2bM4S4n2GeXJ6X5+5ow="
  crossorigin="anonymous"></script>

由于 CloudFlare 在国内没有加速节点,国内访问可能会有些慢,所以推荐一些国内的 cdnjs 的镜像服务:

服务商特点延迟(国内/国外)
CloudFlare
https://cdnjs.cloudflare.com
✅ 全球加速
⚠️ 国内速度较慢
528 ms / 4 ms
又拍云 CDN
https://s4.zstatic.net
✅ 镜像自 Cloudflare CDNJS
✅ 库齐全,国内速度快
30 ms / 8 ms
字节跳动 CDN
https://cdn.bytedance.com
✅ 速度快企业级稳定性
⚠️ 但库较少,仅 JS 无 CSS 库
27 ms / 216 ms
360 前端静态资源库
https://lib.baomitu.com
⚠️ 更新不太及时
⚠️ 库较少
13 ms / 3 ms
SM.MS
https://cdnjs.loli.net/ajax/libs
✅ 镜像自 Cloudflare CDNJS
⚠️ 个人开发者
160 ms / 8 ms
7ED
https://use.sevencdn.com
✅ 镜像自 Cloudflare CDNJS
⚠️ 个人开发者
33 ms / 180 ms

如果你使用的是 WordPress 博客,那么 #WPJAM Basic# 插件也提供了一个前端公共库,让你一键切换,在「WPJAM」-「优化设置」-「增强优化」:

目前已经内置了下面这几家:

cdnjs.cloudflare.com
s4.zstatic.net
cdnjs.snrat.com
lib.baomitu.com
cdnjs.loli.net
use.sevencdn.com

根据上面的推荐,国内最佳可能是 s4.zstatic.net,具体还是需要你自己切换和体验。

CDNJS 总结

CDNJS 是前端开发者的利器,能显著提升资源加载速度并降低维护成本。

  • 全球用户 → 直接使用 cdnjs.cloudflare.com
  • 国内优化 → 切换至 s4.zstatic.net
  • WordPress → 通过 WPJAM Basic 插件一键配置

PHP 8 重大变更:is_callable() 不再支持类名+非静态方法检查,强制返回 false!

2025年4月18日 00:10

最近在 PHP 8 环境下,发现 #WPJAM Basic# 的一个严重问题:就是后台文章和分类列表的一些操作无效了,点击保存按钮没有任何反应。经过深入调试,发现问题根源在于 PHP 8 对is_callable()函数的行为进行了重大调整。

技术细节

在 PHP 8 之前,is_callable() 函数在检查类名与非静态方法的组合时会返回 true,即使这种调用方式在实际执行时可能会导致问题。PHP 8 对此进行了更严格的检查,以提前发现潜在的错误调用方式,因此 is_callable() 在检查一个类名与非静态方法时将返回失败(应当检查一个类的实例)。

具体看下面这个例子就大概能够明白了:

class Test{
    public function method1() { }
    public static function method2() { }
}

// PHP 8 之前
var_dump(is_callable(['Test', 'method1']));	// bool(true)
var_dump(is_callable(['Test', 'method2']));	// bool(true)

// PHP 8 之后
var_dump(is_callable(['Test', 'method1']));	// bool(false)
var_dump(is_callable(['Test', 'method2']));	// bool(true)
var_dump(is_callable([new Test, 'method1']));	// bool(true)

变更原因

PHP 8 的这一变更是为了更严格地执行面向对象编程规范:

  1. 静态与非静态方法调用的区分:非静态方法需要类的实例才能调用,而静态方法可以直接通过类名调用。
  2. 提前错误检测:在 PHP 7 及以下版本,虽然 is_callable() 会返回 true,但实际以静态方式调用非静态方法时会产生警告。PHP 8 通过 is_callable() 提前返回 false 来更早发现问题。
  3. 代码质量提升:强制开发者明确方法调用的上下文,避免模糊的调用方式。

解决方案

既然知道这个原因,那就是代码要根据上下文来写了。所以如果要检查某个对象的非静态方法是否可调用,应该使用一个对象实例而不是类名。

此外,其实很多时候我们只需要检查类的方法是否存在,而不关心其可调用性(例如访问控制),那么可以简单使用 method_exists() 进行判断就好了:

if (method_exists('Test', 'method1')) {
    // Do something
}

原来 explode 还有第三个参数,竟然这么好用!

2025年4月2日 18:25

explode 是 PHP 一个常见的字符串处理函数,主要用于将字符串按照指定的分隔符拆分成数组。比如下面的代码就是通过英文逗号将字符串转换为数组。

$str	= "apple,banana,orange";
$fruits	= explode(",", $str);

// 结果:
// Array
// (
//     [0] => apple
//     [1] => banana
//     [2] => orange
// )

我相信大部分人(主要是我)都是这么用这个函数,但是绝大部分都不知道,explode 这个 PHP 函数还有第三个参数:$limit,这是一个可选参数,限制返回数组的最大元素数量。

我标题里面说这么好用,好用在哪里呢?🧐

🙋‍♀️🌰 我有个字符串,比如 args=type=select&name=gravatar,这个字符串其实是两部分,第一部分是 args 是 key,第二部分(type=select&name=gravatar),它其实是个 query_string,我的目的到时候通过 WordPress 函数 wp_parse_args 函数是将其解析成数组的。

如果直接使用 explode 分割字符串拆成数组的话:

$str	= "args=type=select&name=gravatar";
$arr	= explode("=", $str);

// 结果:
// Array
// (
//     [0] => args
//     [1] => type
//     [2] => select&name
//     [3] => gravatar
// )

这是我又要使用 implode 函数将除了第一个元素之外,其他元素拼成字符串。

有点烦躁。😒

这是第三个参数就派上用场了,设置为 2,看看结果

$str	= "args=type=select&name=gravatar";
$arr	= explode("=", $str, 2);

// 结果:
// Array
// (
//     [0] => args
//     [1] => type=select&name=gravatar
// )

是不是就是我需要的结果!🥳

最后我们在看看第三个参数 $limit 的用法:

  1. 如果设置了 limit 参数并且是正数,则返回的数组包含最多 limit 个元素,而最后那个元素将包含 str 的剩余部分
  2. 如果 limit 参数是负数,则返回除了最后的 -limit 个元素外的所有元素。
  3. 如果 limit 是 0,则会被当做 1。

好了,以后不要傻傻自己使用 implode 函数拼贴回去了,记得 explode  还有第三个参数的,而且非常好用。

前端真麻烦,概念真多,今天碰到了函数节流(Throttle)和函数防抖(Debounce)

2025年3月28日 15:06

前面我在 WordPress 后台集成了标签选择器(tag-input)和多选选择器(mu-select),其中「Tag-input」有个功能是:按退格键(Backspace)不仅可以删除输入的文字,也可以删除前面的标签(为了防止误删,需要快速按两次)。

「为了防止误删,需要快速按两次」这个是怎么实现的,我搜索了一圈和问了 DeepSeek,原来有个牛逼的技术叫做 Debounce,中文翻译成函数防抖,和它一起出现的还有个概念叫做 Throttle,中文翻译是函数节流。

于是各种搜索和 DeepSeek 问,把这两个概念理解的七七七八八,简单整理一篇文章,防止以后快速能用上。先来 DeepSeek 最这个的一句话总结:

函数节流(Throttle)和函数防抖(Debounce)是控制函数执行频率的两种常用技术,它们的核心目标都是优化高频事件触发时的性能,但实现方式和适用场景有所不同。

函数节流(Throttle)​

函数节流的核心是固定时间间隔内只执行一次函数,即使事件被频繁触发,函数也会按照设定的时间间隔规律性执行。

实现原理

  • 记录上一次执行的时间戳,每次触发时判断当前时间与上次执行的时间差。
  • 如果时间差大于等于设定的间隔,则执行函数并更新上次执行时间;否则忽略本次触发。

应用场景

  • 高频事件需定期响应:如滚动(scroll)、窗口调整(resize)、鼠标移动(mousemove)。
  • 防止重复操作:如游戏中的射击按钮(限制点击频率)、表单提交按钮(避免重复提交)。

代码示例

这里仅做演示,一般 LodashUnderScore 已经带了 throttle 函数。

function throttle(func, wait){
	let lastTime	= 0;
	return function(...args){
		const now	= Date.now();
		if(now - lastTime >= wait){
			func.apply(this, args);
			lastTime	= now;
		}
	};
}

函数防抖(Debounce)​

函数防抖的核心是等待事件停止触发后,延迟执行函数。如果事件在等待期间被再次触发,则重新计时。

实现原理

  • 每次触发时清除之前的定时器,并重新设置一个定时器。
  • 只有在事件停止触发超过设定时间后,函数才会执行。

应用场景

  • 输入结束后执行:如搜索框联想建议(用户停止输入后发送请求)。
  • 避免重复触发:如窗口调整结束后的布局计算(resize结束)、表单提交按钮防抖(防止多次提交)。

代码示例

这里仅做演示,一般 LodashUnderScore 已经带了 debounce 函数。

function debounce(func, wait, immediate=false){
	let timeout;
	return function(...args){
		const context	= this;
		const later		= ()=> {
			timeout	= null;
			if(!immediate){
				func.apply(context, args);
			}
		};
		const callNow	= immediate && !timeout;
		clearTimeout(timeout);
		timeout	= setTimeout(later, wait);
		if(callNow){
			func.apply(context, args);
		}
	};
}

核心区别

特性节流(Throttle)​防抖(Debounce)​
执行时机固定间隔执行一次事件停止触发后延迟执行
适用场景需定期响应的连续事件(如滚动、拖拽)只需最终结果的场景(如输入验证、搜索)
高频触发结果规律性执行(如每 100ms 一次)仅执行最后一次触发

如何选择?

  • 需要即时反馈:用节流(如拖拽、滚动时更新元素位置),结合 leadingtrailing 选项
  • 只需最终结果:用防抖(如搜索建议、输入验证),结合 immediate 选项。

通过合理使用这两种技术,可以有效优化性能并提升用户体验。

从 Division by zero 到 set_error_handler

2025年3月20日 17:16

最近做了个简单的表格应用,其中有个功能是支持公式运算,如下图,毛利率这列是其他两列相除算出来的:

Division by zero

做完,客户还没用一天,就碰到问题了,因为有时候公式中的被除数是 0,这时候 log 中一堆 「Division by zero」的 Warning,影响查看其他的 log 的查看。

刚开始我想到的办法是在填充公式变量的时候,判断一下被除数是零就抛出「除零错误」,不去真正计算就好了,确实可以解决问题。嘻嘻,我还是挺聪明的。😎

但是很快又碰到新的问题了,因为有这样的公式:$a / ($b + $c) ,然后 ($b + $c) 的结果为 0。😂

如果继续按照原来的做法,我要根据运算的优先级,来判断被除数是 0,如果公式一复杂,那么什么时候是个头啊。🤦🤦‍♂️🤦‍♀️🤕

Warning 还是异常

先搜索了一圈 Division by zero,然后在 PHP 文档,看到 PHP 就已经有一个预定义的异常 DivisionByZeroError

但是为啥我的代码能够执行成功,只是在 log 中出现一堆 「Division by zero」的 Warning,那就继续看文档:

在 PHP 7 中使用算术运算符 / 不会抛出异常,而在 PHP 8 中会抛出异常,可惜客户的系统还在使用 PHP 7.4。

直接问 DeepSeek 我该怎么办?它给了我四个方法:

1. 手动检查并抛出异常

我这个又不是简单的被除数,真的是。🙄

2. 使用 set_error_handler 捕获警告

貌似这个可以。😘

3. 封装除法操作类

我是算术公式,这个不合适。🤪

4. 升级 PHP 8

我能升级就不会问你了。🤬

看来使用方案二了!

使用 set_error_handler 捕获警告

就是通过 set_error_handler 函数将警告转换成异常抛出。

set_error_handler(function($no, $str){
	if(str_contains($str , 'Division by zero')){
		throw new DivisionByZeroError($str); 
	}

	throw new ErrorException($str , $no);

	return true;
});

如果警告信息的字符串里面有 Division by zero,就抛出 DivisionByZeroError 异常,其他情况抛出 ErrorException

然后原来的执行表达式运算的代码改成 try - catch 结构:

try{
	// 原来执行运算表达式代码
}catch(DivisionByZeroError $e){
	return $if_error ?? '!除零错误';
}catch(throwable $e){
	return $if_error ?? '!计算错误';
}

完美解决!🍾🥂🎆🎇🎊

还没开心 5 分钟,很快就来了但是。😞😮‍💨

如果这样自定义错误处理程序,那么之后的代码的警告都被他接管了,然后直接抛出异常。这 🙄🫤

继续查 PHP 文档,问 DeepSeek,原来 set_error_handler 有返回值的,如果之前有定义的错误处理程序,则返回之前定义的错误处理程序,如果没有定义或者是内置的错误处理程序,则返回 null。

这样的话,那就好处理,给 try - catch 结构加上 finally,最终的代码如下:

$handler	= set_error_handler(function($no, $str){
	if(str_contains($str , 'Division by zero')){
		throw new DivisionByZeroError($str); 
	}

	throw new ErrorException($str , $no);

	return true;
});

try{
	// 原来执行运算表达式代码
}catch(DivisionByZeroError $e){
	return $if_error ?? '!除零错误';
}catch(throwable $e){
	return $if_error ?? '!计算错误';
}finally{
	if($handler){ // 之前有定义的错误处理程序
		set_error_handler($handler);
	}else{ // 恢复到内置的错误处理程序
		restore_error_handler();
	}
}

搞定,收工,记录一下!📝 🐂🍺

微慕小程序专业版V5.6版更新发布

2025年2月26日 13:56

此次更新需更新后端插件和前端小程序,插件更新后需要禁用再启用一次。
1、完善首页导航的样式。
2、优化微信小店首页(需安装微信小店插件:https://www.minapper.com/shops
3、用户信息里加入绑定微信小店功能(需要在微信小店里添加合作小程序)
4、对于绑定了微信小店的用户,在用户主页显示该用户的微信小店商品。
5、在发布文章和话题页面,增加添加微信小店商品(需要绑定微信小店,需安装微信小店插件)、链接、小程序短链的功能。
6、优化小店订单功能(需安装微信小店插件)。
7、调整发布文章的权限功能:文章的发布可以指定会员等级和免审的等级。
8、加入合作小店和合作小店商品功能。(需安装微信小店插件)
9、文章详情底部操作栏优化重构。
10、完善服务号模板消息。

今日除夕

2025年1月28日 15:23

1、今日除夕,在乡下过年,艳阳高照,合理合法躺平的日子,心安理得享受不内卷的闲暇时光。
2、无论朋友圈,还是科技界,DeepSeek AI 都成了热门话题。我用过几个月的DeepSeek 开放平台的api ,对它的理解不深刻,甚至很肤浅。DeepSeek最近一下子就大火了,按流行的说法就是用500万美元实现了10亿美元的效果。看新闻,就在今天DeepSeek 推出了janus pro 多模态大模型,家里有高配电脑的可以玩起来了。在新闻通稿里,在分析文章里,对DeepSeek不吝溢美之词,堆积着各种我这种低端三流程序员看不懂的AI 术语。看了一上午有关DeepSeek AI 文章,我既高兴,又寂寞。高兴的是终于有咱国产的AI扬眉吐气了,很长脸。寂寞的是,这么高大上的高科技,离我这样三流程序员,还真有点远,我已严重落后了。

3、当我越来越依赖AI的辅助去写程序了,身体上虽然轻松了不少,但内心却有些沉重,AI取代我这样三流程序员的时间点,已经快到了。我退休的年龄已推迟到了63岁,还有12年,被AI淘汰的我,能安心写代码吗?

4、为了排解内心的寂寞,我忍住不打开开发工具,写一会儿小程序的代码,心里想着,写一天算一天了,或许那天写的代码就用不上了。

5、既然你看到了这里,就谢谢你耐心读了以上文字。在这除旧迎新的日子,祝福你 :新春大吉,事事如意。

PHP 8.4 正式版发布,一文快速预览新功能

2024年11月23日 23:50

PHP 8.4 在 11 月 21 号就发布了,它包含了许多新功能,对于 WordPress 用户来说,了解 PHP 新版的功能也是一个简单必要的工作,并且  8.4 版是 PHP 在 2024 年的年度的一个主要的版本更新,所以我们就简单快速过一遍 PHP 8.4 的新功能。😄

属性挂钩

对象属性现在可以其 get 和 set 操作中关联相关的附加逻辑。根据用法,这可能会也可能不会使属性变为虚拟属性,即该属性根本没有实际的存储值。

<?php
class Person
{
    // “虚拟”属性,可能无法明确设置。
    public string $fullName {
        get => $this->firstName . ' ' . $this->lastName;
    }

    // 所有的写入操作都会经过这个挂钩,结果就是写入的内容。
    // 读取访问正常。
    public string $firstName {
        set => ucfirst(strtolower($value));
    }

    // 所有的写入操作都会经过这个挂钩,它必须写入支持值本身。
    // 读取访问正常。
    public string $lastName {
        set {
            if (strlen($value) < 2) {
                throw new \InvalidArgumentException('Too short');
            }
            $this->lastName = $value;
        }
    }
}

$p = new Person();

$p->firstName = 'peter';
print $p->firstName; // 打印“Peter”
$p->lastName = 'Peterson';
print $p->fullName; // 打印“Peter Peterson”

不对称属性可见性

现在可以将对象属性的 set 可见性和 get 可见性分开控制。

<?php
class Example
{
    // 第一个可见性修饰符控制 get 可见性,第二个修饰符控制 set 可见性。
    // The get-visibility must not be narrower than set-visibility.
    public protected(set) string $name;

    public function __construct(string $name)
    {
        $this->name = $name;
    }
}

惰性对象

现在可以创建对象,将初始化延迟到访问时。库和框架可以利用这些惰性对象来延迟获取初始化所需的数据或依赖项。

<?php
class Example
{
    public function __construct(private int $data)
    {
    }

    // ...
}

$initializer = static function (Example $ghost): void {
    // 获取数据或者依赖项
    $data = ...;
    // 初始化
    $ghost->__construct($data);
};

$reflector = new ReflectionClass(Example::class);
$object = $reflector->newLazyGhost($initializer);

#[\Deprecated] 属性

新的 #[\Deprecated] 属性使 PHP 的现有弃用机制可用于用户定义的函数、方法和类常量。

class PhpVersion
{
    #[\Deprecated(
        message: "use PhpVersion::getVersion() instead",
        since: "8.4",
    )]
    public function getPhpVersion(): string
    {
        return $this->getVersion();
    }

    public function getVersion(): string
    {
        return '8.4';
    }
}

$phpVersion = new PhpVersion();
// Deprecated: Method PhpVersion::getPhpVersion() is deprecated since 8.4, use PhpVersion::getVersion() instead
echo $phpVersion->getPhpVersion();

新的 ext-dom 功能和 HTML5 支持

新的 DOM API 包括符合标准的支持,用于解析 HTML5 文档,修复了 DOM 功能行为中的几个长期存在的规范性错误,并添加了几个函数,使处理文档更加方便。

新的 DOM API 可以在 Dom 命名空间中使用。使用新的 DOM API 可以使用 Dom\HTMLDocument 和 Dom\XMLDocument 类创建文档。

$dom = Dom\HTMLDocument::createFromString(
    <<<HTML
        <main>
            <article>PHP 8.4 is a feature-rich release!</article>
            <article class="featured">PHP 8.4 adds new DOM classes that are spec-compliant, keeping the old ones for compatibility.</article>
        </main>
        HTML,
    LIBXML_NOERROR,
);

$node = $dom->querySelector('main > article:last-child');
var_dump($node->classList->contains("featured")); // bool(true)

BCMath 的对象 API 

新的 BcMath\Number 对象使在处理任意精度数字时可以使用面向对象的方式和标准的数学运算符。

这些对象是不可变的,并实现了 Stringable 接口,因此可以在字符串上下文中使用,如 echo $num

use BcMath\Number;

$num1 = new Number('0.12345');
$num2 = new Number('2');
$result = $num1 + $num2;

echo $result; // '2.12345'
var_dump($num1 > $num2); // false

新的 array_*() 函数

新增函数 array_find()array_find_key()array_any() 和 array_all()

$animal = array_find(
    ['dog', 'cat', 'cow', 'duck', 'goose'],
    static fn (string $value): bool => str_starts_with($value, 'c'),
);

var_dump($animal); // string(3) "cat"

PDO 驱动程序特定子类

新的 Pdo\DblibPdo\FirebirdPdo\MySqlPdo\OdbcPdo\Pgsql 和 Pdo\Sqlite 的子类可用。

$connection = PDO::connect(
    'sqlite:foo.db',
    $username,
    $password,
); // object(Pdo\Sqlite)

$connection->createFunction(
    'prepend_php',
    static fn ($string) => "PHP {$string}",
); // Does not exist on a mismatching driver.

$connection->query('SELECT prepend_php(version) FROM php');

new MyClass()->method() 不需要括号

现在可以在不使用括号包裹 new 表达式的情况下访问新实例化对象的属性和方法。

$connection = PDO::connect(
    'sqlite:foo.db',
    $username,
    $password,
); // object(Pdo\Sqlite)

$connection->createFunction(
    'prepend_php',
    static fn ($string) => "PHP {$string}",
); // Does not exist on a mismatching driver.

$connection->query('SELECT prepend_php(version) FROM php');

新的类、接口和函数

新的 延迟对象。
基于 IR 框架的新 JIT 实现。
新增 request_parse_body() 函数。
新增 bcceil()、bcdivmod()、bcfloor() 和 bcround() 函数。
新增 RoundingMode 枚举用于 round(),包括 4 个新的舍入模式 TowardsZero、AwayFromZero、NegativeInfinity 和 PositiveInfinity。
新增 DateTime::createFromTimestamp()、DateTime::getMicrosecond()、DateTime::setMicrosecond()、DateTimeImmutable::createFromTimestamp()、DateTimeImmutable::getMicrosecond() 和 DateTimeImmutable::setMicrosecond() 方法。
新增 mb_trim()、mb_ltrim()、mb_rtrim()、mb_ucfirst() 和 mb_lcfirst() 函数。
新增 pcntl_getcpu()、pcntl_getcpuaffinity()、pcntl_getqos_class()、pcntl_setns() 和 pcntl_waitid() 函数。
新增 ReflectionClassConstant::isDeprecated()、ReflectionGenerator::isClosed() 和 ReflectionProperty::isDynamic() 方法。
新增 http_get_last_response_headers()、http_clear_last_response_headers() 和 fpow() 函数。
新增 XMLReader::fromStream()、XMLReader::fromUri()、XMLReader::fromString()、XMLWriter::toStream()、XMLWriter::toUri() 和 XMLWriter::toMemory() 方法。
新增 grapheme_str_split() 函数。

弃用和向后不兼容

IMAP、OCI8、PDO_OCI 和 pspell 扩展已从 PHP 中分离并移至 PECL。
隐式可空参数类型现已弃用。
使用 _ 作为类名现已弃用。
将零的负数次幂现已弃用。
向 round() 传递无效模式将抛出 ValueError。
来自扩展 date、intl、pdo、reflection、spl、sqlite、xmlreader 的类常量现在是有类型的。
GMP 类现已是 final 类。
已删除 MYSQLI_SET_CHARSET_DIR、MYSQLI_STMT_ATTR_PREFETCH_ROWS、MYSQLI_CURSOR_TYPE_FOR_UPDATE、MYSQLI_CURSOR_TYPE_SCROLLABLE 和 MYSQLI_TYPE_INTERVAL 常量。
已弃用 mysqli_ping()、mysqli_kill()、mysqli_refresh() 函数,mysqli::ping()、mysqli::kill()、mysqli::refresh() 方法,以及 MYSQLI_REFRESH_* 常量。
stream_bucket_make_writeable() 和 stream_bucket_new() 现在返回 StreamBucket 实例而不是 stdClass。
exit() 行为变更。
E_STRICT 常量已弃用。

所以你打算升级吗?我打算先把 WPJAM 所有插件的最低版本的要求从 7.4 先升级到 8.0 了,不过现在我已经开始修复一些插件在 PHP 8 版本的兼容问题了。

微慕扫码登录的新功能:网站应用微信登录

2024年11月19日 11:12

最近,我们团队最近对微慕登录插件做了一点完善,加入了“网站应用微信登录”的方式,在pc端除了支持扫码登录外,同时还支持快捷登录,当网站应用发起微信登录请求时,如果用户此时已经登录了微信 3.9.11 for Windows及以上版本客户端,且处于非锁定状态,会优先提示用户使用当前微信客户端已登录的账号进行快速登录。快速登录无需扫码,可直接在Windows设备上进行确认。

用户仍可切换其他微信账号或二维码登录。

注意:扫码登录方式,只能通过微信扫码的方式,不支持在手机微信端长按登录。

测试链接 : https://blog.minapper.com/wp-login.php?type=webapp

有关微慕登录插件功能详见文章:微慕登录插件

插件的下载地址

https://www.minapper.com/shops/

微慕小程序接入微信小店

2024年11月11日 13:14

2024年8月25日开始,视频号小店全面升级为“微信小店”,升级后的微信小店支持店铺及商品信息在公众号(订阅号、服务号)、视频号(直播、短视频)、小程序、搜一搜等多个微信场景内流转,助力商家更好地满足用户消费需求。如在9月25号后仍未升级,视频号小店新增商品上架功能将会受到影响。

自2024年10月21日起,正式支持商家将微信小商店升级成微信小店。未及时升级的小商店,其相关能力将会受到影响,具体时间安排如下:

起始时间 实物商品 电子兑换券
2024年11月4日 停止新增上架 下架全部商品
2024年11月18日 下架全部商品 /

升级中为企业主体的小商店提供了便捷通道,商家可沿用此前的资质将小商店快速升级至微信小店,具体操作请关注本公告后续更新的指引链接。个人主体的小商店,暂不支持直接升级。

微慕小程序此前是对接小程序基础交易组件(小商店组件版),虽然现在小程序交易组件目前还没有要求升级,不过随着独立版小商店被要求升级到微信小店,因此很可能小程序交易组件最后也需要升级,或者被取消。那么对于小程序来说,接入微信小店就是一个新的选择,而微信小店也提供了接入小程序的组件和api。这样无论对于小程序交易组件还是微信小商店用户来说,转到新的微信小店,就是一个很好的延续。

为了应对这个变化和调整,微慕团队开发了微信小店扩展,用于在微慕小程序里对接微信小店。微信小店扩展主要功能如下:

微信小店主页

这个主页主要是替换和取代原来的微信小商店的主页,微信小店主页目前包括精选展位,商品列表,商品分类等内容,如下图所示:

目前微信小店接入小程序时,跳转商品详情只能通过小程序商品组件,希望后续官方可以提供api的跳转方式,这样就可以定义商品和列表的样式,目前的官方的组件样式还比较单一和简陋。

小店主页同时h5页面的方式,供pc端和移动端浏览,如果是微信打开h5小店主页,可以直接打开微信小店购买商品,如下图所示:

微信小店H5端链接 : https://www.watch-life.net/wechatshoppage/1

在小程序页面精选商品

目前,在微慕小程序的首页和文章详情页,都可以接入精选的商品,如下图所示:

在文章详情里插入商品和店铺主页

此功能可以在文章内部的任意位置插入商品和店铺的主页,如下图所示:

查看微信小店订单

用户可以在小程序里查询到在微信小店里的订单,如下图所示:

微信小店扩展后端管理

微信小店的管理者,可以通过微信小店插件后端功能管理小店的基本设置,订单,商品等。如下图所示:

在文章里加入微信小店的主页和商品

2024年11月8日 16:29

2024年8月25日,视频号小店全面升级为微信小店,微信小店将进一步简化商家入驻流程,升级品牌认证和店铺命名体系,降低入驻门槛及保证金,并支持店铺及商品信息在公众号(订阅号、服务号)、视频号(直播、短视频)、小程序、搜一搜等多个微信场景内流转,助力商家更好地满足用户消费需求。

具体详见公告:https://support.weixin.qq.com/cgi-bin/mmsupportacctnodeweb-bin/pages/BuacYUG4Fu9v8tdY

微信小程序基础库 从3.5.5 版本开始,支持小程序内嵌微信小店首页和商品的组件。

1、小程序内嵌微信小店首页
https://developers.weixin.qq.com/miniprogram/dev/component/store-home.html

2、小程序内嵌微信小店商品,展示小店商品,并进行跳转交易。
https://developers.weixin.qq.com/miniprogram/dev/component/store-product.html

3、版微信小店”接入说明、指引及小程序相关开发接入指南
https://developers.weixin.qq.com/community/develop/article/doc/000248620887c0228302bad9463413

使用这两个小程序组件效果如下:

Kutt 开源的网址缩短程序

2023年10月30日 00:00

Kutt 是一个开源的短网址程序,有了它可以在分享一些很长的链接的时候,进行缩短链接,使得分享 URL 更简洁。支持容器部署,虽是英文界面,但操作很简单,感兴趣的小伙伴可以试一下~

支持特性

  1. 开源免费;

  2. 自定义域支持;

  3. 缩短链接自定义 URL;

  4. 设置链接密码;

  5. 设置链接描述;

  6. 链接过期时间;

  7. 查看、编辑、删除、管理链接;

  8. 管理员帐户可查看、删除、禁止链接。

程序部署

如果常规安装的话需要安装 Node.js/PostgreSQL/Redis 等,所以用 Docker 来安装这个是最简单的。将下方的代码保存为.env

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# App port to run on
PORT=3000

# The name of the site where Kutt is hosted
SITE_NAME=Kutt

# The domain that this website is on
DEFAULT_DOMAIN=localhost:3000

# Generated link length
LINK_LENGTH=6

# Postgres database credential details
DB_HOST=postgres
DB_PORT=5432
DB_NAME=postgres
DB_USER=
DB_PASSWORD=
DB_SSL=false

# Redis host and port
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_DB=

# Disable registration
DISALLOW_REGISTRATION=false

# Disable anonymous link creation
DISALLOW_ANONYMOUS_LINKS=false

# The daily limit for each user
USER_LIMIT_PER_DAY=50

# Create a cooldown for non-logged in users in minutes
# Set 0 to disable
NON_USER_COOLDOWN=0

# Max number of visits for each link to have detailed stats
DEFAULT_MAX_STATS_PER_LINK=5000

# Use HTTPS for links with custom domain
CUSTOM_DOMAIN_USE_HTTPS=false

# A passphrase to encrypt JWT. Use a long and secure key.
JWT_SECRET=securekey

# Admin emails so they can access admin actions on settings page
# Comma seperated
ADMIN_EMAILS=

# Invisible reCaptcha secret key
# Create one in https://www.google.com/recaptcha/intro/
RECAPTCHA_SITE_KEY=
RECAPTCHA_SECRET_KEY=

# Google Cloud API to prevent from users from submitting malware URLs.
# Get it from https://developers.google.com/safe-browsing/v4/get-started
GOOGLE_SAFE_BROWSING_KEY=

# Your email host details to use to send verification emails.
# More info on http://nodemailer.com/
# Mail from example "Kutt <support@kutt.it>". Leave empty to use MAIL_USER
MAIL_HOST=
MAIL_PORT=
MAIL_SECURE=true
MAIL_USER=
MAIL_FROM=
MAIL_PASSWORD=

# The email address that will receive submitted reports.
REPORT_EMAIL=

# Support email to show on the app
CONTACT_EMAIL=

在同级目录下,将下方的代码保存为 docker-compose.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
version: "3"

services:
kutt:
image: kutt/kutt
depends_on:
- postgres
- redis
command: ["./wait-for-it.sh", "postgres:5432", "--", "npm", "start"]
ports:
- "3000:3000"
env_file:
- .env
environment:
DB_HOST: postgres
DB_NAME: kutt
DB_USER: user
DB_PASSWORD: pass
REDIS_HOST: redis

redis:
image: redis:6.0-alpine
volumes:
- redis_data:/data

postgres:
image: postgres:12-alpine
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: kutt
volumes:
- postgres_data:/var/lib/postgresql/data

volumes:
redis_data:
postgres_data:

启用服务

修改.envDEFAULT_DOMAIN=localhost:3000 值,将 localhost 改为域名,不然访问时会跳转出错。并确保邮箱相关的配置正确:

1
2
3
4
5
6
MAIL_HOST=
MAIL_PORT=
MAIL_SECURE=true
MAIL_USER=
MAIL_FROM=
MAIL_PASSWORD=

然后通过下方命令启动服务:

1
docker-compose up -d

注意事项

Kutt 默认是没有管理员用户的,需要通过邮箱注册,所以需要.env 里邮箱相关的内容都配置正确。

注册账号时始终提示失败 an error occurred,虽然不影响短链接的生成及使用,但无法登录管理员后台还是很难受的。

注册失败实际是没有发邮件激活成功,通过在作者的 Issues 里发现了有很多人大量反馈此问题,应该是对接 SMTP 服务的 bug。

这种方式可通过登录 PostgreSQL 容器,查找到对应用户验证 Token,再访问 URL 激活即可。

微慕小程序专业版支持Donut多端框架

2024年4月15日 16:51

微慕团队近期发布了微慕专业版5.0版,加入了对微信官方的多端框架 Donut 的支持。Donut 多端框架是支持使用小程序技术和工具开发移动应用的框架,开发者可以一次编码,分别编译为小程序和 Android 以及 iOS 应用。此框架最重要的特性:支持使用原生语法开发的微信小程序和多端应用。 如果已经有开发好的小程序,引入Donut后,可以迅速而简单地构建生成App。

目前使用 Donut 多端框架与工具的开发、编译、调试、预览等基础版功能永久免费;云构建安装包需消耗较多资源,官方提供基础发布与更新所需的免费次数。

微慕专业版加入Donut架构后,通过较小的代码修改,已成功发布Adroid 和 iOS版本,并入选官方推荐案例

1、选择Donut的理由

一个重要的原因是这个是微信官方推出的多端框架,对微信小程序原生代码支持最完整,如果已经有了微信小程序产品,做少量的代码修改,就可以适配生成App,且微信小程序的大量API和sdk都可以兼容并应用到App里,目前这个api和sdk还在不断完善更新中,相信未来会有越来越多的小程序功能可以延展到App里,为App赋予更多的能力。
API兼容性总览参考文档:https://dev.weixin.qq.com/docs/framework/dev/jsapi/total.html

2、多端框架选型

多端的框架很多,微慕团队尝试过多端框架有finclip,uni-app, Donut

finclip是凡泰极客出品,2021年开始构架,已经形成比较完善的生态体系,从开发工具,到与微信小程序兼容框架,到应用市场,到云服务,技术和商务的支持,对小团队来说,比较友好。微慕团队在2022年时候,曾尝试在微慕开源版引入了finclip小程序框架,经过较小的改动,就生成了finclip版本的App,App的兼容性也比较好,此框架适合从微信小程序转App,转换的过程基本没有门槛。用此框架转换的微慕应用还获得了凡泰极客主办的首届FinClip Hackathon二等奖

uni-app是多端业态里时间最长最成熟的框架。这个框架需要采用vue的原生代码来生成小程序和app,如果原来是用微信原生代码的话,需要通过转换的工具把微信小程序原生代码转换成vue的代码,因此一个产品应用在刚刚创建之初,就考虑将来要支持多端的话,用vue代码从零开始构建一个uni-app应用,是非常合适的选择。针对微慕开源版,也搞一个版本进行适配,下载链接是:https://ext.dcloud.net.cn/plugin?id=2214

同时,微慕团队基于uni-app全新开发了微慕增强版,完全放弃了原生版本,真正做到一套代码生成多端。

3、关于App上架应用市场

通过微慕专业版App上架应用市场过程来看,个人觉得上架苹果App Store 应用商店要简单些,对相关资质要求低一些,主要涉及隐私或技术细节的问题,而上架国内市场,著作权、备案和各类资质,都要准备比较充分,过程长,比较折腾。关于上架应用市场常见问题,可以参考这个链接:https://dev.weixin.qq.com/docs/framework/faq/publish.html

访问微慕多端小程序和App可以扫描以下二维码

微信专业版 增强版微信 增强版百度 增强版字节 增强版QQ
微信专业版 微信增强版 百度增强版 字节增强版 QQ增强版
支付宝增强版 快手增强版 专业版APP(ADROID) 专业版APP(IOS) 增强版APP
支付宝增强版 快手增强版 专业版安卓app 专业版苹果app 增强版app

谢谢你的阅读,谢谢你对微慕小程序的支持。

微慕小程序开源版v4.6.9版本更新说明

2024年4月14日 14:53

时光荏苒,一晃微慕开源版发布就过去7年了。这些年来,微慕团队一直在持续完善和优化开源版,通过更新版本,适应微信小程序框架的调整,让开源版成为wordpress站长适应微信生态的一个有力工具。微慕开源版也将持续优化和完善。

这7年来,微信小程序业态也发生了翻天覆地的变化,微信也为小程序赋予了非常多的能力,成为微信里的一个超级应用。各大app都推出了各自的小程序,比如百度,支付宝,抖音,QQ等,不过从我个人的观察来看,微信小程序的是业态里最成熟,发展最好的,真正成为了微信生态圈里不可替代的应用体系。经过7年的发展,我依然相信,微信小程序在微信应用领域充满着生机和活力。

微慕小程序开源版目前最新的版本是V4.6.9版。最新更新的主要功能是:1、列表样式自定义。2、增加“发现”列表。

列表样式定义

在小程序里,列表是一种很常见也是应用最多的信息,对于不同的小程序类型对于列表的需求可能会不一样,微慕小程序以前的版本只支持“左图右文”的样式,不少站长希望可以用其他的方式显示列表,就只能进行修改代码。对于不了解小程序编码的站长来说,就没办法了。在支持“列表样式自定义”功能后,不但支持多种样式,还支持用户按自己的阅读习惯来自定义样式。

1、可以在config.js文件里定义默认的列表样式

2、对于小程序用户可以按自己的喜好来选择列表的样式

增加“发现”列表

以前微慕小程序里的列表主要是按时间顺序显示的,要看更多的内容就需要一页一页地翻,或者去搜索。为了让更多的文章在小程序里曝光出来,增加了一个“发现”的列表页,这个页面的文章主要是打乱顺序随机显示文章,或许某次不经意的打开后,你会看到让你感兴趣的老文章。

谢谢你的阅读,谢谢你对微慕小程序的支持。

利用PDF.js在微信小程序里预览PDF文件

2023年8月21日 18:15

在微信小程序可以通过wx.downloadFilewx.openDocument 两个api下载并打开pdf文件。这种方式主要有不少的缺点:

1、需要下载才可以查看,且每次打开都需要下载生成一个临时文件,如果PDF文件比较多的话,临时文件会越来越多,且如果PDF文件比较大的话,打开会比较慢。
2、在导航栏显示标题是临时文件名,看上去不够优雅。
3、翻页不方便。

那PDF能不能在小程序直接预览呢?我尝试用微信小程序的web-view里显示PDF的文件,在开发工具里可以显示,但在真机里无法显示。在微信开放社区看有人用PDF.js在浏览器里打开PDF文件,PDF.js 由 Mozilla 提供支持,目标是创建一个通用的、基于 Web 标准的平台,用于解析和呈现 PDF. 通过web-view方式打开通过PDF.js解析的PDF文件,在微信开发工具里无法正常显示,不过好消息是:在真机里可以显示正常。

使用PDF.js来解析PDF方法如下:

1、去PDF.js官方网站下载此框架:https://mozilla.github.io/pdf.js/getting_started

2、把PDF.js部署到网站,PDF.js有两个文件夹web和build,把这两个文件放到网站的一个目录下比如pdfljs目录,在web目录下有个viewer.html文件,可以用它来在线解析pdf文件,当然pdf文件的链接需要在同一个域名,预览的方式是:

https://wwww.domianname.com/pdfjs/web/viewer.html?file=xxx/xxx/xxx.pdf

微慕专业版已集成了PDF.js框架,支持通过pdf的链接在浏览器和小程序里预览PDF文件,在微慕专业里体验该功能的效果。

预览pdf文件:https://blog.minapper.com/wp-content/uploads/微慕小程序专业版.pdf

注意以上方式PDF文件的链接所在域名需要设置的小程序业务域名里。对于跨域的链接,虽然也支持,不过需要特别处理,具体详见链接:https://github.com/mozilla/pdf.js/wiki/Frequently-Asked-Questions#faq-xhr

下载打开pdf文件:https://www.watch-life.net/微慕小程序开源版.pdf

以上下载打开PDF文件的方式需要设置业务域名和downloadfile域名。

利用PDF.js在微信小程序里预览PDF文件,支持PDF.js的相关功能,比如:侧栏,查找,分页,缩放,添加文字,绘图,旋转,演示模式等。

上面是通过官方viewer.html来显示PDF文件,也可以通过引入PDF.js的方式来解析和显示,这个方式就可以自定义功能。方法如下:

1、引入pdf.js库

<script src="./build/pdf.js"></script>
<script src="./build//pdf.worker.js"></script>

2、用canvas接收需要读取到的pdf内容并显示

<canvas id="myCanvas"></canvas>

3、创建PDF对象:data可以是pdf文件对应的Base64字符串,也可以是文件所在相对或者绝对路径,也可以是一个在线文件url地址

var loadingTask = pdfjsLib.getDocument(data)
loadingTask.promise.then(function (pdf) {
                for (var i = 1; i 

有关PDF.js的更多信息,可以参考官方网站:https://mozilla.github.io/pdf.js/

The post 利用PDF.js在微信小程序里预览PDF文件 first appeared on 守望轩.

最近的一些事

2023年11月10日 21:18
很久以前买的张志春先生的《神奇之门》和《开悟之门》,因为和六爻梅花体系不同,所以一直搁置在案头吃灰,最近突然就特别想看看,这可好,一看就停不下来了,连三国志手游都不太愿意上了,充值的月卡白瞎了。不过看书终归是个好事情,就和锻炼一样,看了一段...
❌
❌