标签归档:开发经验

使用 Laravel Envoy 进行项目部署

在大型企业当中,往往会有各种各样的 CI & CD 工作流来完成项目的部署,而对于我自己的项目而言,我也希望能够有这样顺畅的交付体验,因此,我也为自己的业务加上了类似的方案,以实现顺滑的开发体验。

不过,我没有做的那么重,没有上 Ansible,也没有封装 Docker,就是标准的 LNMP 环境。只不过,我在标准的环境上加入了 Laravel Envoy — 一个快速部署工具

什么是 Laravel Envoy

Envoy 是 Laravel 团队开发的一个在远程服务器上执行某些命令的工具,因为他同样使用 Blade 语法,因此在 Laravel 项目中接入非常方便。

如何使用 Laravel Envoy

1. 安装 Laravel Envoy

Envoy 有多种使用方式,一种是跟随项目的使用方式,即使用 composer require laravel/envoy --dev 来安装,并使用 php vendor/bin/envoy 的方式来运作,这样的好处是你无需污染顶级目录,只在项目层面使用 Envoy 来进行部署。

不过我因为有很多项目,所以我会更加倾向于直接在全局安装 envoy,执行 composer global requre laravel/envoy 实现在全局安装 Enovy。全局安装完成后,当你需要执行命令,只需要执行 envoy run deploy,就能执行当前目录下所定义的命令。

除了全局安装,你也可以考虑使用 Bash alias 来实现类似的效果,将 envoy alias 给 php vendor/bin/envoy 就可以实现即使没有全局安装,也可以实现不需要手动输入前缀的特性。

2. 初始化配置文件

当你需要配置时,你要做的很简单,只需要执行 envoy init 服务器地址/昵称就可以获得一个 Envoy.blade.php 文件,你可以通过修改这个文件的内容,来实现定义不同的工作。同样的,你可以将这个文件纳入到版本控制系统当中,从而实现部署脚本的跟踪和定义,对于后续的持续维护比较有帮助。

因为 Envoy.blade.php 也是采用了 Blade 的后缀,所以也同样适用了 Blade 引擎,你可以通过一个语意更加友好的方式来定义你的脚本。

@servers(['web' => '127.0.0.1'])

@task('deploy')
    cd /path/to/site
    git pull origin master
@endtask

比如,上面的脚本就是一个最基础的脚本,首先定义了这个脚本的命令会在名为 web 的服务器上执行,而这个服务器的地址是 127.0.0.1;并定义了一个任务 deploy。

3. 执行部署命令

当你完成代码的编写,并提交了 Git 之后。执行命令 envoy run deploy ,就会自动的在 web 这个服务器上执行在 deploy 当中执行的命令。

deploy 的命令只要是正常的 bash 命令即可。

高级用法

1. 引入其他任务

因为 Envoy 使用 Blade 语法来编写,你也可以使用 Blade 语法中的命令来引入其他文件的中的任务。因此,你可以将自己常用的命令定义出来,并将其通过 Packagist 发布出去,并在实际应用过程中,引入对应的文件来使用。

@import('vendor/package/Envoy.blade.php')

2. 处理多个服务器

Envoy 支持定义多个服务器以及并行执行,对于架构相对复杂的业务,你可以定义不同的 servers ,并在对应的 task 当中来设定需要在哪些 Server 上执行。定义任务时,如果你传入了 parallel 参数,则可以控制命令同时在多个服务器上并行(该属性为 false 时则是串行(

@servers(['web-1' => '192.168.1.1', 'web-2' => '192.168.1.2'])
 
@task('deploy', ['on' => ['web-1', 'web-2'], 'parallel' => true])
    cd /home/user/example.com
    git pull origin {{ $branch }}
    php artisan migrate --force
@endtask

3. 定义变量或引入其他功能函数

你可以在 Envoy.blade.php 中加入 @setup 来完成变量的定义,并在后续的代码中使用。

@setup
    $now = new DateTime;
@endsetup

也可以直接使用 @include 来引入文件

@include('vendor/autoload.php')
 
@task('restart-queues')
    # ...
@endtask

4. 使用命令行参数

在启动时,如果你使用诸如 --param=value这样的参数来调用,则可以在你的代码中直接读取对应的变量,从而实现执行时带入不同的参数。

更多用法

Envoy 当中还有更多的用法,这里不再一一列举,如果你感兴趣,可以直接参考其官方文档,了解如何使用 Envoy 达成你的部署目标。

总结

在你不是很着急使用 Ansible 或 更重的部署工具的时候, Envoy 是一个不错的选择。特别是你使用 Laravel 进行开发时,Envoy 就是一个有效的工具。

man in black long sleeve shirt sitting on black chair

什么样的业务用什么样的架构

多年来,我一直是 PHP 的拥趸,不论中间我写过多少编程语言,但我始终觉得,PHP 能从 2000 年活到现在,是有其意义和价值的。

而到了现在,我终于可以回答这个价值和意义到底是什么 —— 什么样的业务,使用什么样的架构

作为技术创业者,我们最常遇见的问题,便是过度设计技术方案:我的项目是不是应该做个数据库主从备份?我的项目是不是应该做 Kuberentes 集群来处理弹性?

但实际上,对于绝大多数的业务而言,这些问题都是根本不重要的 —— 如果没有 Kubernetes,我们能否保证我们的业务的稳定性?如果没有主动备份,我们能否保障我们的业务可以平稳运转?

技术在绝大多数的业务中,都是一个成本中心,是一个保障业务正常运转的基石。他应该被重视,但不应该被过度重视。一个创业公司可能会因为业务不够稳定而失去用户,但绝不会因为业务非常稳定而留住用户。用户在乎的是你为用户解决了什么样的问题,而不是用了什么样的架构。

过度(提前)优化是万恶之源。

从头梳理,看看中国的 ICP 备案制度

对于生活在中国互联网环境中的开发者来说,ICP备案(后称「备案」)是一个很难绕过去的话题,但凡你需要正经的建设一个网站,那么备案就是一个必须要做的事情,不然可能会出现各种奇奇怪怪的问题。但,你是否思考过,为什么我们需要备案?你是否了解过备案二字背后的深层含义?

为什么我们需要备案?

备案的要求和历史,可以追溯到 2000 年 9 月 20 日国务院第 31 次常务会议通过的一个管理办法 —— 《互联网信息服务管理办法》(后称信息管理办法),由时任总理朱镕基先生签发。后续我们所进行的一切备案工作,其实都是这个信息管理办法的延展。

在《互联网信息服务管理办法》当中,第二条说明了什么样的情况才需要进行备案:在中国境内从事互联网信息服务活动。

第二条 在中华人民共和国境内从事互联网信息服务活动,必须遵守本办法。

互联网信息服务管理办法, 2000 年 9 月 20 日

这一条也解释了,为什么 Facebook、Google这些网站也可以正常运转,因为他们严格意义上来说,并不算在中国境内从事相关的信息服务活动(但也没保证你一定能够访问)。同样的,如果你自己的网站托管在海外的服务器上,一样不需要备案(但同样不保证你一定能访问,毕竟你不在境内提供服务,网络出现波动啥的非常正常)。

圈定了范围之后,第三条规定了不同的互联网信息服务都是什么样的类型,进行了定性操作。

第三条 互联网信息服务分为经营性和非经营性两类。经营性互联网信息服务,是指通过互联网向上网用户有偿提供信息或者网页制作等服务活动。非经营性互联网信息服务,是指通过互联网向上网用户无偿提供具有公开性、共享性信息的服务活动。

互联网信息服务管理办法, 2000 年 9 月 20 日

而在完成了定性操作后,紧接着在第四条声明了不同服务类型应该执行的策略:经营性采取许可制度,非经营性采取备案制度。

第四条 国家对经营性互联网信息服务实行许可制度;对非经营性互联网信息服务实行备案制度。

互联网信息服务管理办法, 2000 年 9 月 20 日

至此,我们熟悉的 ICP 备案走上了台前。而我们现在了解到的各种具体的备案的细节,则是在管理办法内容的延展,如果你对于这些内容感兴趣,可以详细的阅读这个信息管理办法。

在 2005 年 1 月 28 日,在《互联网信息管理办法》之上的细分管理办法《非经营性互联网信息服务备案管理办法》(后称非经营性管理办法)也相应出台,对于非经营性互联网信息服务进行了更加明确的安排和说明。我们所熟悉的备案登记表、省级通管局、网站底部公示备案编号等描述也出现在了非经营性管理办法当中,网站的备案事项,也正式被推行。

备案到底在备案什么?

备案对于很多人来说,最麻烦的不是搞不清楚怎么备案,而是搞不清楚如何处理域名、网站、 服务器、个人之间的关系。而所有的这些关系,其实可以用一个词来说明 —— 「双绑定制度」。

现行的网站备案制度其实是一种“双绑定制度”,一方面将网站和所有人绑定在一起,另一方面,将网站和它的接入商绑定在一起。一旦某个网站出现了问题,则需要能够在第一时间找到网站的所有人和接入商。

阮一峰,《关于网站备案,》,2009年 9 月 9 日

备案并非一个阻拦性措施,而是一个监管性措施(对比经营性互联网信息服务的许可制),希望大家可以积极踊跃的进行互联网信息服务的同时,避免恶劣影响的网站持续提供服务。找到接入商来第一时间拔网线,缩小影响范围;找到所有人来进行相应的处罚。

备案过程中的五个关键角色

当我们搞清楚了备案的核心理念和目的之后,我们重新回过头来看看备案当中的五个关键角色:备案主体省通管局接入服务提供商域名注册商域名管理机构

各关键角色之间的关系

当我们真正开始进行备案时,就会面临三个元素:域名、服务器、所有人。这五个关键角色与三个元素,依次发生关系。

域名管理机构和域名

当我们开始思考建设一个网站时,必然会涉及到域名的问题。而实际上自 2017 年 11 月 30 日颁布的《工业和信息化部关于规范互联网信息服务使用域名的通知》正式实行之后, 并不是每一个域名都能够完成备案。在该通知中,要求互联网域名注册服务机构要在我国进行相应的注册和审批,才能够让对应的后缀进入到备案的流程。如果对应的域名注册服务机构没有在我国进行注册,则对应的域名无法在我国进行备案。

从而,也就出现了现在很多时候我们在购买域名时候需要考虑的一些问题 —— 域名能否完成备案?

目前所有完成了相应注册机构的审批都可以在工业和信息化部域名行业管理信息公示网站找到。如果你需要购买域名且有打算在国内进行备案,则一定记得确认你所购买的域名是否可以在国内完成备案的流程。

此外,如果你感兴趣的话,还可以在这里找到一些你可能从来都没听说过、没见过的后缀,比如.baidu

对于已经进行的域名注册服务机构,因为工信部有了相应的沟通方式,则可以在出现问题后,下发公函,要求对应的服务机构协助调查和配合。而对于没有注册的管理机构,无法完成相应的配合,干脆不予后缀备案。

这也解释了为什么现在你购买的 .io 域名不能备案,因为对应的注册管理机构没有在我国注册,不符合备案的要求。至于你看到的那些有备案号的 .io 域名,则是在相应的通知发布之前完成的备案,故而可以正常使用。

域名注册商和域名

当你选择了一个可以进行备案的域名后缀(比如 .com)之后,就可以进行域名的注册了。域名的注册需要到对应的域名注册商来完成。这个时候,也会涉及到不同的域名注册商可能会影响你的备案流程。

按照新的管理办法,你的域名需要在购买的时候完成实名认证。因此,你如果域名想要备案,则要求你的域名注册商支持进行实名认证。

比如,目前你可以在阿里云(万网)、腾讯云(新网)、西部数据等域名注册服务提供商这里购买域名并进行实名认证,在实名认证完成后,再进行后续的域名备案操作。

相应的,对于域名注册服务机构,也采用了审批和注册的限制,不同的服务商能够支持的域名实名认证也是不同的,企业需要根据「互联网域名注册服务机构审批」来进行提交,等待审批通过后,才能提供相应的域名注册和实名认证服务。

目前通过审批的域名注册服务机构也可以在工业和信息化部域名行业管理信息公示网站找到。

这也解释了为什么你在 Godaddy 、Namesilo、Namecheap 之类的域名注册机构注册的域名无法在国内进行备案,因为他们并没有在国内注册,接入相应的实名认证流程,自然无法为你提供后续的备案服务。

域名和所有人

当你选择好了域名后缀和域名注册服务商,进入了详细的域名注册流程当中时,会涉及到域名的实名认证的环节。

在这个时候你需要注意,备案要求域名实名认证和备案的主体是保持一致的,因此,如果你后续打算以哪个主体(个人或者企业)进行备案,在这里进行实名认证时,就要选择对应的信息来进行实名认证。

你提交信息后,域名注册服务机构(域名注册服务商)就会将你的域名和对应的实名信息提交到工信部。一般 1 ~ 2 个工作日后,你的信息就可以在工信部的内部系统可查,这个时候才能进行下一步的备案。

接入服务提供商和域名

域名购买好了,接下来就是选择接入服务提供商。由于备案实际上是个人与网站、网站与接入服务商进行的「双向绑定」,因此,能够提供备案服务的服务商都是在工信部进行了相应的注册的服务商(需要持有互联网数据中心业务经营许可证)。

在进行备案时,会要求你的域名已经完成了实名认证,然后再提交信息进行相应的备案,这里会要求域名的实名认证和接入服务提供商的实名认证信息保持一致,进行审核。

这也解释了为什么你购买的 DigitalOcean 的服务器无法进行备案,因为 DigitalOcean 并不持有互联网数据中心业务经营许可证。当然, DigitalOcean 的服务器也不需要进行备案。

所有人和通信管理局

当你进行备案时,会让你选择一个备案的所在地,备案的所在地决定了你的网站归属于哪个省份的通信管理局来进行管理。

一般来说,大家会选择身份证所在地。当然,如果按照惯例办法中的说明了,其实你还可以选择你的当前所在地(因为管局在乎的是能否联系到你)。选择了对应的省份,需要你提交相应的信息(需要能够精确到门牌号),基于对应的信息来进行备案。不过需要注意的是,不同的地区可能有所不同,比如基于当前所在地的备案可能会要求你提供居住证来进行备案。

理论上,你可以拥有多个不同的备案号,一个身份证所在地的备案号,和多个居住地所在的备案号

接入服务提供商和通信管理局

当你准备好所有信息,接入服务商帮你进行了基本的审核后,就会将你的信息提交至你备案的对应的通管局。而 ICP 备案本身是备案制度,因此,审批其实大部分情况下都是能够通过的,只是说需要一点时间,不同的省份在备案的审批上所需的时间也不同,根据《非经营性互联网信息服务备案管理办法》的要求,会在 20 个工作日内完成审核。不过某些特定的省份是比较快的(比如上海、浙江,据说是可以 1个工作日完成审批)。

当你完成审批后,就会获得一个备案号,将对应的备案号放置在你的网站底部,并加上前往通管局的链接即可。

拿到备案号之后的注意事项

新增备案网站

当你完成了第一个网站备案和建设后,你可能会需要进行第二个网站的备案和建设,这个时候,你需要注意:

  1. 你的第一个网站的用途应该符合备案时申报的类目(不然可能无法进行第二轮)
  2. 你应当按照规定放置了各项备案号等信息。
  3. 进行第二轮备案时,你的第一个网站应该是可以打开的。后续每新增一个网站,就会要求你之前的网站是可以正常打开的。

这就要求你如果某个域名不再使用,尽快将其关闭,并注销域名和相应的备案。

备案号、域名和接入服务商

当你拿到域名备案号之后,你就可以在你办理备案的接入服务商进行网站的建设和开发工作了。但需要注意的是,你仅能使用当前办理了接入服务商的服务,如果想要使用其他家的服务,则需要再次办理接入,新增一个接入,以使用对应服务商的产品。毕竟,备案的核心诉求是「双向绑定」,绑定用户和网站、网站和接入商。你虽然完成了备案,解决了用户和网站的绑定,但如果你没有完成网站和接入商的绑定(做接入备案),接入商同样会拒绝为你提供服务。

此外,需要注意的是,你的名下应当始终至少有一个网站,对于一个网站都没有的(比如你注销掉了所有的网站)人,在备案系统中被称为「空壳主体」,通管局会定期清理空壳主体,所以为了保住你的主体和备案号,尽量保证你的名下有网站。如果需要迁移服务商,也在完成了新的服务商接入之后,再注销之前服务商的接入。

独特的 CDN

在备案体系中,有一个很独特的产品 —— CDN 。和云主机不同,云主机会要求进行接入后才能使用,而 CDN 则没有相应的要求,你只要完成了备案,无需再进行接入即可使用 CDN 。

CDN 作为一个分布式的产品,因为没办法统一进行接入,故而只做了备案的限制,而不再要求你的域名必须在对应的服务商进行接入。

如果网站出现了问题,可能会怎么处理?

其实前面说了那么多,都是一些知识性的内容,接下来,我们来面对一个真实世界的问题 —— 如果某个网站出现了问题。可能会怎么处理

前面说过,备案本质上是为了能够在出现问题后快速找到你,解决网站中的问题。但真正在执行时,可能会有哪些操作?

  1. 如果有备案,直接联系备案时提供的联系电话(这也是为什么你备案的时候会有一个联系电话和一个备用联系电话),取得联系后,由网站负责人进行整改。
  2. 如果备案电话打不通,则可以联系服务接入商,通过接入商来暂停网站的接入服务,从而让网站无法访问。
  3. 如果服务商接入暂停后,依然可以访问(即你虽然备案了,但你的网站其实在其他服务器),那就通知你的 DNS 解析服务提供商停止解析。
  4. 如果你的 DNS 也是自己解析的,那就可以联系你的域名注册服务提供商,直接停止域名的服务状态。
  5. 如果你甚至域名注册服务商都是自己搞的,那可以联系域名管理机构,停掉你这个域名的使用。
  6. 当上述的所有办法都无法解决问题后,那就直接给你拉入黑名单,成为我们过去常说的被「墙」了的网站。

后记

这篇文章写完了,让我心头一松。我从 2012 年开始建立个人网站,2013 年、2014 年开始备案第一个域名,后续在 2016 年,在网易实习时以接入服务商视角看待了备案,再到在线,备案的政策不断的变化,我自己从某种意义上也经历了备案从松到严的一段历程(虽然没有经过最松的那段时间)。

备案机制从很多从业者的角度来看,觉得是繁文缛节,但换个角度来看,也是为我们的互联网提供了一定的基本保障制度。互联网上的信息良莠不齐,备案虽不能以黑名单的机制保证我们所看的信息“绝对干净“,但却也以白名单的方式给了一些建议。

不过,这篇文章也有遗憾,因为篇幅限制,我未能介绍关于公安备案相关的信息,这部分就留作后续再独立成文来介绍吧。

turned on black Android smartphone

如何在阿里云上申请免费的 SSL 证书

在进行网站建设的时候, SSL 证书是一个可以有效的提升网络数据传输过程安全性的手段。而一般来说,大家可以选择直接购买 SSL 证书或使用 Let’s Encrypt 提供的免费证书。但 Let’s Encrypt 的证书问题在于往往是短期证书(比如三个月),如果要使用在 CDN 上,就会需要经常替换,非常的麻烦。

目前各家云厂商大多都提供了免费的 SSL 证书,方便大家使用,不过有些时候入口比较深,所以大部分人找不到,只能购买付费的证书。写这篇文章就是希望帮助那些希望使用云服务厂商免费证书的同学快速找到入口。

操作步骤

1. 进入阿里云控制台

打开 console.aliyun.com 了,登录进入阿里云控制台。

2. 进入「SSL 证书(应用安全)」页面

在阿里云控制台找到 SSL 证书(应用安全)这个产品,点击进入产品控制台页面。

3. 点击控制台左侧的 「SSL 证书页面」,进入到 SSL 证书页面

4. 在 SSL 证书页面点击上方 Tab,进入到免费证书 Tab

5. 点击「创建证书」按钮,创建新的空白证书

6. 点击操作栏中的「证书申请」,申请证书

这部分需要注意的是:

  1. 证书绑定域名:阿里云免费证书只支持单个域名。不支持多域名和泛域名。
  2. 域名验证方式:如果你的域名使用了阿里云提供的 DNS 服务,就可以使用自动 DNS 验证方式。如果不是用阿里云的 DNS,就需要手动配置 TXT 记录来进行验证。
  3. 联系人:填写具体的信息,如果证书申请过程中有疑问,会联系这个电话。
  4. CSR 生成方式:你可以自己创建 CSR,也可以让系统生成。如果不是特别关注,使用系统生成就行。

7. 下载证书

当你配置完成后,稍等片刻,证书就会生成成功,此时点击操作栏的「下载」即可下载到证书文件,你根据自己的实际情况,下载对应格式的证书即可。

总结

虽然阿里云的免费证书一年只有 20 个证书的名额,但对于绝大多数人来说,已经足够,在你需要 CDN 或其他更新不方便的情况下,使用云厂商提供的免费一年证书还是挺好的。

在 Pug 中实现类似 get_sidebar() 全局方法

在开发 WordPress 主题时,我们会用到一些全局方法,来帮助我们快速加载特定的区域的代码。如果我们在设计和开发主题的时候,也可以实现类似的功能,则在开发对应的页面和应用的时候,我们就可以根据自己的需求来进行特定区域的代码的封装。这样我们在进行后续的开发的时候,就可以简化自己的代码,同时还可以按照 WordPress 的规范提前拆分代码,在实际进行项目开发的时候,提升效率。

原理

本次实现主要是基于 Pug 自带的 Mixins 机制来实现在主题中提供自定义的函数,从而实现我们想要的内容。在原理上,和 WordPress 的 get_sidebar 之类的方法不完全一样。所以,在体验上还没有做到像 WordPress 那么方便。

实现方式

1. 创建 Mixins

在项目的根目录下创建一个 mixins 目录,并在其中创建一个 includes.pug 文件和一个 getHeader.pug 文件。

includes.pug 文件中添加如下代码

include getHeader

getHeader.pug 文件中添加如下代码

mixin getHeader
    p header

2. 引入 includes.pug

在你的 layouts.pug 文件中加入 includes.pug 的引入。需要注意的是,要加在 doctype 之前。

include ../mixins/includes
doctype html
html
  head

3. 在需要的地方调用

当你完成上述的配置后,即可在需要的地方进行调用。

extends layout

block content
  +getHeader
  h1= title
  p hi to #{title}

调用的效果如下:

如果你希望实现更加强大的功能(比如参数),可以参考 Pug 官网的 Mixins 页面。

为 Express 项目添加文件变更自动刷新

我最近在准备开发新的 WordPress 的主题的工具,为了方便自己开发主题,正在准备一个脚手架,本文是开发过程中的经验总结。

白宦成

在进行主题开发的时候,一个很好的体验是来自于变更能够实时生效。过去我们想要实现这样的功能需要自己手动刷新浏览器。但随着前端技术的进步,我们可以借助诸如 browser-sync 这样的工具来完成自动的刷新,从而实现无需手动刷新,代码修改后自动刷新。此外,除了 browser-sync, 你还可以选择 webpack-dev-server

原理分析

此类工具的工作模式是比较简单的,一般而言,会要求你设定一个代理的端口,他会将对应端口的请求进行转发,并在其中加入 BrowserSync 的 JS 文件,从而实现可以无需手动操作来刷新。

browser-sync-client.js

而其刷新的能力则是源自于 BrowserSync 本地提供的 WebSocket Server。嵌入的 JS 文件中加入了 WebSocket 的相关链接代码, 从而实现从 Command Line 当中获取刷新指令,前端则在获取到指令后进行刷新。

WebSocket 请求

具体步骤

1. 安装 BrowserSync

首先在项目中引入 browser-sync ,执行如下命令

npm install browser-sync --save-dev

2. 修改启动文件

我是使用 express generator 生成的项目,需要修改 bin/www 文件,如果你是自己建设的项目,则需要自行修改。

在启动文件中找到监听端口的代码, 并在其回调函数中添加如下代码

# 添加前
server.listen(port)
# 添加后
var bs = require('browser-sync').create();
server.listen(port, () => { 
  bs.init({
    open: false,
    ui: false,
    notify: false,
    proxy: 'localhost:' + port,
    files: ['./views/**', './routes/**'],
    port: 8080
  });
});

上述配置中,可以根据你的需要修改 proxy 、files、port 等选项,实现自定义配置。

关于配置的更多信息,你可以参考 https://browsersync.io/docs/options

3. 启动服务,并测试

在命令行中执行 npm start ,在浏览器中打开对应的页面,当可以看到内容后,可以修改文件,等待页面的自动刷新,如果成功刷新了,则表示你已经完成了相关能力的配置。

silver mercedes benz emblem on blue surface

在开发 Gutenberg 插件时,如何处理防抖?

在开发 Gutenberg 插件时,如果你需要对编辑器的内容进行处理,则需要为其加入防抖措施,避免你的函数被频繁调用。

思路

由于编辑器需要高频处理,因此需要采用防抖策略,避免将多次输入视为不同的事件。这里可以使用 WordPress 提供的 useDebounce 来实现控制。需要注意的是,WordPress 自动的 Debounce 函数传入的参数应当采用 useCallback 来进行包裹。

参考代码

import {subscribe} from "@wordpress/data";
import {useDebounce} from  "@wordpress/compose"
import {useCallback} from "@wordpress/element";

const debounced = useDebounce(
        useCallback(() => {
            // do some thing
        }),500
    );
subscribe(debounced);
black and white penguin toy

使用 Github Action 发布 WordPress 插件

WordPress 官网的插件系统采用的是 SVN 来进行管理,如果你希望将自己的插件发布至 WordPress 官网,就必须在本地安装 SVN,不太利于项目的管理。此外,WordPress 官网提供的是一个发布系统,而非版本控制系统(只是将 SVN 用作插件管理,并不是真打算让你每一个变更都提交),因此,你还是需要使用另外的版本控制系统(比如 Git)来管理你的代码。

为了简化发布,你可以采用 GitHub Action 来完成你的插件自动发布,这样你就可以使用 Git 来开发和管理你的插件,并让 Github Action 自动进行插件的发布。

具体操作

1. 配置 Action Secrets

因为你需要使用版本控制来进行发布,以及可能存在的协作开发的场景,处于安全的考虑,你应该将你的 SVN 的信息放置在 Action Secrets 当中。

在仓库当中找到 Secrets – Actions ,新建两个 Secrets:SVN_USERNAMESVN_PASSWORD,配置上你的 SVN 账号信息,稍后将会使用你的这个账号信息来进行插件的发布

2. 创建一个 .distignore 文件

.distignore 文件可以帮助你实现忽略不需要发布到 WordPress 插件系统的文件,这样就让你的插件目录变得更加干净一些。在你的仓库根目录创建一个 .distignore 文件,并填写具体要忽略的文件,即可实现部分发布至 WordPress 插件目录。

以下是我的 Example

node_modules/
.github/
.git/
.distignore
.gitignore
package.json
package-lock.json
yarn.lock

3. 创建 Action 文件

在你的项目的 .github/workflows/ 中创建一个 publish.yml文件,并在其中添加如下代码即可。

name: Publish to WordPress Plugin Directory
on:
  push:
    tags:
    - "*"
jobs:
  tag:
    name: New tag
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@master
    - name: Build # 如果没有 NPM 相关构建工作,可以去除这一步。
      run: |
        npm install
        npm run build
    - name: WordPress Plugin Deploy
      uses: 10up/action-wordpress-plugin-deploy@stable
      env:
        SVN_PASSWORD: ${{ secrets.SVN_PASSWORD }}
        SVN_USERNAME: ${{ secrets.SVN_USERNAME }}
        SLUG: wpstoreapp-spellcheck

4. 发布版本

接下来只需要执行如下操作,既可以发布版本

git add .
git commit -m 'feat: release new version'
git tag 1.0.0
git push && git push origin 1.0.0

上述代码中的 1.0.0 即为具体的版本号,可以根据你的诉求进行修改。

总结

在完成了配置后,后续你的插件发布就变得十分简单了:修改本地的 readme.txt 中的版本号,加入 changelog,并修改插件文件头中的版本,即可提交一个 commit ,添加对应的版本的 tag,然后 Push 到 Github 上,由 Github 来进行插件的发布。

而如果你是以公司/团队的形式来管理插件,也可以用类似的方式,这样虽然每个人都不掌握 SVN 的账号密码,但却可以完成插件的发布,十分方便。

turned-on monitor

在 WordPress 的插件页面添加更多的连接

WordPress 的插件页面提供了不少的 Hooks,可以帮助我们实现自定义的插件页面,让用户在启用插件后,第一时间可以看到我们为其提供的功能,从而降低用户使用的成本。

以上图为例,你可以看到,我在左侧的功能区添加了「配置 Token」和「购买额度」;右侧的更多链接区提供了帮助手册和联系我们,帮助用户优化体验,更快的了解如何使用你的插件。

左侧功能区

首先,在左侧功能,WordPress 提供了两个 Filter来实现这个功能:

一个是 plugin_action_links,这个插件传入两个参数,一个是当前插件的链接构成的数组,另一个则是插件的文件路径,你可以根据传入的参数判断当前是使用哪个插件,并通过返回不同的数组,来实现不同的功能。

另外一个则是 plugin_action_links_wpstore-spellcheck/wpstore-spellcheck.php,是在上一个 filter 的基础之上,加入了插件的路径,从而让你可以无需进行判断,直接针对对应插件来完成链接的修改。

以上图效果为例,具体的代码如下

function wpstoreapp_plugin_action_link($links, $file)
{
	if($file == plugin_basename(dirname(__FILE__) . '/wpstore-spellcheck.php')){
		$links[] = '<a href="./options-writing.php">配置 Token</a>';
		$links[] = '<a href="https://api.wpstore.app/plugins/spell-check/pricing" target="_blank">购买额度</a>';
	}
	return $links;
}

add_filter('plugin_action_links', 'wpstoreapp_plugin_action_link', 10, 2);

这里我选择的是第一种实现方式,主要的原因是我无法保证用户安装插件一定是采用的是 WordPress 插件目录的安装方式,因此,在这种情况下,第二种带了路径的 filter 的名字是有可能发生变化的。

右侧更多链接区

右侧更多链接区可以用来承载联系我们、帮助文档等入口,从而实现用户在使用时,可以快速找到相应的辅助资料,从而获得更好的体验。

从实现的逻辑上来看,和在左侧功能区添加链接的基本逻辑是一样的,需要判断当前插件是否是目标插件,如果是,则可以根据需要添加功能。

add_filter( 'plugin_row_meta', 'wpstoreapp_plugin_row_meta', 10, 2 );

function wpstoreapp_plugin_row_meta( $links, $file ) {
	if ( plugin_basename( __FILE__ ) == $file ) {
		$row_meta = array(
			'docs'    => '<a href="https://www.wpstore.app/?p=291">帮助手册</a>',
			'contact'    => '<a href="mailto:hi@wpstore.app">联系我们</a>'
		);

		return array_merge( $links, $row_meta );
	}
	return (array) $links;
}

总结

WordPress 为我们提供了方便的 Hook,可以让我们可以自定义我们想要的功能,借助这些功能来降低你的用户的 Landing 成本,从而实现更好的服务用户,是一个不错的选择。

silver mercedes benz emblem on blue surface

如何“抄”一个 WordPress 插件

WordPress 作为 CMS 生态的第一产品,PHP 生态的护城河,可以说,只要 WordPress 不死,PHP 就还能继续活下去。而 WordPress 团队也在不断迭代项目。因此,有不少的开发者以 WordPress 开发为生。

而得益于 WordPress 基于 GPL 开发的,所以,理论上,如果你开发的插件是通过 WordPress 官方的目录分发的,你的插件是需要以 GPL 兼容的协议开源的,这也给了想要开发同款插件的开发者们提供了机会。

PHP in WordPress themes must be GPL, artwork and CSS may be but are not required.

Matt Mullenweg, https://wordpress.org/news/2009/07/themes-are-gpl-too/

不过,需要注意的是,只有 PHP 的插件源码才需要以 GPL 兼容协议开源,CSS 和图片文件不需要开源,而随着 Gutenberg 编辑器的不断推广,以后的插件可能不太好“抄”了。

在这篇文章,我们不再讨论 Gutenberg 功能的研究,只谈插件的 PHP 部分。

如何理解 WordPress 的插件开发?

《人人都能学会的 WordPress 课程》当中,我曾经介绍过 WordPress 的插件的运行机制。如果现在用一个更加简单的说法来探讨这个问题的话,WordPress 是通过提供do_actionapply_filter这两个方法,实现了功能的可修改性。而插件则需要 add_actionadd_filter 这个方法来影响 WordPress 的运行表现

当你明白这个问题之后,你就会意识到,如果我们需要解析一个插件的运行机制,来开发自己的同类型插件之时,唯一要做的其实就是找到对应的 Action 和 Filter,修改 WordPress 的运行表现。

因此,你接下来要做的事情就变得简单很多,你需要做的,仅仅是在插件的代码中搜索所有的 add_actionadd_filter,并记录下对应的 action 和 filter 的名字,在 Code Reference 当中搜索对应的 action 的应用场景和用法即可。

Tips

  1. 按照 WordPress 的开发规范,插件的目录中一定有一个和其目录名相同的 PHP 文件,这个文件是 WordPress 插件的入口文件,你只需要从这个插件看起,就能找到你想要了解的实现方式。
  2. WordPress 插件的代码都可以在 WordPress Directory 中找到,你只需要在 Development 目录中,就可以找到对应的插件的源码。