Category: Web

  • OpenVidu:快速集成视频通话的利器

    在当今数字化时代,实时视频通话已经成为许多应用的核心功能之一。无论是远程医疗、在线教育、客户服务,还是虚拟会议,视频通话的需求都在不断增加。今天,我要向大家介绍的是一款强大的开源平台——OpenVidu,它能帮助开发者快速且低成本地将视频通话功能集成到他们的应用中。

    什么是 OpenVidu?

    OpenVidu 是一个旨在简化视频通话集成的开源平台。它提供了一整套技术栈,方便开发者快速将实时通讯功能添加到他们的应用中。无论你是开发网页应用还是移动应用,OpenVidu 都能满足你的需求。

    主要特性

    1. WebRTC 视频会议:支持一对一、一对多以及多对多的各种组合,几乎可以实现你能想到的任何场景。
    2. 开源:OpenVidu 是一个开源项目,使用 Apache License v2 许可证,完全免费。
    3. 多平台兼容:支持 Chrome、Firefox、Safari、Opera、Edge、Android、iOS 以及桌面应用,所有这些平台都能相互兼容。
    4. 易于使用:提供即用型组件,只需简单地粘贴代码即可快速实现视频通话。如果你需要更多的自定义,OpenVidu 的 API 也是非常简单且强大的。
    5. 易于部署:支持在最流行的云服务提供商上进行快速部署,或是通过 Docker 进行本地部署,过程非常简便。

    快速入门

    开始使用 OpenVidu 非常简单。你可以参考 OpenVidu 文档 中的“Getting started”部分,了解如何安装和配置 OpenVidu。以下是一些关键步骤:

    1. 安装 OpenVidu Server:你可以选择在 AWS 上一键部署 OpenVidu,也可以使用 Docker 在本地部署。
    2. 集成前端和后端:OpenVidu 提供了多种前端技术的示例,如 JavaScript、Angular、React、Vue.js 等。后端技术则包括 Java、Node.js 以及 REST API,方便你选择适合的技术栈。

    开发你的视频应用

    OpenVidu 提供了丰富的教程和示例,帮助你快速上手。以下是一些推荐的步骤:

    1. 学习基础知识:文档中提供了“Hello World”示例,帮助你快速了解基本的 API 调用和使用方法。
    2. 探索高级功能:你可以查看“Advanced features”部分,了解如何实现录制视频、屏幕共享、音视频滤镜等高级功能。
    3. 使用现成组件:如果你希望快速实现某些功能,可以使用 OpenVidu 提供的即用型组件,如自定义 UI、自定义工具栏等。

    安全性和隐私保护

    OpenVidu 非常重视用户的隐私和安全。它通过 WebRTC 加密、服务器 API 和客户端基于角色的系统,确保所有通话内容都是完全私密的。此外,OpenVidu 还允许你限制客户端的能力,通过预定义角色来决定用户是否可以订阅、发布或管理视频流。

    适用场景

    OpenVidu 的应用场景非常广泛,包括但不限于以下几种:

    • 客户服务:集成一对一视频通话中心,提供面对面的客户服务。
    • 远程医疗:医生可以通过视频通话直接与患者进行交流,确保私密和安全。
    • 在线教育:教师可以通过视频通话向学生讲解课程,支持多名学生同时在线。
    • 会议服务:支持演讲者实时应用音视频滤镜,提高会议质量。
    • 安防系统:接收来自安防摄像头的视频流,实现监控功能。

    结语

    无论你是想开发一个简单的视频聊天应用,还是一个复杂的视频会议系统,OpenVidu 都能提供强大的支持。它不仅简化了开发过程,还提供了丰富的功能和高水平的安全性,是你开发视频通话应用的不二选择。

    更多详细信息和教程,请访问 OpenVidu 文档


    参考文献:

  • 了解 Caddy 的分布式 HTTP 缓存模块

    Caddy 是一款功能强大的网络服务器,而 caddyserver/cache-handler 模块为其提供了强大的分布式 HTTP 缓存功能。本文将带你了解这个模块的特点、基本配置以及一些高级用法。

    模块简介

    caddyserver/cache-handler 是一个基于 Souin 缓存的分布式 HTTP 缓存模块。它具备以下主要特点:

    • 遵循 RFC 7234 标准的 HTTP 缓存。
    • 设置 Cache-Status HTTP 响应头。
    • 提供 REST API 来清除缓存和列出存储的资源。
    • 支持 ESI 标签处理(使用 go-esi 包)。
    • 内置分布式缓存支持。

    基本配置

    使用最小配置,响应会被缓存 120 秒。以下是一个简单的例子:

    {
        cache
    }
    
    example.com {
        cache
        reverse_proxy your-app:8080
    }

    这个配置中,只需添加 cache 指令,所有请求的响应将被缓存 120 秒。

    全局选项语法

    全局选项允许你更细粒度地控制缓存行为。以下是一些常用的配置选项:

    {
        log {
            level debug
        }
        cache {
            allowed_http_verbs GET POST PATCH
            api {
                basepath /some-basepath
                prometheus
                souin {
                    basepath /souin-changed-endpoint-path
                }
            }
            badger {
                path the_path_to_a_file.json
            }
            cache_keys {
                .*\.css {
                    disable_body
                    disable_host
                    disable_method
                    disable_query
                    headers X-Token Authorization
                    hide
                }
            }
            cache_name Another
            cdn {
                api_key XXXX
                dynamic
                email darkweak@protonmail.com
                hostname domain.com
                network your_network
                provider fastly
                strategy soft
                service_id 123456_id
                zone_id anywhere_zone
            }
            etcd {
                configuration {
                    # Your etcd configuration here
                }
            }
            key {
                disable_body
                disable_host
                disable_method
                headers Content-Type Authorization
            }
            log_level debug
            mode bypass
            nuts {
                path /path/to/the/storage
            }
            olric {
                url url_to_your_cluster:3320
                path the_path_to_a_file.yaml
                configuration {
                    # Your olric configuration here
                }
            }
            regex {
                exclude /test2.*
            }
            stale 200s
            ttl 1000s
            default_cache_control no-store
        }
    }
    
    :4443
    respond "Hello World!"

    指令选项

    缓存指令允许你在更具体的请求路径上应用缓存策略。例如:

    @match path /path
    
    handle @match {
        cache {
            cache_name ChangeName
            cache_keys {
                (host1|host2).*\.css {
                    disable_body
                    disable_host
                    disable_method
                    disable_query
                    headers X-Token Authorization
                }
            }
            cdn {
                api_key XXXX
                dynamic
                email darkweak@protonmail.com
                hostname domain.com
                network your_network
                provider fastly
                strategy soft
                service_id 123456_id
                zone_id anywhere_zone
            }
            key {
                disable_body
                disable_host
                disable_method
                disable_query
                headers Content-Type Authorization
            }
            log_level debug
            regex {
                exclude /test2.*
            }
            stale 200s
            ttl 1000s
            default_cache_control no-store
        }
    }

    缓存提供者配置

    caddyserver/cache-handler 支持多种缓存提供者,包括 Badger、Etcd、NutsDB、Olric 和 Redis。以下是一些示例配置:

    Badger

    badger-path.com {
        cache {
            badger {
                path /tmp/badger/first-match
            }
        }
    }

    Redis

    redis-url.com {
        cache {
            redis {
                url 127.0.0.1:6379
            }
        }
    }

    结论

    caddyserver/cache-handler 模块为 Caddy 提供了强大的分布式 HTTP 缓存能力。通过灵活的配置选项和多种缓存提供者的支持,你可以根据具体需求优化网站性能。如果你正在寻找一种高效的缓存解决方案,这个模块无疑是一个值得尝试的选择。

    参考文献:GitHub – caddyserver/cache-handler: Distributed HTTP caching module for Caddy

  • Caddy 2.8 Web 服务器:重大改进与更新

    Caddy 2.8 作为一个强大的跨平台开源 Web 和反向代理服务器,已经正式发布了。这次发布带来了许多令人瞩目的改进,包括对 ACME Renewal Information (ARI) 的支持、通过 HTTP/3 代理到后端的功能以及核心依赖项的重要改进,如 ACMEz 和 CertMagic。

    主要改进概览

    证书处理方式的变化

    在之前的版本中,Caddy 自动使用 Let’s Encrypt 和 ZeroSSL 提供证书而无需配置。然而,在新版中,由于 ZeroSSL 政策的变化,用户现在必须在配置中包含电子邮件地址以继续使用 ZeroSSL。这一调整虽然与 Caddy 过去几年的推荐一致,但可能需要一些用户进行配置更新以维持当前设置。

    核心技术的升级

    开发人员对 Caddy 的底层技术进行了精细优化,包括 CertMagic 和 ACMEz 的重大升级,这对证书管理和自动化至关重要。这些变化虽然对终端用户来说可能不太明显,但对于在高流量环境中增强 Caddy 的操作效率至关重要。

    新功能与弃用功能

    Caddy 2.8 引入了多个重要的破坏性更改:

    • ZeroSSL 发行模块不再具备 ACME 能力;它需要 API 密钥才能继续使用。
    • lego_deprecated DNS 提供模块已被移除,取而代之的是更灵活、更精简的 caddy-dns 模块。
    • 一些过时的选项,如 remote_ip 匹配器中的 forwarded 选项,已被移除,推动用户采用新的配置。

    用户体验的提升

    Caddy 2.8 非常重视用户体验,许多改进直接来源于社区反馈。这些改进包括配置简化和文档更新,这些文档将在 Caddy 网站上很快更新,以反映新的变化。

    此外,发布日志中还列出了许多较小的更新,供用户查看。

    总结

    Caddy 2.8 版本不仅在功能上进行了重大更新,还在用户体验和操作效率上进行了全面提升。对于那些依赖 Caddy 进行高流量管理和自动化证书管理的用户来说,这些改进将是非常有价值的。

    更多详细更新和配置说明,请访问 Caddy 官方网站或查看发布日志。


    参考文献

    • Bobby Borisov, “Caddy 2.8 Web Server Is Here with Many Improvements,” Linuxiac, May 30, 2024. 链接
  • 为apache2 开启https

    要在Apache2上启用HTTPS,您需要完成以下步骤:

    • 安装所需的软件包:
    • 在Ubuntu上,使用以下命令安装Apache2:sudo apt install apache2
    • 在CentOS上,使用以下命令安装Apache2:sudo yum install httpd
    • 生成SSL证书和密钥:
    • 您可以使用自签名证书进行测试,或者从证书颁发机构获取有效的SSL证书。
    • 使用以下命令生成自签名证书和密钥:
      sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/ssl/private/apache-selfsigned.key -out /etc/ssl/certs/apache-selfsigned.crt
    • 配置Apache2以使用SSL:
    • 在Ubuntu上,使用以下命令启用SSL模块:sudo a2enmod ssl
    • 在CentOS上,SSL模块已经默认启用。
    • 创建SSL虚拟主机配置文件:
    • 在Ubuntu上,使用以下命令创建新的配置文件:sudo nano /etc/apache2/sites-available/default-ssl.conf
    • 在CentOS上,使用以下命令创建新的配置文件:sudo nano /etc/httpd/conf.d/ssl.conf
    • 将以下内容添加到配置文件中: <VirtualHost _default_:443> ServerAdmin your_email@example.com DocumentRoot /var/www/html ServerName your_domain.com SSLEngine on SSLCertificateFile /etc/ssl/certs/apache-selfsigned.crt SSLCertificateKeyFile /etc/ssl/private/apache-selfsigned.key &lt;FilesMatch "\.(cgi|shtml|phtml|php)$"&gt; SSLOptions +StdEnvVars &lt;/FilesMatch&gt; &lt;Directory /usr/lib/cgi-bin&gt; SSLOptions +StdEnvVars &lt;/Directory&gt; ErrorLog ${APACHE_LOG_DIR}/error.log CustomLog ${APACHE_LOG_DIR}/access.log combined </VirtualHost>
    • 启用SSL虚拟主机配置:
    • 在Ubuntu上,使用以下命令启用配置文件:sudo a2ensite default-ssl.conf
    • 在CentOS上,无需启用配置文件。
    • 重新加载Apache2配置:
    • 在Ubuntu上,使用以下命令重新加载配置:sudo systemctl reload apache2
    • 在CentOS上,使用以下命令重新加载配置:sudo systemctl reload httpd
    • 防火墙设置:
    • 在Ubuntu上,使用以下命令允许HTTPS流量:sudo ufw allow 'Apache Full'
    • 在CentOS上,使用以下命令允许HTTPS流量:sudo firewall-cmd --permanent --add-service=https
    • 重新加载防火墙规则:
      • 在Ubuntu上,使用以下命令重新加载防火墙:sudo ufw reload
      • 在CentOS上,使用以下命令重新加载防火墙:sudo firewall-cmd --reload

    现在,您的Apache2服务器已经配置为使用HTTPS。您可以通过访问https://your_domain.com来测试它。


    Learn more:

    1. Ubuntu下安装和配置apache2并启用https_ubuntu apache2 配置443反向代理-CSDN博客
    2. Apache配置HTTPS功能 – 温柔易淡 – 博客园
    3. 下载并在Ubuntu Apache2配置SSL证书实现网站HTTPS加密_数字证书管理服务(原SSL证书)(SSL Certificate)-阿里云帮助中心
  • 揭秘Caddy 2 + PHP-FPM 部署WordPress的卡顿谜团

    大家好,今天我将带您走进一个神秘的领域——Caddy 2 服务器与 PHP-FPM 部署的 WordPress 的卡顿现象。为什么我们辛辛苦苦搭建的网站会卡顿?让我们一探究竟。

    1. 初识Caddy 2与PHP-FPM

    首先,让我们简单介绍一下Caddy 2和PHP-FPM。Caddy 2 是一款现代化的开源Web服务器,因其自动HTTPS配置和简洁的配置文件赢得了广泛的好评。PHP-FPM(PHP FastCGI Process Manager)则是一个专为处理PHP请求优化的进程管理器,常用于提高网站性能。

    2. 卡顿现象的初步排查

    2.1 服务器资源不足

    当我们发现WordPress网站卡顿,首要怀疑的就是服务器资源不足。检查CPU使用率和内存占用情况是否过高。如果是的话,可能需要升级服务器配置。

    2.2 网络带宽问题

    网络带宽不足同样会造成网站卡顿。使用工具如 pingtraceroute 检查网络延迟和丢包率。如果网络状况不佳,可以尝试联系服务提供商解决。

    2.3 数据库性能

    WordPress的数据库性能也至关重要。使用 SHOW FULL PROCESSLIST; 命令检查MySQL数据库是否有慢查询,或者配置查询缓存来提高性能。

    3. 深入探讨Caddy 2与PHP-FPM的优化

    3.1 Caddy 2 的优化

    Caddy 2 默认的配置已经相当不错,但我们可以进一步优化:

    {
        auto_https off
        http_port 80
        https_port 443
    }
    
    example.com {
        root * /var/www/html
        php_fastcgi unix//run/php/php7.4-fpm.sock
        file_server
        encode gzip
    }
    • gzip压缩:启用gzip压缩可以减少传输数据量,提高加载速度。
    • 缓存:配置缓存以减少对服务器的压力。

    3.2 PHP-FPM 的优化

    PHP-FPM 的配置文件通常位于 /etc/php/7.4/fpm/pool.d/www.conf

    pm = dynamic
    pm.max_children = 50
    pm.start_servers = 5
    pm.min_spare_servers = 5
    pm.max_spare_servers = 35
    pm.max_requests = 500
    • pm.max_children:设置能够处理的最大并发请求数。
    • pm.max_requests:设置每个子进程在重启前处理的最大请求数,避免内存泄漏。

    4. WordPress的优化

    4.1 插件管理

    尽量减少并优化插件,特别是那些影响性能的插件。使用插件如 Query Monitor 来监控性能瓶颈。

    4.2 缓存插件

    使用缓存插件如 W3 Total Cache 或 WP Super Cache,可以显著提高页面加载速度。

    4.3 图像优化

    图像通常是页面加载时间的主要因素之一。使用插件如 Smush 来优化图像大小。

    5. CDN加速

    将静态资源(如图片、CSS、JS)托管到CDN(内容分发网络)上,可以显著提高加载速度,减轻服务器负担。

    结论

    通过以上措施,我们可以有效解决Caddy 2 + PHP-FPM 部署的WordPress网站卡顿问题。当然,实际情况可能千差万别,您需要根据具体情况进行调整和优化。希望这篇文章对您有所帮助!


    参考文献:

  • Caddy 2中配置缓存

    在Caddy 2中配置缓存可以使用cache-handler插件。以下是配置Caddy 2缓存的步骤:

    1. 安装cache-handler插件:首先,确保已安装Caddy 2,并且可以使用插件。然后,下载并安装cache-handler插件。可以从GitHub的caddyserver/cache-handler仓库中获取插件的最新版本。
    2. 配置全局缓存选项:在Caddyfile中配置全局缓存选项。这些选项将适用于所有站点。以下是一些常用的全局选项:
    • order cache before rewrite:确保缓存处理在重写之前执行。
    • cache:启用缓存处理。
    • cache.allowed_http_verbs:允许被缓存的HTTP方法,默认为GET和HEAD。
    • cache.api.basepath:缓存管理API的基本路径。
    • cache.badger.path:配置Badger缓存存储的路径。
    • cache.cache_name:覆盖在Cache-Status响应头中使用的缓存名称。
    • cache.default_cache_control:如果上游未设置Cache-Control响应头,则设置默认值。 你可以根据需要配置其他选项。完整的全局选项列表可以在cache-handler插件的文档中找到[1]
    1. 配置站点缓存选项:在Caddyfile中为每个站点配置缓存选项。以下是一个示例配置:
       example.com {
         cache
         reverse_proxy your-app:8080
       }

    在这个示例中,cache指令启用了缓存处理,并将请求代理到your-app:8080

    1. 配置缓存键:你可以使用cache_keys指令为特定的URI配置缓存键生成规则。例如:
       cache_keys {
         .+\.css {
           disable_body
           disable_host
           disable_method
           disable_query
           headers X-Token Authorization
         }
       }

    在这个示例中,所有以.css结尾的URI将使用指定的规则生成缓存键。

    1. 配置CDN管理(可选):如果你使用CDN代理请求,可以配置CDN管理选项。这些选项将与缓存处理一起使用。例如:
       cdn {
         provider fastly
         api_key XXXX
         dynamic
         email darkweak@protonmail.com
         hostname domain.com
         network your_network
         strategy soft
         service_id 123456_id
         zone_id anywhere_zone
       }

    在这个示例中,使用Fastly作为CDN提供商,并配置了相关的API密钥、邮箱、主机名等信息。

    1. 保存并重新加载Caddy配置:完成配置后,保存Caddyfile并重新加载Caddy配置,以使更改生效。

    这些是配置Caddy 2缓存的基本步骤。你可以根据需要调整和扩展这些配置选项。请参考cache-handler插件的文档以获取更多详细信息和选项[1]


    Learn more:

    1. GitHub – caddyserver/cache-handler: Distributed HTTP caching module for Caddy
    2. State of HTTP Caching in Caddy 2 – Help – Caddy Community
    3. 【Caddy2】最新Caddy2配置文件解析 – 詩 – 博客园

    darkweak/souin 是一个HTTP缓存系统,符合RFC标准,并与@tyktechnologies、@traefik、@caddyserver、@go-chi、@bnkamalesh、@beego、@devfeel、@labstack、@gofiber、@go-goyave、@go-kratos、@gin-gonic、@roadrunner-server、@zalando、@zeromicro、@nginx和@apache兼容[1]。它可以作为反向代理缓存系统,可以放置在当前反向代理(如Apache、Nginx)的顶部,也可以作为Træfik、Caddy或Tyk等喜爱的反向代理的插件使用。由于它是用Go语言编写的,可以部署在任何服务器上,并且由于具有Docker集成,可以轻松安装在Swarm或Kubernetes实例上。它支持Vary、请求合并、陈旧的Cache-Control和其他与RFC-7234相关的规范,还支持新编写的RFC(目前处于草案阶段)http-cache-groups和http-invalidation。它还支持ESI标签,感谢go-esi包的支持[1]

    Souin的配置文件存储在/anywhere/configuration.yml中,可以根据需要提供自己的配置文件。配置文件中包含了各种配置选项,如缓存时间、反向代理URL、API配置、缓存键配置等[1]

    Souin还提供了各种插件,如Beego过滤器、Caddy模块、Chi中间件、Dotweb中间件、Echo中间件、Fiber中间件、Gin中间件、Goa中间件、Go-zero中间件、Goyave中间件、Hertz中间件、Kratos过滤器、Roadrunner中间件、Skipper过滤器、Træfik插件、Tyk插件、Webgo中间件、Prestashop插件和Wordpress插件等[1]

    Souin还支持Prometheus API、Souin API和Security API,可以用于监控和管理缓存系统[1]

    总结:darkweak/souin是一个符合RFC标准的HTTP缓存系统,可以作为反向代理缓存系统使用,也可以作为插件嵌入到其他反向代理中。它支持各种配置选项和插件,并提供了API用于监控和管理缓存系统。


    Learn more:

    1. GitHub – darkweak/souin: An HTTP cache system, RFC compliant, compatible with @tyktechnologies, @traefik, @caddyserver, @go-chi, @bnkamalesh, @beego, @devfeel, @labstack, @gofiber, @go-goyave, @go-kratos, @gin-gonic, @roadrunner-server, @zalando, @zeromicro, @nginx and @apache
    2. darkweak · GitHub
    3. Releases · darkweak/souin · GitHub
  • 提高MySQL性能:修改事务隔离级别的最佳实践

    在实际生产环境中,合理选择和调整MySQL的事务隔离级别可以显著提升系统的性能。然而,事务隔离级别的调整需要结合实际业务需求和系统的并发访问情况,因此需要对其特点及适用场景有充分的了解。本文将详细介绍如何通过修改MySQL的事务隔离级别来提高性能,并提供相关的操作方法。

    了解事务隔离级别的特点和适用场景

    MySQL定义了四种常见的事务隔离级别:

    1. 读未提交(Read Uncommitted):允许一个事务读取另一个事务未提交的数据,可能导致脏读问题。不推荐在生产环境中使用。
    2. 读提交(Read Committed):一个事务只能读取已经提交的数据,避免了脏读问题,但可能导致不可重复读问题。适用于大多数场景。
    3. 可重复读(Repeatable Read):一个事务在执行期间多次读取同一数据时,能够保证读取到的结果一致,避免了脏读和不可重复读问题,但可能存在幻读问题。是InnoDB的默认隔离级别。
    4. 串行化(Serializable):最高的隔离级别,强制事务串行执行,避免了脏读、不可重复读和幻读问题,但降低了并发性能。

    评估当前系统的性能瓶颈

    在修改事务隔离级别之前,需要先评估当前系统的性能瓶颈。通过性能监控工具(如MySQL的Explain、Slow Query Log等)来分析系统的性能问题,确定是否由于事务隔离级别不当导致性能瓶颈。

    修改事务隔离级别的方法

    临时修改

    可以在当前会话中临时修改事务隔离级别,使用以下命令:

    SET SESSION TRANSACTION ISOLATION LEVEL <isolation_level>;

    例如,将隔离级别设置为读提交:

    SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

    全局修改

    全局修改事务隔离级别,需要重启MySQL服务才能生效,使用以下命令:

    SET GLOBAL TRANSACTION ISOLATION LEVEL <isolation_level>;

    例如,将隔离级别设置为可重复读:

    SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;

    选择合适的事务隔离级别

    根据实际业务需求和并发访问情况选择合适的事务隔离级别:

    • 读提交(Read Committed):适用于大多数场景,可以避免脏读问题。
    • 可重复读(Repeatable Read):适用于需要保证数据一致性,但不介意幻读问题的场景。
    • 串行化(Serializable):适用于需要最高数据一致性的场景,但要注意可能导致的并发性能问题。

    监控和调优

    在修改事务隔离级别后,需要进行性能监控和调优,确保系统的性能得到提升。可以使用MySQL的性能监控工具,如Explain、Slow Query Log等,来分析和优化查询语句的性能。

    参考资料

    1. MySQL事务隔离级别详解及默认设置_mysql默认事务隔离级别-CSDN博客
    2. MySQL事务隔离级别的最佳实践(上)-阿里云开发者社区
    3. MySQL:修改事务隔离级别和常用语句 – 划水的鱼dm – 博客园

    通过合理选择和调整事务隔离级别,可以在保证数据一致性的同时,提高系统的并发性能。但需要注意的是,事务隔离级别的调整应该根据实际业务需求和并发访问情况进行权衡,以确保系统的稳定和高效运行。


    要查询当前MySQL全局事务隔离级别,可以使用以下SQL命令:

    SELECT @@GLOBAL.transaction_isolation;

    这个命令会返回当前MySQL实例的全局事务隔离级别。以下是一个示例输出:

    mysql> SELECT @@GLOBAL.transaction_isolation;
    +------------------------------+
    | @@GLOBAL.transaction_isolation |
    +------------------------------+
    | REPEATABLE-READ              |
    +------------------------------+
    1 row in set (0.00 sec)

    在这个例子中,MySQL的全局事务隔离级别是“REPEATABLE-READ”。

    另外,你也可以查询当前会话的事务隔离级别,使用以下命令:

    SELECT @@SESSION.transaction_isolation;

    这个命令会返回当前会话的事务隔离级别。

    mysql> SELECT @@SESSION.transaction_isolation;
    +-------------------------------+
    | @@SESSION.transaction_isolation |
    +-------------------------------+
    | REPEATABLE-READ               |
    +-------------------------------+
    1 row in set (0.00 sec)

    在这个例子中,当前会话的事务隔离级别也是“REPEATABLE-READ”。

    通过这些命令,你可以方便地查看MySQL的事务隔离级别设置,以便进行相应的调整和优化。


    什么是幻读问题?

    幻读(Phantom Read)是指在一个事务中,多次执行同一个查询时,结果集的行数可能不一致。具体来说,当一个事务在查询某一范围的数据时,另一个事务在该范围内插入了新的数据,导致前一个事务再次查询时,结果集中出现了“幻影”数据。这种现象通常发生在并发事务处理中,是一种并发一致性问题。

    幻读问题的示例

    假设有一个表 heros_temp,记录了许多英雄的信息。以下是一个简单的示例来说明幻读问题:

    1. 事务A:开始一个事务并查询所有英雄: BEGIN; SELECT * FROM heros_temp WHERE id BETWEEN 1 AND 20; 此时,假设查询结果包含10条记录。
    2. 事务B:在 heros_temp 表中插入一个新的英雄记录,该记录的 id 在事务A查询的范围内: BEGIN; INSERT INTO heros_temp (id, name) VALUES (15, '吕布'); COMMIT;
    3. 事务A:再次执行相同的查询:
      sql SELECT * FROM heros_temp WHERE id BETWEEN 1 AND 20;
      此时,查询结果会包含11条记录,因为事务B插入的新记录也被包含进来。这就产生了幻读现象。

    InnoDB 如何解决幻读问题

    InnoDB存储引擎默认的事务隔离级别是 可重复读(Repeatable Read)。虽然可重复读能够避免脏读(Dirty Read)和不可重复读(Non-repeatable Read)问题,但它不能完全避免幻读问题。

    使用Next-Key Locks

    InnoDB通过使用一种称为 Next-Key Locks 的锁机制来部分解决幻读问题。Next-Key Locks结合了行锁和间隙锁,锁定了索引记录以及索引记录之间的间隙,从而阻止其他事务在锁定范围内插入新记录。

    例如,在上述示例中,当事务A执行查询时,InnoDB会锁定 id 为1到20之间的所有记录,以及这些记录之间的间隙。这意味着在事务A提交之前,事务B无法在 id 为1到20之间插入新的记录,从而避免幻读问题。

    完全避免幻读

    要完全避免幻读,可以将事务隔离级别设置为 串行化(Serializable)。在串行化隔离级别下,事务将按照顺序执行,确保事务之间完全隔离,从而避免所有并发一致性问题,包括幻读。

    SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;

    然而,串行化隔离级别会显著降低系统的并发性能,因此需要根据实际业务需求进行权衡。

    总结

    幻读是并发事务处理中常见的一种一致性问题,指的是一个事务在多次执行相同查询时,结果集中出现了其他事务新插入的记录。InnoDB通过使用Next-Key Locks部分解决了幻读问题,但要完全避免幻读,需要将事务隔离级别设置为串行化。选择合适的事务隔离级别,需要在性能和数据一致性之间进行权衡。


  • 探索 ActivityPub:去中心化社交网络协议

    引言

    随着互联网的不断发展,去中心化的社交网络逐渐成为热点话题。ActivityPub 正是在这种背景下应运而生的,它是一种去中心化的社交网络协议,基于 ActivityStreams 2.0 数据格式。本文将详细介绍 ActivityPub 的工作原理及其实现方式。

    什么是 ActivityPub?

    ActivityPub 是一种去中心化的社交网络协议,旨在通过客户端到服务器和服务器到服务器的 API 实现内容的创建、更新和删除,以及通知和内容的分发。它的主要目标是让去中心化的网站能够共享信息,并让用户通过各种客户端与自己的账户进行互动。

    ActivityPub 的两层协议

    服务器到服务器的联邦协议

    这种联邦协议使得不同的去中心化网站能够共享信息。例如,当一个用户在一个服务器上发布内容时,其他服务器上的用户也能够看到并互动。

    客户端到服务器的协议

    这种协议允许用户(包括真实用户、机器人和其他自动化进程)通过各种客户端(如手机、桌面应用或 Web 应用)与他们在服务器上的账户进行通信。

    用户和演员

    在 ActivityPub 中,用户通过服务器上的账户表示为“演员”。每个演员都有一个收件箱和一个发件箱,用于接收和发送消息。这些都是通过 URL 进行标识的。

    示例

    {
      "@context": "https://www.w3.org/ns/activitystreams",
      "type": "Person",
      "id": "https://social.example/alyssa/",
      "name": "Alyssa P. Hacker",
      "preferredUsername": "alyssa",
      "summary": "Lisp enthusiast hailing from MIT",
      "inbox": "https://social.example/alyssa/inbox/",
      "outbox": "https://social.example/alyssa/outbox/",
      "followers": "https://social.example/alyssa/followers/",
      "following": "https://social.example/alyssa/following/",
      "liked": "https://social.example/alyssa/liked/"
    }

    如何发送和接收消息?

    发送消息

    • POST 到收件箱:将消息发送到某人的收件箱(仅适用于服务器到服务器的通信)。
    • POST 到发件箱:将消息发送到全世界(客户端到服务器)。
    • GET 从发件箱:查看某人发送的消息(客户端到服务器和/或服务器到服务器)。

    接收消息

    • GET 从收件箱:查看最新收到的消息(客户端到服务器)。

    一个完整的示例

    假设 Alyssa 想给她的朋友 Ben 发送一条消息,询问他是否还记得归还一本借来的书。她可以创建一个 ActivityStreams 对象并将其发送到她的发件箱。

    {
      "@context": "https://www.w3.org/ns/activitystreams",
      "type": "Note",
      "to": ["https://chatty.example/ben/"],
      "attributedTo": "https://social.example/alyssa/",
      "content": "Say, did you finish reading that book I lent you?"
    }

    服务器会将这条消息包装在一个 Create 活动中,并发送到 Ben 的收件箱。

    {
      "@context": "https://www.w3.org/ns/activitystreams",
      "type": "Create",
      "id": "https://social.example/alyssa/posts/a29a6843-9feb-4c74-a7f7-081b9c9201d3",
      "to": ["https://chatty.example/ben/"],
      "actor": "https://social.example/alyssa/",
      "object": {
        "type": "Note",
        "id": "https://social.example/alyssa/posts/49e2d03d-b53a-4c4c-a95c-94a6abf45a19",
        "attributedTo": "https://social.example/alyssa/",
        "to": ["https://chatty.example/ben/"],
        "content": "Say, did you finish reading that book I lent you?"
      }
    }

    安全性和验证

    在 ActivityPub 中,服务器应该验证收到的内容,以防止内容欺骗攻击。例如,当服务器接收到一个 Like 活动时,应该验证该活动的 id 是否存在并且是一个有效对象。

    结论

    ActivityPub 通过提供灵活的客户端到服务器和服务器到服务器的协议,使得创建、更新和分发内容变得更加简单和高效。它的去中心化特性也为社交网络带来了更多的自由和可能性。如果您对构建去中心化社交网络感兴趣,ActivityPub 将是一个非常有价值的工具。

    希望这篇文章能帮助您更好地理解 ActivityPub 及其在去中心化社交网络中的重要作用。

  • WordPress插件的安全性

    在WordPress插件开发的过程中,安全性是一个至关重要的方面。插件代码可能会在数百万个WordPress站点上运行,因此确保代码的安全性是开发者不可忽视的责任。在《WordPress插件开发教程手册》的插件安全部分中,我们可以学习到如何检查用户能力、验证输入和清理输出等安全措施。

    检查用户能力

    为了确保插件的安全性,首先需要检查用户的能力。如果插件允许用户提交数据(无论是管理员还是游客),一定要检查用户权限。WordPress以用户角色和能力的形式提供了这个能力系统。比如,管理员角色会拥有“manage_options”的能力,这让他们有权限查看、编辑、保存网站选项,而其他角色则没有这个权限。

    示例代码

    以下代码在前端创建了一个删除文章的链接,但没有检查用户能力,导致所有访问网站的用户都可以使用这个链接删除文章:

    function wporg_generate_delete_link($content)
    {
        if (is_single() && in_the_loop() && is_main_query()) {
            $url = add_query_arg(
                [
                    'action' => 'wporg_frontend_delete',
                    'post'   => get_the_ID(),
                ],
                home_url()
            );
            return $content . ' <a href=' . esc_url($url) . '>' . esc_html__('Delete Post', 'wporg') . '</a>';
        }
        return null;
    }
    
    function wporg_delete_post()
    {
        if (isset($_GET['action']) && $_GET['action'] === 'wporg_frontend_delete') {
            $post_id = (isset($_GET['post'])) ? ($_GET['post']) : (null);
            $post = get_post((int)$post_id);
            if (empty($post)) {
                return;
            }
            wp_trash_post($post_id);
            $redirect = admin_url('edit.php');
            wp_safe_redirect($redirect);
            die;
        }
    }
    
    add_filter('the_content', 'wporg_generate_delete_link');
    add_action('init', 'wporg_delete_post');

    为了确保只有拥有编辑能力的用户可以看到这个链接并删除文章,我们需要在显示链接时检查用户是否拥有“edit_others_posts”能力:

    if (current_user_can('edit_others_posts')) {
        add_filter('the_content', 'wporg_generate_delete_link');
        add_action('init', 'wporg_delete_post');
    }

    数据验证

    数据验证是确保输入数据符合预期的一项重要措施。它可以防止恶意数据的输入和潜在的安全漏洞。数据验证应尽早执行,可以在前端使用JavaScript进行初步验证,但绝不能代替后端的PHP验证。

    常见的验证方法

    1. 必填字段检查: 确保输入的必填字段不为空。
    2. 格式检查: 确保输入的数据符合预期的格式,比如电话号码、邮编、Email等。
    3. 范围检查: 确保输入数据在预期的范围内,比如数量字段要大于0。

    在WordPress中,我们可以使用多种方法来验证数据,包括PHP内置函数、WordPress核心函数以及自定义函数。以下是一些常用的PHP内置函数:

    • isset()empty():检查变量是否存在或为空。
    • mb_strlen()strlen():检查字符串长度。
    • preg_match():检查字符串是否匹配指定的正则表达式。

    通过这些方法,我们可以有效地确保数据的安全性,防止潜在的安全漏洞。

    结论

    确保WordPress插件的安全性不仅是对用户负责,也是对自己的代码负责。通过检查用户能力和验证输入数据,可以大大减少安全风险,提升插件的可靠性和安全性。希望每一位开发者都能在开发过程中重视安全性,为用户提供安全可靠的插件。

  • WordPress 插件开发

    学习 WordPress 插件开发是一个非常有益且实用的技能,可以帮助你扩展和定制 WordPress 网站的功能。以下是一个详细的指南,帮助你从零开始学习 WordPress 插件开发。

    1. 基础知识准备

    在开始插件开发之前,你需要具备一些基本知识:

    • HTML/CSS:了解基本的网页结构和样式。
    • JavaScript:掌握基本的编程逻辑和DOM操作。
    • PHP:WordPress 插件主要使用 PHP 编写,需要了解 PHP 的基本语法和功能。
    • MySQL:了解基本的数据库操作,因为 WordPress 使用 MySQL 作为数据库。

    2. 设置开发环境

    为插件开发设置一个合适的开发环境:

    • 本地服务器:使用工具如 XAMPP、MAMP 或 Local by Flywheel 来设置一个本地开发服务器。
    • 文本编辑器或 IDE:选择一个代码编辑器,如 Visual Studio Code、Sublime Text 或 PHPStorm。
    • WordPress 安装:在本地服务器上安装最新版本的 WordPress。

    3. 了解 WordPress 插件结构

    一个简单的 WordPress 插件通常包含以下文件和文件夹:

    • 插件主文件:通常命名为plugin-name.php,包含插件的主要功能代码。
    • README 文件:包含插件的基本信息和说明。
    • 其他文件:如 CSS、JavaScript、图片等资源文件。

    4. 创建第一个插件

    下面是创建一个简单插件的步骤:

    4.1 创建插件目录和主文件

    wp-content/plugins 目录下创建一个新文件夹,例如 my-first-plugin。在该文件夹中创建一个 PHP 文件,例如 my-first-plugin.php

    4.2 插件头部信息

    my-first-plugin.php 文件中添加插件的头部信息:

    <?php
    /*
    Plugin Name: My First Plugin
    Plugin URI: https://example.com/my-first-plugin
    Description: This is my first WordPress plugin.
    Version: 1.0
    Author: Your Name
    Author URI: https://example.com
    License: GPL2
    */

    4.3 插件的基本功能

    添加一些基本功能,如在 WordPress 后台显示一个简单的信息:

    function my_first_plugin_admin_notice() {
        echo '<div class="notice notice-success is-dismissible">
            <p>My First Plugin is activated!</p>
        </div>';
    }
    add_action('admin_notices', 'my_first_plugin_admin_notice');

    5. 学习 WordPress 钩子(Hooks)

    WordPress 插件开发的核心是钩子(Hooks),它包括动作钩子(Action Hooks)和过滤器钩子(Filter Hooks)。通过钩子,你可以在特定的时间点执行代码:

    • 动作钩子(Actions):允许你在 WordPress 的执行过程中插入自定义代码。
    • 过滤器钩子(Filters):允许你修改 WordPress 数据或输出。

    例子:

    // 动作钩子
    add_action('wp_footer', 'my_first_plugin_footer_message');
    function my_first_plugin_footer_message() {
        echo '<p>Thank you for visiting my website!</p>';
    }
    
    // 过滤器钩子
    add_filter('the_content', 'my_first_plugin_content_filter');
    function my_first_plugin_content_filter($content) {
        return $content . '<p>Custom content added by My First Plugin.</p>';
    }

    6. 学习 WordPress 插件 API

    WordPress 提供了丰富的 API 供插件开发者使用:

    • Settings API:管理插件设置页面。
    • Shortcode API:创建短代码。
    • Widgets API:创建自定义小工具。
    • REST API:创建自定义 REST 端点。

    7. 学习插件国际化和本地化

    为了使你的插件可以被翻译成不同的语言,了解如何国际化和本地化插件是很重要的:

    // 加载插件文本域
    function my_first_plugin_load_textdomain() {
        load_plugin_textdomain( 'my-first-plugin', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' );
    }
    add_action('plugins_loaded', 'my_first_plugin_load_textdomain');

    8. 探索插件开发资源

    • 官方文档:阅读 WordPress 插件开发手册
    • 教程和书籍:查找相关的在线教程和书籍,如《Professional WordPress Plugin Development》。

    9. 配置插件设置页面

    为插件添加一个设置页面是常见的需求。以下是如何使用 Settings API 创建一个简单的插件设置页面的步骤:

    9.1 添加菜单项

    首先,需要在 WordPress 管理菜单中添加一个新的菜单项:

    function my_first_plugin_menu() {
        add_menu_page(
            'My First Plugin Settings', // 页面标题
            'My First Plugin', // 菜单标题
            'manage_options', // 用户权限
            'my-first-plugin', // 菜单别名
            'my_first_plugin_settings_page', // 回调函数
            'dashicons-admin-generic' // 图标
        );
    }
    add_action('admin_menu', 'my_first_plugin_menu');

    9.2 创建设置页面

    在回调函数 my_first_plugin_settings_page 中定义设置页面的内容:

    function my_first_plugin_settings_page() {
        ?>
        <div class="wrap">
            <h1>My First Plugin Settings</h1>
            <form method="post" action="options.php">
                <?php
                settings_fields('my_first_plugin_options_group');
                do_settings_sections('my-first-plugin');
                submit_button();
                ?>
            </form>
        </div>
        <?php
    }

    9.3 注册设置

    使用 Settings API 注册插件设置:

    function my_first_plugin_settings_init() {
        register_setting('my_first_plugin_options_group', 'my_first_plugin_option_name');
    
        add_settings_section(
            'my_first_plugin_settings_section', // ID
            'My First Plugin Settings', // 标题
            'my_first_plugin_settings_section_callback', // 回调函数
            'my-first-plugin' // 页面
        );
    
        add_settings_field(
            'my_first_plugin_field', // ID
            'Sample Field', // 标签
            'my_first_plugin_field_callback', // 回调函数
            'my-first-plugin', // 页面
            'my_first_plugin_settings_section' // 部分
        );
    }
    add_action('admin_init', 'my_first_plugin_settings_init');
    
    function my_first_plugin_settings_section_callback() {
        echo 'Enter your settings below:';
    }
    
    function my_first_plugin_field_callback() {
        $option = get_option('my_first_plugin_option_name');
        echo '<input type="text" name="my_first_plugin_option_name" value="' . esc_attr($option) . '" />';
    }

    10. 创建短代码

    短代码允许用户在文章和页面中插入自定义内容。以下是如何创建一个简单的短代码:

    function my_first_plugin_shortcode($atts) {
        $atts = shortcode_atts(
            array(
                'text' => 'Hello, World!',
            ),
            $atts,
            'my_shortcode'
        );
    
        return '<div class="my-shortcode">' . esc_html($atts['text']) . '</div>';
    }
    add_shortcode('my_shortcode', 'my_first_plugin_shortcode');

    11. 创建小工具

    小工具可以在 WordPress 侧边栏等小工具区域显示内容。以下是如何创建一个简单的小工具:

    “`php
    class My_First_Plugin_Widget extends WP_Widget {
    function construct() { parent::_construct( ‘my_first_plugin_widget’, // ID (‘My First Plugin Widget’, ‘text_domain’), // 名称
    array(‘description’ => _
    (‘A simple widget’, ‘text_domain’)) // 描述
    );
    }

    public function widget($args, $instance) {
        echo $args['before_widget'];
        echo $args['before_title'] . apply_filters('widget_title', $instance['title']) . $args['after_title'];
        echo '<p>' . esc_html($instance['text']) . '</p>';
        echo $args['after_widget'];
    }
    
    public function form($instance) {
        $title = !empty($instance['title']) ? $instance['title'] : __('New title', 'text_domain');
        $text = !empty($instance['text']) ? $instance['text'] : __('Hello, World!', 'text_domain');
        ?>
        <p>
            <label for="<?php echo esc_attr($this->get_field_id('title')); ?>"><?php _e('Title:'); ?></label>
            <input class="widefat" id="<?php echo esc_attr($this->get_field_id('title')); ?>" name="<?php echo esc_attr($this->get_field_name('title')); ?>" type="text" value="<?php echo esc_attr($title); ?>">
        </p>
        <p>
            <label for="<?php echo esc_attr($this->get_field_id('text')); ?>"><?php _e('Text:'); ?></label>
    
    

    11. 创建小工具(续)

    11.1 完成小工具的表单和更新方法

    在上面的代码基础上,继续完成小工具的表单和更新方法:

            <textarea class="widefat" id="<?php echo esc_attr($this->get_field_id('text')); ?>" name="<?php echo esc_attr($this->get_field_name('text')); ?>"><?php echo esc_attr($text); ?></textarea>
            </p>
            <?php
        }
    
        public function update($new_instance, $old_instance) {
            $instance = array();
            $instance['title'] = (!empty($new_instance['title'])) ? strip_tags($new_instance['title']) : '';
            $instance['text'] = (!empty($new_instance['text'])) ? strip_tags($new_instance['text']) : '';
    
            return $instance;
        }
    }
    
    // 注册小工具
    function register_my_first_plugin_widget() {
        register_widget('My_First_Plugin_Widget');
    }
    add_action('widgets_init', 'register_my_first_plugin_widget');

    12. 使用 WordPress REST API

    WordPress REST API 允许你创建和访问自定义端点,从而使你的插件能够与外部应用程序进行通信。

    12.1 创建自定义 REST 端点

    以下是如何创建一个简单的自定义 REST 端点:

    function my_first_plugin_register_routes() {
        register_rest_route('my-first-plugin/v1', '/data/', array(
            'methods' => 'GET',
            'callback' => 'my_first_plugin_get_data',
        ));
    }
    add_action('rest_api_init', 'my_first_plugin_register_routes');
    
    function my_first_plugin_get_data($request) {
        return new WP_REST_Response(array('message' => 'Hello, World!'), 200);
    }

    13. 安全性和最佳实践

    确保你的插件是安全的,并遵循最佳实践:

    13.1 数据验证和清理

    在处理用户输入时,确保对数据进行验证和清理:

    // 验证和清理输入数据
    function my_first_plugin_validate_data($data) {
        return sanitize_text_field($data);
    }

    13.2 使用非ces (Nonces) 进行安全验证

    在处理表单提交时,使用非ces 来防止跨站请求伪造 (CSRF) 攻击:

    // 创建一个 nonce
    function my_first_plugin_create_nonce() {
        return wp_create_nonce('my_first_plugin_nonce_action');
    }
    
    // 验证 nonce
    function my_first_plugin_verify_nonce($nonce) {
        return wp_verify_nonce($nonce, 'my_first_plugin_nonce_action');
    }

    13.3 避免直接文件访问

    在插件的每个文件顶部添加以下代码,防止直接访问:

    if (!defined('ABSPATH')) {
        exit; // 退出如果直接访问
    }

    14. 插件国际化和本地化(续)

    确保你的插件可以被翻译成不同的语言:

    14.1 创建语言文件

    在插件目录下创建一个 languages 文件夹,并使用 .pot 文件保存插件的翻译字符串。例如,创建一个 my-first-plugin.pot 文件。

    14.2 加载语言文件

    在插件初始化时加载语言文件:

    function my_first_plugin_load_textdomain() {
        load_plugin_textdomain('my-first-plugin', false, dirname(plugin_basename(__FILE__)) . '/languages');
    }
    add_action('plugins_loaded', 'my_first_plugin_load_textdomain');

    15. 发布和分发插件

    当你完成插件开发后,可以考虑将插件发布到 WordPress 插件目录中。

    15.1 准备插件

    确保你的插件包含以下文件:

    • 主插件文件:包含插件头部信息和主要功能代码。
    • README 文件:详细描述插件的功能、安装方法和使用说明。
    • 语言文件:包含 .pot 文件和翻译文件。
    • 其他资源文件:如 CSS、JavaScript、图片等资源文件。

    15.2 创建 README 文件

    使用 Markdown 编写 README 文件,并确保它包含以下部分:

    • Plugin Name: 插件名称
    • Description: 插件描述
    • Installation: 安装说明
    • Frequently Asked Questions: 常见问题解答
    • Changelog: 更新日志

    15.3 提交插件

    访问 WordPress 插件提交页面 并按照指南提交你的插件。一旦审核通过,你的插件将出现在 WordPress 插件目录中。

    16. 进一步学习资源

    要深入学习 WordPress 插件开发,以下资源非常有用:

    16.1 官方文档

    16.2 在线课程和教程

    • Udemy: 提供了许多关于 WordPress 插件开发的在线课程。
    • Lynda/LinkedIn Learning: 提供了高质量的 WordPress 开发视频教程。

    16.3 开发者社区

    16.4 博客和文章

    • Tuts+: 提供了大量关于 WordPress 开发的文章和教程。
    • Smashing Magazine: 定期发布关于 WordPress 的开发技巧和最佳实践的文章。

    17. 高级插件开发技巧

    17.1 面向对象编程 (OOP)

    使用面向对象编程风格可以使你的插件代码更具可维护性和可扩展性。以下是一个简单的 OOP 插件示例:

    class My_First_Plugin {
        public function __construct() {
            add_action('init', array($this, 'init'));
        }
    
        public function init() {
            // 初始化代码
        }
    
        public function register_post_type() {
            $args = array(
                'public' => true,
                'label'  => 'Custom Post',
            );
            register_post_type('custom_post', $args);
        }
    }
    
    $my_first_plugin = new My_First_Plugin();

    17.2 使用命名空间

    使用命名空间可以避免类名和函数名冲突:

    namespace MyFirstPlugin;
    
    class Main {
        public function __construct() {
            add_action('init', array($this, 'init'));
        }
    
        public function init() {
            // 初始化代码
        }
    
        public function register_post_type() {
            $args = array(
                'public' => true,
                'label'  => 'Custom Post',
            );
            register_post_type('custom_post', $args);
        }
    }
    
    $main = new Main();

    17.3 自动加载

    使用自动加载器可以简化类文件的加载:

    spl_autoload_register(function ($class) {
        $prefix = 'MyFirstPlugin\\';
        $base_dir = __DIR__ . '/inc/';
    
        $len = strlen($prefix);
        if (strncmp($prefix, $class, $len) !== 0) {
            return;
        }
    
        $relative_class = substr($class, $len);
        $file = $base_dir . str_replace('\\', '/', $relative_class) . '.php';
    
        if (file_exists($file)) {
            require $file;
        }
    });

    17.4 单例模式

    使用单例模式确保插件的主类只实例化一次:

    class My_First_Plugin {
        private static $instance = null;
    
        private function __construct() {
            add_action('init', array($this, 'init'));
        }
    
        public static function get_instance() {
            if (self::$instance == null) {
                self::$instance = new self();
            }
            return self::$instance;
        }
    
        public function init() {
            // 初始化代码
        }
    }
    
    My_First_Plugin::get_instance();

    18. 性能优化

    确保你的插件不会对网站性能产生负面影响:

    18.1 使用缓存

    利用 WordPress 内置的缓存功能:

    function get_cached_data() {
        $cache_key = 'my_first_plugin_cached_data';
        $data = wp_cache_get($cache_key);
    
        if ($data === false) {
            // 获取数据
            $data = '...';
            wp_cache_set($cache_key, $data, '', 3600);
        }
    
        return $data;
    }

    19. 数据库操作

    19.1 创建自定义数据库表

    有时候,插件需要创建自定义数据库表来存储特定的数据。以下是创建自定义表的示例:

    function my_first_plugin_create_table() {
        global $wpdb;
        $table_name = $wpdb->prefix . 'my_first_plugin_table';
    
        $charset_collate = $wpdb->get_charset_collate();
    
        $sql = "CREATE TABLE $table_name (
            id mediumint(9) NOT NULL AUTO_INCREMENT,
            name tinytext NOT NULL,
            email text NOT NULL,
            date datetime DEFAULT '0000-00-00 00:00:00' NOT NULL,
            PRIMARY KEY  (id)
        ) $charset_collate;";
    
        require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
        dbDelta($sql);
    }
    
    register_activation_hook(__FILE__, 'my_first_plugin_create_table');

    19.2 插入数据

    function my_first_plugin_insert_data($name, $email) {
        global $wpdb;
        $table_name = $wpdb->prefix . 'my_first_plugin_table';
    
        $wpdb->insert(
            $table_name,
            array(
                'name' => $name,
                'email' => $email,
                'date' => current_time('mysql'),
            )
        );
    }

    19.3 获取数据

    function my_first_plugin_get_data() {
        global $wpdb;
        $table_name = $wpdb->prefix . 'my_first_plugin_table';
    
        $results = $wpdb->get_results("SELECT * FROM $table_name", ARRAY_A);
        return $results;
    }

    20. AJAX 操作

    使用 AJAX 可以在不刷新页面的情况下与服务器进行交互。

    20.1 在前端调用 AJAX

    jQuery(document).ready(function($) {
        $('#my-button').click(function() {
            $.ajax({
                url: my_first_plugin_ajax_object.ajax_url,
                type: 'POST',
                data: {
                    action: 'my_first_plugin_action',
                    data: 'some_data'
                },
                success: function(response) {
                    console.log(response);
                }
            });
        });
    });

    20.2 在后端处理 AJAX 请求

    function my_first_plugin_ajax_handler() {
        $data = $_POST['data'];
    
        // 处理数据
        $response = array('message' => 'Success', 'data' => $data);
    
        wp_send_json_success($response);
    }
    
    add_action('wp_ajax_my_first_plugin_action', 'my_first_plugin_ajax_handler');
    add_action('wp_ajax_nopriv_my_first_plugin_action', 'my_first_plugin_ajax_handler');

    21. 使用自定义钩子和过滤器

    创建自定义钩子和过滤器,使其他开发者可以扩展你的插件功能。

    21.1 创建自定义动作钩子

    function my_first_plugin_do_custom_action() {
        do_action('my_first_plugin_custom_action');
    }
    
    function my_first_plugin_add_custom_action() {
        echo 'Custom Action Triggered!';
    }
    
    add_action('my_first_plugin_custom_action', 'my_first_plugin_add_custom_action');

    21.2 创建自定义过滤器

    function my_first_plugin_apply_custom_filter($content) {
        return apply_filters('my_first_plugin_custom_filter', $content);
    }
    
    function my_first_plugin_modify_content($content) {
        return $content . ' - Modified by custom filter';
    }
    
    add_filter('my_first_plugin_custom_filter', 'my_first_plugin_modify_content');

    22. 使用第三方库

    有时,你可能需要在插件中使用第三方库。你可以通过 Composer 来管理依赖关系。

    22.1 安装 Composer

    确保在你的开发环境中安装了 Composer

    22.2 创建 composer.json 文件

    在插件根目录下创建一个 composer.json 文件:

    {
        "name": "yourname/yourplugin",
        "description": "A description of your plugin",
        "require": {
            "some/package": "^1.0"
        }
    }

    22.3 安装依赖

    在终端中运行以下命令:

    composer install

    22.4 在插件中加载 Composer 自动加载器

    在插件的主文件中添加以下代码:

    require_once __DIR__ . '/vendor/autoload.php';

    23. 测试和调试(续)

    23.1 使用 WP_DEBUG

    wp-config.php 文件中启用调试模式:

    define('WP_DEBUG', true);
    define('WP_DEBUG_LOG', true);
    define('WP_DEBUG_DISPLAY', false);
    @ini_set('display_errors', 0);

    此设置会将错误记录到 wp-content/debug.log 文件中,而不是直接显示在页面上。

    23.2 使用 PHPStorm 和 Xdebug 进行调试

    1. 安装 Xdebug: 根据你的 PHP 版本和操作系统安装 Xdebug。
    2. 配置 php.ini: 添加以下行到你的 php.ini 文件: zend_extension="path/to/xdebug.so" xdebug.remote_enable=1 xdebug.remote_host=127.0.0.1 xdebug.remote_port=9000 xdebug.remote_handler=dbgp
    3. 配置 PHPStorm: 在 PHPStorm 中配置 Xdebug 远程调试。在 “Preferences” -> “Languages & Frameworks” -> “PHP” -> “Debug” 中设置 Xdebug 端口为 9000。
    4. 设置断点: 在 PHPStorm 中打开你的插件代码并设置断点。
    5. 启动调试: 启动 PHPStorm 的调试监听,然后在浏览器中访问你的插件页面。代码将在断点处暂停,你可以逐步调试。

    24. 国际化与本地化

    24.1 准备插件以进行翻译

    使用 __()_e() 函数准备你的插件代码:

    echo __('Hello, World!', 'my-first-plugin');
    _e('Hello, World!', 'my-first-plugin');

    24.2 创建 POT 文件

    使用 xgettext 或 Poedit 等工具生成 POT 文件:

    xgettext --from-code=UTF-8 -k__ -k_e -o languages/my-first-plugin.pot $(find . -name "*.php")

    24.3 加载翻译文件

    在插件初始化时加载翻译文件:

    function my_first_plugin_load_textdomain() {
        load_plugin_textdomain('my-first-plugin', false, dirname(plugin_basename(__FILE__)) . '/languages');
    }
    add_action('plugins_loaded', 'my_first_plugin_load_textdomain');

    25. 安全性

    25.1 数据验证和清理

    确保所有用户输入的数据都经过验证和清理:

    $cleaned_data = sanitize_text_field($_POST['data']);

    25.2 权限检查

    在处理敏感操作之前检查用户权限:

    if (!current_user_can('manage_options')) {
        wp_die(__('You do not have sufficient permissions to access this page.', 'my-first-plugin'));
    }

    25.3 防止 SQL 注入

    使用 $wpdb->prepare() 函数来防止 SQL 注入:

    global $wpdb;
    $sql = $wpdb->prepare("SELECT * FROM {$wpdb->prefix}my_table WHERE id = %d", $id);
    $results = $wpdb->get_results($sql);

    26. 创建设置页面

    26.1 添加设置页面

    function my_first_plugin_add_admin_menu() {
        add_options_page(
            __('My First Plugin Settings', 'my-first-plugin'),
            __('My First Plugin', 'my-first-plugin'),
            'manage_options',
            'my-first-plugin',
            'my_first_plugin_options_page'
        );
    }
    add_action('admin_menu', 'my_first_plugin_add_admin_menu');

    26.2 创建设置页面内容

    function my_first_plugin_options_page() {
        ?>
        <div class="wrap">
            <h1><?php _e('My First Plugin Settings', 'my-first-plugin'); ?></h1>
            <form action="options.php" method="post">
                <?php
                settings_fields('my_first_plugin_options');
                do_settings_sections('my-first-plugin');
                submit_button();
                ?>
            </form>
        </div>
        <?php
    }

    26.3 注册设置

    “`php
    function my_first_plugin_settings_init() {
    register_setting(‘my_first_plugin_options’, ‘my_first_plugin_options’);

    add_settings_section(
        'my_first_plugin_section',
        __('General Settings', 'my-first-plugin'),
        'my_first_plugin_section_callback',
        'my-first-plugin'
    );
    
    add_settings_field(
        'my_first_plugin_field_text',
        __('Sample Text Field', 'my-first-plugin'),
        'my_first_plugin_field_text_callback',
        '

    26. 创建设置页面(续)

    26.3 注册设置(续)

        add_settings_field(
            'my_first_plugin_field_text',
            __('Sample Text Field', 'my-first-plugin'),
            'my_first_plugin_field_text_callback',
            'my-first-plugin',
            'my_first_plugin_section'
        );
    }
    
    add_action('admin_init', 'my_first_plugin_settings_init');
    
    function my_first_plugin_section_callback() {
        echo __('Enter your settings below:', 'my-first-plugin');
    }
    
    function my_first_plugin_field_text_callback() {
        $options = get_option('my_first_plugin_options');
        ?>
        <input type="text" name="my_first_plugin_options[my_first_plugin_field_text]" value="<?php echo isset($options['my_first_plugin_field_text']) ? esc_attr($options['my_first_plugin_field_text']) : ''; ?>">
        <?php
    }

    27. 创建自定义小工具

    27.1 定义小工具类

    class My_First_Plugin_Widget extends WP_Widget {
        public function __construct() {
            parent::__construct(
                'my_first_plugin_widget', // Base ID
                __('My First Plugin Widget', 'my-first-plugin'), // Name
                array('description' => __('A sample widget for showing text', 'my-first-plugin')) // Args
            );
        }
    
        public function widget($args, $instance) {
            echo $args['before_widget'];
            if (!empty($instance['title'])) {
                echo $args['before_title'] . apply_filters('widget_title', $instance['title']) . $args['after_title'];
            }
            echo __('Hello, World!', 'my-first-plugin');
            echo $args['after_widget'];
        }
    
        public function form($instance) {
            $title = !empty($instance['title']) ? $instance['title'] : __('New title', 'my-first-plugin');
            ?>
            <p>
                <label for="<?php echo $this->get_field_id('title'); ?>"><?php _e('Title:', 'my-first-plugin'); ?></label>
                <input class="widefat" id="<?php echo $this->get_field_id('title'); ?>" name="<?php echo $this->get_field_name('title'); ?>" type="text" value="<?php echo esc_attr($title); ?>">
            </p>
            <?php
        }
    
        public function update($new_instance, $old_instance) {
            $instance = array();
            $instance['title'] = (!empty($new_instance['title'])) ? strip_tags($new_instance['title']) : '';
            return $instance;
        }
    }

    27.2 注册小工具

    function my_first_plugin_register_widgets() {
        register_widget('My_First_Plugin_Widget');
    }
    
    add_action('widgets_init', 'my_first_plugin_register_widgets');

    28. 使用 REST API

    28.1 创建自定义 REST API 路由

    function my_first_plugin_register_rest_route() {
        register_rest_route('my-first-plugin/v1', '/data/', array(
            'methods' => 'GET',
            'callback' => 'my_first_plugin_get_data',
        ));
    }
    
    add_action('rest_api_init', 'my_first_plugin_register_rest_route');
    
    function my_first_plugin_get_data($request) {
        $data = array('message' => 'Hello, World!');
        return new WP_REST_Response($data, 200);
    }

    28.2 使用 REST API

    在前端使用 JavaScript 访问自定义 REST API 路由:

    fetch('/wp-json/my-first-plugin/v1/data/')
        .then(response => response.json())
        .then(data => console.log(data))
        .catch(error => console.error('Error:', error));

    29. 自定义短代码

    29.1 注册短代码

    function my_first_plugin_shortcode($atts = [], $content = null) {
        $atts = array_change_key_case((array)$atts, CASE_LOWER);
        $a = shortcode_atts([
            'title' => 'Default Title',
        ], $atts);
    
        return '<div class="my-first-plugin">' . esc_html($a['title']) . '</div>';
    }
    
    add_shortcode('my_first_plugin', 'my_first_plugin_shortcode');

    29.2 使用短代码

    在文章或页面中使用短代码:

    [my_first_plugin title="Hello, World!"]

    30. 单元测试(续)

    30.1 设置 PHPUnit(续)

    tests 目录中创建 bootstrap.php 文件以初始化测试环境:

    <?php
    $_tests_dir = getenv('WP_TESTS_DIR');
    
    if (!$_tests_dir) {
        $_tests_dir = rtrim(sys_get_temp_dir(), '/\\') . '/wordpress-tests-lib';
    }
    
    require_once $_tests_dir . '/includes/functions.php';
    
    function _manually_load_plugin() {
        require dirname(__DIR__) . '/my-first-plugin.php';
    }
    
    tests_add_filter('muplugins_loaded', '_manually_load_plugin');
    
    require $_tests_dir . '/includes/bootstrap.php';

    tests 目录中创建 test-sample.php 文件以编写第一个测试:

    <?php
    class SampleTest extends WP_UnitTestCase {
    
        function test_sample() {
            $this->assertTrue(true);
        }
    }

    30.2 运行测试

    使用以下命令运行 PHPUnit 测试:

    phpunit --bootstrap tests/bootstrap.php tests/test-sample.php

    31. 使用自定义表单

    31.1 创建自定义表单

    在插件中使用 HTML 和 PHP 创建自定义表单:

    function my_first_plugin_form() {
        ?>
        <form action="<?php echo esc_url(admin_url('admin-post.php')); ?>" method="post">
            <input type="hidden" name="action" value="my_first_plugin_form_action">
            <?php wp_nonce_field('my_first_plugin_form_nonce', 'my_first_plugin_nonce'); ?>
            <label for="name"><?php _e('Name:', 'my-first-plugin'); ?></label>
            <input type="text" name="name" id="name" required>
            <input type="submit" value="<?php _e('Submit', 'my-first-plugin'); ?>">
        </form>
        <?php
    }

    31.2 处理表单提交

    在插件中处理表单提交:

    function my_first_plugin_form_handler() {
        if (!isset($_POST['my_first_plugin_nonce']) || !wp_verify_nonce($_POST['my_first_plugin_nonce'], 'my_first_plugin_form_nonce')) {
            wp_die(__('Nonce verification failed', 'my-first-plugin'));
        }
    
        if (!current_user_can('manage_options')) {
            wp_die(__('You do not have sufficient permissions to access this page.', 'my-first-plugin'));
        }
    
        $name = sanitize_text_field($_POST['name']);
        // 处理表单数据,例如保存到数据库
        wp_redirect(admin_url('options-general.php?page=my-first-plugin&message=success'));
        exit;
    }
    
    add_action('admin_post_my_first_plugin_form_action', 'my_first_plugin_form_handler');

    32. 使用 AJAX

    32.1 注册 AJAX 动作

    function my_first_plugin_ajax_handler() {
        check_ajax_referer('my_first_plugin_nonce', 'security');
    
        if (!current_user_can('manage_options')) {
            wp_send_json_error(__('You do not have sufficient permissions to access this page.', 'my-first-plugin'));
        }
    
        $response = array('message' => __('Hello, World!', 'my-first-plugin'));
        wp_send_json_success($response);
    }
    
    add_action('wp_ajax_my_first_plugin_action', 'my_first_plugin_ajax_handler');

    32.2 在前端使用 AJAX

    在前端脚本中使用 AJAX 进行请求:

    jQuery(document).ready(function($) {
        $('#my-button').on('click', function() {
            $.ajax({
                url: ajaxurl,
                type: 'POST',
                data: {
                    action: 'my_first_plugin_action',
                    security: my_first_plugin_vars.nonce
                },
                success: function(response) {
                    if (response.success) {
                        alert(response.data.message);
                    } else {
                        alert('Error: ' + response.data);
                    }
                }
            });
        });
    });

    在插件中注册和本地化脚本:

    function my_first_plugin_enqueue_scripts() {
        wp_enqueue_script('my-first-plugin-script', plugins_url('js/my-first-plugin.js', __FILE__), array('jquery'), null, true);
        wp_localize_script('my-first-plugin-script', 'my_first_plugin_vars', array(
            'nonce' => wp_create_nonce('my_first_plugin_nonce')
        ));
    }
    
    add_action('admin_enqueue_scripts', 'my_first_plugin_enqueue_scripts');

    33. 创建自定义数据库表

    33.1 激活时创建表

    在插件激活时创建自定义数据库表:

    function my_first_plugin_create_db_table() {
        global $wpdb;
        $table_name = $wpdb->prefix . 'my_first_plugin_table';
    
        $charset_collate = $wpdb->get_charset_collate();
    
        $sql = "CREATE TABLE $table_name (
            id mediumint(9) NOT NULL AUTO_INCREMENT,
            name tinytext NOT NULL,
            value text NOT NULL,
            PRIMARY KEY  (id)
        ) $charset_collate;";
    
        require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
        dbDelta($sql);
    }
    
    register_activation_hook(__FILE__, 'my_first_plugin_create_db_table');

    33.2 插入数据

    在插件中插入数据到自定义表:

    function my_first_plugin_insert_data($name, $value) {
        global $wpdb;
        $table_name = $wpdb->prefix . 'my_first_plugin_table';
    
        $wpdb->insert(
            $table_name,
            array(
                'name' => $name,
                'value' => $value,
            )
        );
    }

    33.3 查询数据

    从自定义表中查询数据:

    function my_first_plugin_get_data() {
        global $wpdb;
        $table_name = $wpdb->prefix . 'my_first_plugin_table';
    
        $results = $wpdb->get_results("SELECT * FROM $table_name", OBJECT);
    
        return $results;
    }

    34. 本地化

    34.1 加载插件的翻译文件

    在插件中加载翻译文件:

    function my_first_plugin_load_textdomain() {
        load_plugin_textdomain('my-first-plugin', false, dirname(plugin_basename(__FILE__)) . '/languages');
    }
    
    add_action('plugins_loaded', 'my_first_plugin_load_textdomain');

    34.2 创建翻译文件

    使用 Poedit 或其他翻译工具创建 .po.mo 文件,并将它们放在 languages 目录中。例如,创建 my-first-plugin-zh_CN.pomy-first-plugin-zh_CN.mo 文件。

    35. 安全性

    35.1 数据验证和清理

    在处理用户输入时,确保数据的验证和清理:

    $name = sanitize_text_field($_POST['name']);

    35.2 非可信数据的输出

    在输出非可信数据时,使用适当的 WordPress 函数进行转义:

    echo esc_html($name);

    35.3 使用非ces

    使用 Nonces 来验证请求的合法性:

    if (!isset($_POST['my_first_plugin_nonce']) || !wp_verify_nonce($_POST['my_first_plugin_nonce'], 'my_first_plugin_form_nonce')) {
        wp_die(__('Nonce verification failed', 'my-first-plugin'));
    }

    36. 优化性能

    36.1 减少数据库查询

    缓存频繁使用的数据以减少数据库查询次数:

    $data = wp_cache_get('my_first_plugin_data');
    
    if ($data === false) {
        $data = my_first_plugin_get_data();
        wp_cache_set('my_first_plugin_data', $data);
    }

    36.2 异步处理

    使用异步方式处理耗时操作:

    function my_first_plugin_enqueue_async_script($url) {
        if (strpos($url, '#async') === false) {
            return $url;
        } else if (is_admin()) {
            return str_replace('#async', '', $url);
        } else {
            return str_replace('#async', '', $url) . "' async='async";
        }
    }
    
    add_filter('clean_url', 'my_first_plugin_enqueue_async_script', 11, 1);

    37. 与第三方服务集成

    37.1 使用 API 密钥

    存储和使用 API 密钥:

    $api_key = get_option('my_first_plugin_api_key');
    $response = wp_remote_get("https://api.example.com/data?api_key={$api_key}");

    37.2 处理 API 响应

    处理第三方 API 的响应:

    if (is_wp_error($response)) {
        $error_message = $response->get_error_message();
        echo "Something went wrong: $error_message";
    } else {
        $body = wp_remote_retrieve_body($response);
        $data = json_decode($body);
    }

    38. 版本控制

    38.1 使用 Git

    使用 Git 进行版本控制是现代开发的最佳实践。以下是一些基本步骤来初始化和使用 Git:

    1. 初始化 Git 仓库git init
    2. 添加 .gitignore 文件: 创建一个 .gitignore 文件,以忽略不需要版本控制的文件和目录。例如: /vendor/ /node_modules/ /wp-content/uploads/ .env
    3. 添加并提交文件git add . git commit -m "Initial commit"
    4. 推送到远程仓库git remote add origin <remote-repository-URL> git push -u origin master

    38.2 使用 GitHub Actions 进行 CI/CD

    GitHub Actions 可以帮助你自动化测试、构建和部署插件。

    1. 创建 GitHub Actions 工作流: 在你的仓库中创建 .github/workflows/main.yml 文件: name: CI on: push: branches: - master pull_request: branches: - master jobs: build: runs-on: ubuntu-lateststeps: - name: Checkout code uses: actions/checkout@v2 - name: Set up PHP uses: shivammathur/setup-php@v2 with: php-version: '7.4' - name: Install dependencies run: composer install - name: Run tests run: vendor/bin/phpunit</code></pre></li>

    39. 钩子和过滤器

    39.1 自定义钩子

    创建自定义钩子,让其他开发者可以扩展你的插件功能:

    do_action('my_first_plugin_custom_action', $arg1, $arg2);

    使用自定义钩子:

    add_action('my_first_plugin_custom_action', 'my_custom_function', 10, 2);
    
    function my_custom_function($arg1, $arg2) {
        // 处理自定义钩子
    }

    39.2 自定义过滤器

    创建自定义过滤器以便于修改数据:

    $value = apply_filters('my_first_plugin_custom_filter', $value);

    使用自定义过滤器:

    add_filter('my_first_plugin_custom_filter', 'my_custom_filter_function');
    
    function my_custom_filter_function($value) {
        // 修改并返回数据
        return $value . ' modified';
    }

    40. REST API

    40.1 注册自定义 REST API 路由

    使用 WordPress REST API 注册自定义路由:

    function my_first_plugin_register_rest_route() {
        register_rest_route('my-first-plugin/v1', '/data', array(
            'methods' => 'GET',
            'callback' => 'my_first_plugin_rest_callback',
        ));
    }
    
    add_action('rest_api_init', 'my_first_plugin_register_rest_route');

    40.2 处理 REST API 请求

    处理 REST API 请求并返回响应:

    function my_first_plugin_rest_callback($request) {
        $data = array(
            'message' => __('Hello, World!', 'my-first-plugin'),
        );
    
        return new WP_REST_Response($data, 200);
    }

    41. Gutenberg 块

    41.1 创建自定义 Gutenberg 块

    继续创建和注册自定义 Gutenberg 块。

    1. 创建编辑器脚本(续): 在 src 目录中继续创建 index.js 文件的内容: import { registerBlockType } from '@wordpress/blocks'; import { useBlockProps, RichText } from '@wordpress/block-editor'; registerBlockType('my-first-plugin/my-custom-block', { edit({ attributes, setAttributes }) { const blockProps = useBlockProps(); return ( <div {...blockProps}> <RichText tagName="p" onChange={(content) => setAttributes({ content })} value={attributes.content} placeholder="Write your content here..." /> </div> ); }, save({ attributes }) { const blockProps = useBlockProps.save(); return ( <div {...blockProps}> <RichText.Content tagName="p" value={attributes.content} /> </div> ); }, attributes: { content: { type: 'string', source: 'html', selector: 'p' } } });
    2. 编译脚本: 更新 package.json 以添加构建脚本: { "scripts": { "build": "wp-scripts build", "start": "wp-scripts start" }, "devDependencies": { "@wordpress/scripts": "^23.0.0" } } 然后运行构建命令: npm run build
    3. 加载脚本: 在插件的 PHP 文件中加载编译后的 JavaScript 文件: function my_first_plugin_register_block() { wp_register_script( 'my-first-plugin-editor-script', plugins_url('build/index.js', __FILE__), array('wp-blocks', 'wp-element', 'wp-editor') );register_block_type('my-first-plugin/my-custom-block', array( 'editor_script' =&gt; 'my-first-plugin-editor-script', ));} add_action('init', 'my_first_plugin_register_block');

    42. 使用 WP-CLI

    WP-CLI 是一个强大的命令行工具,可以用于管理 WordPress 安装,包括插件的开发和调试。

    42.1 创建自定义 WP-CLI 命令

    1. 注册自定义命令: 在插件的主文件中注册一个自定义 WP-CLI 命令: if (defined('WP_CLI') && WP_CLI) { WP_CLI::add_command('my_first_plugin', 'My_First_Plugin_CLI_Command'); } class My_First_Plugin_CLI_Command { public function hello($args, $assoc_args) { WP_CLI::success('Hello, World!'); } }
    2. 运行命令: 在终端中运行自定义 WP-CLI 命令: wp my_first_plugin hello

    43. 单元测试

    43.1 使用 PHPUnit 进行单元测试

    1. 安装 PHPUnit: 使用 Composer 安装 PHPUnit: composer require --dev phpunit/phpunit
    2. 设置测试环境: 创建 phpunit.xml.dist 文件以配置测试环境: <phpunit bootstrap="tests/bootstrap.php"> <testsuites> <testsuite name="My First Plugin Test Suite"> <directory>tests</directory> </testsuite> </testsuites> </phpunit>
    3. 编写测试: 在 tests 目录中创建测试文件,例如 test-sample.phpclass SampleTest extends WP_UnitTestCase { public function test_sample() { $this->assertTrue(true); } }
    4. 运行测试: 在终端中运行 PHPUnit: vendor/bin/phpunit

    44. 代码质量和静态分析

    44.1 使用 PHPCS 检查代码规范

    1. 配置 PHPCS: 创建 phpcs.xml.dist 文件,以定义代码检查规则: <?xml version="1.0"?> <ruleset name="My First Plugin"> <description>WordPress Plugin Coding Standards</description> <rule ref="WordPress-Core"/> <rule ref="WordPress-Docs"/> <rule ref="WordPress-Extra"/> <file>./</file> <exclude-pattern>*/vendor/*</exclude-pattern> </ruleset>
    2. 运行 PHPCS: 在终端中运行 PHP_CodeSniffer 以检查代码规范: vendor/bin/phpcs

    44.2 使用 PHPStan 进行静态分析

    1. 安装 PHPStan: 使用 Composer 安装 PHPStan: composer require --dev phpstan/phpstan
    2. 配置 PHPStan: 创建 phpstan.neon 文件: includes: - vendor/phpstan/phpstan/conf/bleedingEdge.neon parameters: level: max paths: - %currentWorkingDirectory%/src excludePaths: - %currentWorkingDirectory%/vendor
    3. 运行 PHPStan: 在终端中运行 PHPStan 以进行静态分析: vendor/bin/phpstan analyse

    45. 安全性

    45.1 数据验证和清理

    为了防止安全漏洞,必须对所有用户输入进行验证和清理。

    1. 验证输入: 使用 sanitize_* 系列函数来清理输入数据: $email = sanitize_email($_POST['email']); $url = esc_url($_POST['url']);
    2. 非ces验证: 使用 wp_verify_noncecheck_admin_referer 来验证非ces: if (!wp_verify_nonce($_POST['_wpnonce'], 'my_action')) { die('Security check failed'); }

    45.2 SQL 注入防护

    使用 prepare 方法来防止 SQL 注入:

    global $wpdb;
    $wpdb->query($wpdb->prepare("SELECT * FROM $wpdb->posts WHERE post_title = %s", $title));

    46. 国际化和本地化

    46.1 准备插件进行翻译

    1. 加载文本域: 在插件主文件中加载文本域: function my_first_plugin_load_textdomain() { load_plugin_textdomain('my-first-plugin', false, dirname(plugin_basename(__FILE__)) . '/languages'); } add_action('plugins_loaded', 'my_first_plugin_load_textdomain');
    2. 使用翻译函数: 使用 __, _e, _n 等函数进行国际化: $message = __('Hello, World!', 'my-first-plugin'); _e('Welcome to my plugin!', 'my-first-plugin');
    3. 生成 POT 文件: 使用 wp i18n 工具来生成 POT 文件: npx wp i18n make-pot . languages/my-first-plugin.pot

    47. 优化性能

    47.1 缓存

    1. 使用 Transients API: 缓存临时数据,以减少数据库查询: $data = get_transient('my_first_plugin_data'); if ($data === false) { $data = costly_database_query(); set_transient('my_first_plugin_data', $data, 12 * HOUR_IN_SECONDS); }
    2. 使用对象缓存: 使用 wp_cache_setwp_cache_get 进行对象缓存: wp_cache_set('my_cache_key', $data); $cached_data = wp_cache_get('my_cache_key');

    47.2 优化数据库查询

    确保数据库查询高效,避免不必要的查询和数据加载:

    global $wpdb;
    $results = $wpdb->get_results($wpdb->prepare("SELECT * FROM $wpdb->posts WHERE post_status = %s", 'publish'));

    48. 高级插件架构

    48.1 使用面向对象编程(OOP)

    通过面向对象编程(OOP),可以使插件的代码更加模块化、可维护和可扩展。

    1. 创建基础类: 创建一个基础类来管理插件的初始化和加载: class MyFirstPlugin { protected static $instance = null;public static function get_instance() { if (null === self::$instance) { self::$instance = new self(); } return self::$instance; } private function __construct() { $this-&gt;setup_hooks(); } private function setup_hooks() { add_action('init', array($this, 'init')); } public function init() { // 初始化代码 }} MyFirstPlugin::get_instance();
    2. 模块化插件功能: 将插件的不同功能模块化,以便更好地管理和扩展: class MyFirstPlugin_Admin { public function __construct() { add_action('admin_menu', array($this, 'add_admin_menu')); }public function add_admin_menu() { add_menu_page('My First Plugin', 'My First Plugin', 'manage_options', 'my-first-plugin', array($this, 'admin_page')); } public function admin_page() { echo '&lt;h1&gt;My First Plugin Settings&lt;/h1&gt;'; }} if (is_admin()) { new MyFirstPlugin_Admin(); }

    48.2 使用依赖注入(DI)

    依赖注入(DI)是一种设计模式,它可以使类的依赖更显式,并且更容易进行单元测试。

    1. 创建依赖注入容器: 使用一个简单的 DI 容器来管理类的实例: class DIContainer { protected $instances = array();public function set($name, $instance) { $this-&gt;instances[$name] = $instance; } public function get($name) { return $this-&gt;instances[$name]; }} $container = new DIContainer();
    2. 使用 DI 容器: 注册和使用依赖: $container->set('admin', new MyFirstPlugin_Admin()); $admin = $container->get('admin');

    49. REST API

    49.1 创建自定义 REST API 端点

    WordPress 提供了一个内置的 REST API,可以用来创建自定义端点。

    1. 注册自定义端点: 使用 register_rest_route 函数注册一个自定义 REST API 端点: function my_first_plugin_register_api_routes() { register_rest_route('my-first-plugin/v1', '/data', array( 'methods' => 'GET', 'callback' => 'my_first_plugin_get_data', )); } add_action('rest_api_init', 'my_first_plugin_register_api_routes'); function my_first_plugin_get_data($request) { return new WP_REST_Response(array('data' => 'Hello, World!'), 200); }
    2. 测试端点: 访问 http://your-site/wp-json/my-first-plugin/v1/data 以测试自定义端点。

    50. 多站点支持

    50.1 多站点激活和停用

    ```php
            foreach ($blog_ids as $blog_id) {
                switch_to_blog($blog_id);
                my_first_plugin_single_deactivate();
                restore_current_blog();
            }
        } else {
            my_first_plugin_single_deactivate();
        }
    }
    
    register_deactivation_hook(__FILE__, 'my_first_plugin_deactivate');
    ```

    50.2 处理多站点特定功能

    1. 存储全局设置: 在多站点环境中,可以使用 get_site_optionupdate_site_option 来存储和检索全局设置: function my_first_plugin_get_global_setting($key) { return get_site_option($key); } function my_first_plugin_set_global_setting($key, $value) { update_site_option($key, $value); }
    2. 跨站点数据同步: 在多站点网络中,可以通过 switch_to_blogrestore_current_blog 函数在不同站点之间切换,以同步数据: function my_first_plugin_sync_data_across_sites($data) { global $wpdb; $blog_ids = $wpdb->get_col("SELECT blog_id FROM $wpdb->blogs"); foreach ($blog_ids as $blog_id) { switch_to_blog($blog_id); update_option('my_first_plugin_data', $data); restore_current_blog(); } }

    51. 单元测试

    51.1 使用 PHPUnit 进行测试

    1. 安装 PHPUnit: 使用 Composer 安装 PHPUnit: composer require --dev phpunit/phpunit
    2. 配置 PHPUnit: 创建 phpunit.xml.dist 文件: <phpunit bootstrap="tests/bootstrap.php"> <testsuites> <testsuite name="My Plugin Test Suite"> <directory>tests/</directory> </testsuite> </testsuites> </phpunit>
    3. 编写测试用例: 创建测试用例文件,例如 tests/test-sample.phpclass SampleTest extends WP_UnitTestCase { public function test_sample() { $this->assertTrue(true); } }
    4. 运行测试: 运行 PHPUnit 以执行测试: vendor/bin/phpunit

    52. 钩子和过滤器

    52.1 创建自定义钩子

    1. 创建自定义动作钩子: 在插件代码中创建一个自定义动作钩子: do_action('my_first_plugin_custom_action', $arg1, $arg2);
    2. 创建自定义过滤器钩子: 在插件代码中创建一个自定义过滤器钩子: $value = apply_filters('my_first_plugin_custom_filter', $value, $arg1, $arg2);

    52.2 使用钩子和过滤器

    1. 添加动作钩子: 使用 add_action 函数来挂载自定义动作钩子: add_action('my_first_plugin_custom_action', 'my_custom_action_function', 10, 2); function my_custom_action_function($arg1, $arg2) { // 自定义动作处理逻辑 }
    2. 添加过滤器钩子: 使用 add_filter 函数来挂载自定义过滤器钩子: add_filter('my_first_plugin_custom_filter', 'my_custom_filter_function', 10, 3); function my_custom_filter_function($value, $arg1, $arg2) { // 自定义过滤逻辑 return $value; }