普通视图

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

如何在本地调试WordPress时忽略对wordpress.org的网络连接请求

作者 大致
2025年3月13日 10:43

本地调试WordPress时,为了保证代码的正确性,插件和主题作者往往会打开wp-config.php里的WP_DEBUG开关。如果您不知道这个开关,就不要往下看了。

define('WP_DEBUG', true);

这样PHP运行时的错误和警告就会以醒目的字体直接打印到网页上。
20250313_WP_local_debug_warning

但是这样会带来一个程序员的日经问题:是我的BUG我改,不是我的BUG莫挨老子!
按说WP这种成熟的产品是不会让用户看到警告和错误的。但是,由于众所周知的原因,WP内核、主题和插件升级所需要的WordPress的官网wordpress.org时灵时不灵,连不上的时候就会显示大面积的警告信息。
安装完成以后,后台这种需要连接到wordpress.org的地方大抵有4种:core update、theme update、 plugin update和translation api,出现在后台Dashboard、 Plugin、 Theme、 和Settings页面。其中Dashboard会调用前三种Update,Settings四种都会调用。
这些信息不仅是影响对于出错代码的判断(比如图中提示的“Cannot modify header information”,其实根本就不是本地header的问题),还会直接影响页面元素的渲染和操作。
那就必须要干掉它!

再次更新,

define('WP_HTTP_BLOCK_EXTERNAL', true);

这个宏屁用没用,还是得用我下面的方法。

开整。

//下面的action钩子调用的早,所以在加载主题或插件的时候就要直接remove掉,否则没机会了。
if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
    remove_action('admin_init', '_maybe_update_core');
    remove_action('admin_init', '_maybe_update_plugins');
    remove_action('admin_init', '_maybe_update_themes');
    remove_action('init', 'wp_schedule_update_checks');

    //translations_api默认会返回false,之后会访问wordpress.org,返回空数组之后就不访问了。
    //Since 4.0.0
    add_filter('translations_api', '__return_empty_array');
}

//调试者作为admin,默认是有各种update权限的。这里令各种内部调用user_has_cap询问4种权限的结果强行置为false。
function _debug_ignore_wp_request ($allcaps, $caps, $args){
    $server_caps = array('install_languages', 'update_themes', 'update_plugins', 'update_core');
    foreach ($caps as $cap) {
        if ( in_array($cap, $server_caps)) {
            $allcaps[$cap] = false;
        }
    }
    return $allcaps;
}

function my_admin_init {
    if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
        //下面的钩子很多不能移除得太早。
        //宁杀错不放过。
        remove_action('upgrader_process_complete', 'wp_update_plugins');
        remove_action('upgrader_process_complete', 'wp_update_themes');
        remove_action('load-plugins.php', 'wp_plugin_update_rows', 20);
        remove_action('load-themes.php', 'wp_theme_update_rows', 20);
        remove_action('load-plugins.php', 'wp_update_plugins');
        remove_action('load-themes.php', 'wp_update_themes');
        wp_unschedule_hook('wp_version_check');
        wp_unschedule_hook('wp_update_plugins');
        wp_unschedule_hook('wp_update_themes');

        remove_action('wp_version_check', 'wp_version_check');
        remove_action('load-plugins.php', 'wp_update_plugins');
        remove_action('load-update.php', 'wp_update_plugins');
        remove_action('load-update-core.php', 'wp_update_plugins');
        remove_action('wp_update_plugins', 'wp_update_plugins');
        remove_action('load-themes.php', 'wp_update_themes');
        remove_action('load-update.php', 'wp_update_themes');
        remove_action('load-update-core.php', 'wp_update_themes');
        remove_action('wp_update_themes', 'wp_update_themes');
        remove_action('update_option_WPLANG', 'wp_clean_update_cache', 10, 0);
        remove_action('wp_maybe_auto_update', 'wp_maybe_auto_update');
        add_filter('user_has_cap', '_debug_ignore_wp_request', 10, 3);
    }
}

add_action('admin_init','my_admin_init');

完事。这个世界清净了。


  • (1):农历每年冬至所在的月固定为十一月。如果两个冬至间有13次朔望,则该年产生闰月。13个月中,第一个没有二十四节气里的偶数节气的月份就是闰月。因为十一月是起调点,所以农历十二月和正月是不会出现闰月的,正月初一和第一个节气春分受的影响也很小。我数学不好,但查表得到的结果,从1950年到2069的120年间,闰年就是双春,双春就是闰年,一一对应,无一例外。闰月是典型的用太阳历调整月历的规则,所以农历是阴阳历而不是纯阴(月)历。

std::thread()使用lambda表达式调用函数导致的离奇bug

作者 大致
2025年1月13日 10:44

最近项目升级开发环境,从visual stdio 2017升级到visual stdio 2022,出现奇怪的现象:同样的代码,2017编译出来风平浪静,2022编译出来一运行就是段错误。

我们的函数大概长这样:

class a {
    public:
    void DoThings(std::string str1, std::string str2, int idx){...};
    void Do0(){...};
    void OnInit() {
        std::string str1 = "FileName.txt";
        std::string str2 = "X:\\Dest\\Path\";
        int i = 0;
        std::thread thd = std::thread([&]{DoThings(str1, str2, i);});
        thd.detach();
    };
};

从debug表现来看,是调用线程函数的时候,传入了典型的野指针。但是啊,三个参数,两个是std::string,另外一个是int啊!string有问题可以理解,可int怎么还能错呢?
把参数改成传入前new,调用后delete,自然是解决了。但心里各种不爽,new一个int,脸往哪搁啊!
好在问题定位的范围比较小,只是起线程调用函数这一小块地方。

2017只支持到C++ 11,而2022是C++ 14,看来问题出在这里了。
去找lambda的说明:

For the entities that are captured by reference (with the capture-default [&] or when using the character &, e.g. [&a, &b, &c]), it is unspecified if additional data members are declared in the closure type, but any such additional members must satisfy.

人家说了,你用lambda进行引用捕获的时候,必须保证捕获的成员是安全的。
看到这里差不多明白了,是[&]的锅。[&]的意思是所有参数按照引用的方式捕获。而你的三个变量都是临时变量,传个毛线的引用啊!
如果不安全会怎么样?这玩意儿叫“未定义的行为”,爱咋样咋样。也就是说,我们的写法触发了这种未定义的右值引用行为,人家可以给你实装成保留地址,也可以转换成另外的指针进行实装。故而2017和2022都没错,错的是写代码的人。
继续写个例子验证一下:

#include "stdafx.h"
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;

std::mutex g_mtx;

class CTester {
public:
    CTester() {
    };

    virtual ~CTester() {
    };
    void Run() {
        for (int i = 100, j = 1, n = 0; n < 3; i += 100, j += 1, n++) {
            std::thread thd = std::thread([&#038;] { Show(i, j, "std::thread([&#038;] { Show(i, j); }): "); });
            thd.detach();
            thd = std::thread([&#038;, i, j] { Show(i, j, "std::thread([&#038;, i, j] { Show(i, j); }): "); });
            thd.detach();
            thd = std::thread([&#038;, j] { Show(i, j, "std::thread([&#038;, j] { Show(i, j); }): "); });
            thd.detach();
            thd = std::thread([=] { Show(i, j, "std::thread([=] { Show(i, j); }): "); });
            thd.detach();
        }
    }
    void Show(int x, int y, const char* pri) {
        std::lock_guard< std::mutex>lock(g_mtx);
        char szOut[128] = { 0 };
        sprintf(szOut, "pri = %s x=%d y=%d\n", pri, x, y);
        std::cout << szOut;
    }
};

int main()
{
    std::cout << "Test Start" << '\n';
    CTester t;
    t.Run();
    _sleep(500);
    std::cout << "Test End" << '\n';
    int c = getchar();
    return 0;
}

2017的运行结果:
Test Start
pri = std::thread([&] { Show(i, j); }): x=100 y=1
pri = std::thread([&, i, j] { Show(i, j); }): x=100 y=1
pri = std::thread([&, j] { Show(i, j); }): x=100 y=1
pri = std::thread([=] { Show(i, j); }): x=100 y=1
pri = std::thread([&] { Show(i, j); }): x=200 y=2
pri = std::thread([&, i, j] { Show(i, j); }): x=200 y=2
pri = std::thread([&, j] { Show(i, j); }): x=200 y=2
pri = std::thread([=] { Show(i, j); }): x=200 y=2
pri = std::thread([&] { Show(i, j); }): x=300 y=3
pri = std::thread([&, i, j] { Show(i, j); }): x=300 y=3
pri = std::thread([&, j] { Show(i, j); }): x=300 y=3
pri = std::thread([=] { Show(i, j); }): x=300 y=3
Test End

2022的运行结果:
Test Start
pri = std::thread([&] { Show(i, j); }): x=7599872 y=1992189472
pri = std::thread([=] { Show(i, j); }): x=100 y=1
pri = std::thread([&] { Show(i, j); }): x=7599872 y=1992189472
pri = std::thread([&, i, j] { Show(i, j); }): x=100 y=1
pri = std::thread([&, j] { Show(i, j); }): x=7599872 y=1
pri = std::thread([&, i, j] { Show(i, j); }): x=200 y=2
pri = std::thread([&, j] { Show(i, j); }): x=7599872 y=2
pri = std::thread([=] { Show(i, j); }): x=200 y=2
pri = std::thread([&] { Show(i, j); }): x=7599872 y=1992189472
pri = std::thread([&, j] { Show(i, j); }): x=7599872 y=3
pri = std::thread([&, i, j] { Show(i, j); }): x=300 y=3
pri = std::thread([=] { Show(i, j); }): x=300 y=3
Test End

很明显,只要敢给2022(C++ 14)传引用,它就敢给你乱引……

这部分代码是从厂商的例子里抄的。人家写的是

std::thread([&]{Do0();});

只是隐式地捕获一个this!当然写[&]就行了。
友军抄的时候根本不知道方括号是干什么的,只改了后面,才造成了这样的后果。

那么怎么解决呢?写[=]或者[&, str1, str2, i]吗?并不是。
lambda叫啥?“匿名函数”啊!你都要调用真正的函数了,就别整匿名函数那一套了。
正确的std::thread调用类函数的时候应该长这样:

std::thread thrd = std::thread(&a::DoThings, this, str1, str2, i);
thrd.detach();

我是真心的不喜欢lambda。


  • (1):农历每年冬至所在的月固定为十一月。如果两个冬至间有13次朔望,则该年产生闰月。13个月中,第一个没有二十四节气里的偶数节气的月份就是闰月。因为十一月是起调点,所以农历十二月和正月是不会出现闰月的,正月初一和第一个节气春分受的影响也很小。我数学不好,但查表得到的结果,从1950年到2069的120年间,闰年就是双春,双春就是闰年,一一对应,无一例外。闰月是典型的用太阳历调整月历的规则,所以农历是阴阳历而不是纯阴(月)历。
  • (2):白名单上的微软网址是升级补丁用的
  • (3):统计表里记作4部是因为有同名,公式如何修改还没想好
❌
❌