标签: PHP

  • 黄聪:WordPress动作钩子函数add_action()、do_action()源码解析

    黄聪:WordPress动作钩子函数add_action()、do_action()源码解析

    WordPress常用两种钩子,过滤钩子和动作钩子。过滤钩子相关函数及源码分析在上篇文章中完成,本篇主要分析动作钩子源码。

    然而,在了解了动作钩子的源码后你会发现,动作钩子核心代码竟然跟过滤钩子差不多!是的,至此,我不得不告诉你,动作钩子只是WP开发者为了区分概念而把过滤钩子另外命名的一种东西!当然,它们还是有一些细微的差别,下面我们将从源码来深入解读。

    动作钩子概念:动作钩子是WP代码执行到某处或某个事件发生时触发的一系列函数,插件可以利用动作钩子API在WP代码执行的特定点之前插入一系列函数以控制执行。它跟过滤钩子极像,唯一不同的是过滤钩子返回一个处理后的值,而动作钩子仅完成函数执行并不返回值,如果钩子不存在则返回NULL并新增该钩子。

    动作钩子原理:由于动作钩子和过滤钩子几乎一样,所以它们的实现原理也是一样的。它主要利用一个全局变量$wp_filter,增加动作函数时使用add_action()函数给全局变量$wp_filter增加了一个数组元素,这个元素键名中含有钩子名,值中含有对应函数及执行优先级等信息,在调用do_action()函数使用动作钩子时,它通过循环查找出所有跟钩子关联的函数并将其依次调用,最后返回处理后的数据。

    实例

    # ======= Somewhere in a (mu-)plugin, theme or the core ======= #
     
    /**
     * You can have as many arguments as you want,
     * but your callback function and the add_action call need to agree in number of arguments.
     * Note: `add_action` above has 2 and 'i_am_hook' accepts 2. 
     * You will find action hooks like these in a lot of themes & plugins and in many place @core
     * @see: https://codex.wordpress.org/Plugin_API/Action_Reference
     */
     
    # ======= e.g., inside your functions.php file ======= #
     
    /**
     * Define callback function
     * Inside this function you can do whatever you can imagine
     * with the variables that are loaded in the do_action() call above.
     */
    function wpdocs_who_is_hook( $a, $b ) {
        echo '<code>';
            print_r( $a ); // `print_r` the array data inside the 1st argument
        echo '</code>';
     
        echo '<br />'.$b; // echo linebreak and value of 2nd argument
    }
     
    // then add it to the action hook, matching the defined number (2) of arguments in do_action
    // see [https://codex.wordpress.org/Function_Reference/add_action] in the Codex 
     
    // add_action( $tag, $function_to_add, $priority, $accepted_args );
    add_action( 'wpdocs_i_am_hook', 'wpdocs_who_is_hook', 10, 2 );
     
    // Define the arguments for the action hook
    $a = array(
        'eye patch'  => 'yes',
        'parrot'     => true,
        'wooden leg' => 1
    );
    $b = __( 'And Hook said: "I ate ice cream with Peter Pan."', 'textdomain' ); 
     
    // Executes the action hook named 'i_am_hook'
    do_action( 'wpdocs_i_am_hook', $a, $b );

    运行结果

    Array (     
    ['eye patch'] => 'yes'    
    ['parrot'] => true    
    ['wooden leg'] => 1
    )
    And hook said: "I ate ice cream with Peter Pan."

    动作钩子使用步骤:

    由于PHP代码会经过Zend等引擎翻译,代码中步骤的先后顺序并不重要,所以以下步骤仅为便于理解钩子原理的伪步骤,不具有实际参考意义!

    1、创建钩子(可省略):使用do_action()函数可以创建一个没有挂载函数的钩子,挂载函数可以通过add_action()添加,最后再使用do_action()调用执行;

    2、创建动作函数:它可以有传入参数也可以无传入参数,其他与创建普通函数没有任何区别,函数的作用为完成某项动作;

    3、挂载函数:即使用add_action()将函数挂载到指定钩子上;

    4、执行动作钩子:使用do_action()可以依次执行挂载在指定钩子上的所有函数以完成指定任务;

    动作钩子函数详解:

    在看动作钩子函数作用、参数说明等时,你会发现几乎是跟过滤钩子重复的。至于为什么会这样,那就要看源码了,我保证,看完源码后你会感慨自己被WP开发者涮了!

    add_action($tag,$function_to_add,$priority = 10,$accepted_args = 1)

    add_action()作用:该函数用于给指定的动作钩子$tag添加指定的挂载函数$function_to_add,同时它可以确定挂载函数执行优先级及其可接收参数个数;

    add_action()参数说明:

    $tag为钩子名;

    $function_to_add为挂载函数名;

    可选参数$priority为该挂载函数执行的优先级,默认为10,该数字越小则越早执行,数字相同则按其添加到钩子上的顺序执行,越早添加越早执行;

    可选参数$accepted_args确定挂载函数接收的参数个数,默认为1;

    add_action()源码分析:

    function add_action($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
        return add_filter($tag, $function_to_add, $priority, $accepted_args);
    }

    怎么样,看到了吧!被坑了有木有!add_action()函数的代码竟然是调用一次add_filter()!这尼玛完全是一个人的大名和小名的问题有木有!

    2、do_action($tag, $arg = ”)

    do_action()作用:该函数调用挂载在过滤钩子$tag上的所有函数以完全特定的任务;

    do_action()参数说明:

    $tag为钩子名;

    $arg为动作钩子上挂载函数的传入参数,默认为空;

    do_action()源码分析:

    function do_action($tag, $arg = '') {
        global $wp_filter, $wp_actions, $merged_filters, $wp_current_filter;
        if ( ! isset($wp_actions) )
            $wp_actions = array();
    # 如果$wp_actions变量未设置过,则将其定义为数组;
        if ( ! isset($wp_actions[$tag]) )
            $wp_actions[$tag] = 1;
        else
            ++$wp_actions[$tag];
        # 如果$wp_actions[$tag]未设置则将其赋值为1,否则将其值加1;
        if ( isset($wp_filter['all']) ) {
            $wp_current_filter[] = $tag;
            $all_args = func_get_args();
            _wp_call_all_hook($all_args);
        }
        # 跟apply_filters()中的all钩子处理方式完全一样!_wp_call_all_hook()源码分析见上篇文章过滤钩子源码解析;
        if ( !isset($wp_filter[$tag]) ) {
            if ( isset($wp_filter['all']) )
                array_pop($wp_current_filter);
            return;
        }
        # 当前钩子不存在,则直接返回,不再执行以后代码;
        if ( !isset($wp_filter['all']) )
            $wp_current_filter[] = $tag;
        # 将当前钩子设置为$tag;
        $args = array();
        if ( is_array($arg) && 1 == count($arg) && isset($arg[0]) && is_object($arg[0]) )
            $args[] =& $arg[0];
        else
            $args[] = $arg;
        # do_action()若有传入参数,且为一个数组,该数组仅此一个元素,该元素有值则将$args值设置为引用$arg[0],否则直接赋值;
        for ( $a = 2; $a < func_num_args(); $a++ )
            $args[] = func_get_arg($a);
        # 通过for循环,若do_action()有不只一个传入参数,将这些值赋给数组$args;
        if ( !isset( $merged_filters[ $tag ] ) ) {
            ksort($wp_filter[$tag]);
            $merged_filters[ $tag ] = true;
        }
        # 跟apply_filter()函数排序代码完全一样!详解见上文;
        reset( $wp_filter[ $tag ] );
        do {
            foreach ( (array) current($wp_filter[$tag]) as $the_ )
                if ( !is_null($the_['function']) )
                    call_user_func_array($the_['function'], array_slice($args, 0, (int) $the_['accepted_args']));
        } while ( next($wp_filter[$tag]) !== false );
        array_pop($wp_current_filter);
    }
        # 除了少了一行return $value其他跟apply_filters()完全一样!

    php中call_user_func 与 call_user_func_array的使用

  • php中call_user_func 与 call_user_func_array的使用

    php中call_user_func 与 call_user_func_array的使用

    call_user_func()是利用回调函数处理字符串,call_user_func_array是利用回调函数处理数组。

    // 1、 调用自定义函数
    function test($a, $b)
    {
        echo $a + $b;
    }
    // 字符串传参
    call_user_func('test', 1, 2); // 3
    // 数组式传参
    call_user_func_array('test', [1, 2]); // 3
    // 2、 调用匿名函数
    call_user_func(function($a, $b){ echo $a + $b ;}, 1, 2); // 3
    call_user_func_array(function($a, $b){ echo $a + $b ;}, [1, 2]); // 3
    // 3、 调用系统函数
    echo call_user_func('strtoupper', 'abc'); // ABC
    echo call_user_func_array('strtoupper', ['abc']); // ABC
    // 4、 调用类中的函数
    class Test
    {
        static public function demo($a, $b)
        {
            echo $a + $b;
        }
        public function show($a, $b)
        {
            echo $a + $b;
        }
    }
    // 调用类中的静态方法
       // 类名方法名以数据形式
    call_user_func(['Test', 'demo'], 1, 2); // 3
    call_user_func_array(['Test', 'demo'], [1, 2]); // 3
       // 类名方法名以字符串形式
    call_user_func('Test::demo', 1, 2); // 3
    call_user_func_array('Test::demo', [1, 2]); // 3
    // 调用类中的动态方法,对象和方法必须通过数组形式传递
    call_user_func([new Test, 'show'], 1, 2); // 3
    call_user_func_array([new Test, 'show'], [1, 2]); // 3

    总结: call_user_func 和 call_user_func_array不同在于传参方式,前者是字符串形式,后者是数组形式。

    二者皆可调用自定义函数、匿名函数、系统函数以及类中的静态动态方法。如在TP5.1中运用Facade门面类作为静态代理类,

    使用静态方式调用动态类中的动态方法,即让类无需实例化而直接进行静态方式的调用,带来了更好的可测试性和扩展性。

  • vscode 去除多余空行

    vscode 去除多余空行

    使用正则表达式 ^\s*(?=\r?$)\n ​​​​

    ^\s*(?=\r?$)\n
  • LNMP定时自动检测502 Bad Gateway错误并重启PHP

    LNMP定时自动检测502 Bad Gateway错误并重启PHP

    简单的脚本,用来监控php的 502 Bad Getway 并自动重启php服务进程,下面记录一下具体的脚本内容,比如将文件命名为 check_php.sh ,放在 /root 目录中

    #!/bin/bash
    MY_URL="https://www.jiloc.com/"
    RESULT=`curl -I $MY_URL|grep "HTTP/1.1 502"`
    if [ -n "$RESULT" ]; then
        /etc/init.d/php-fpm restart
    fi

    给予可执行权限:

    chmod +x /root/check_php.sh

    通过 crontab 设置自动运行:

    crontab -e

    crontab里写入如下内容

    */5 * * * * /root/check_php.sh
  • WordPress搬家插件All-in-one wp Migration导入文件大小限制问题解决办法 宝塔面板 图文教程

    WordPress搬家插件All-in-one wp Migration导入文件大小限制问题解决办法 宝塔面板 图文教程

    站长在进行WordPress建站的时候,经常需要进行WordPress网站搬家,搬家工具All in one WP migration,该软件全程傻瓜式操作,可以一键完成搬家,不需要任何技术知识和代码操作,可以备份导出您的WordPress完整网站,包括网站程序文件、数据库、插件和主题等。但是也有用户说文件大小超出限制文件大小,该如何解决呢?今天老季为大家分享WordPress搬家插件All-in-one wp Migration导入文件大小限制问题解决办法。

    WordPress搬家插件All-in-one wp Migration导入文件大小限制问题解决办法 (https://www.wpmee.com/) WordPress插件 第1张


    今天以宝塔为例,为大家讲解一下具体操作。

    修改PHP配置文件

    WordPress搬家插件All-in-one wp Migration导入文件大小限制问题解决办法 (https://www.wpmee.com/) WordPress插件 第2张
    WordPress搬家插件All-in-one wp Migration导入文件大小限制问题解决办法 (https://www.wpmee.com/) WordPress插件 第3张
    WordPress搬家插件All-in-one wp Migration导入文件大小限制问题解决办法 (https://www.wpmee.com/) WordPress插件 第4张

    1.打开宝塔面板 – 软件管理 – 运行环境

    2.点击设置,进入到PHP的配置界面,选择配置修改

    3.上传限制修改

    将这几个数值修改成自己需要的数值,比如200M,修改完成后点击保存。

    修改NGINX配置

    WordPress搬家插件All-in-one wp Migration导入文件大小限制问题解决办法 (https://www.wpmee.com/) WordPress插件 第5张
    WordPress搬家插件All-in-one wp Migration导入文件大小限制问题解决办法 (https://www.wpmee.com/) WordPress插件 第6张

    1.继续在宝塔面板内选择 Nginx设置

    2.选择性能调整,其中有一个选项叫做 client_max_body_size,表示最大上传文件,将其调整为你需要的最大数值即可。

    最后记得将php和nginx都重启一下,再次上传就不会出现错误了。

  • 记一次SourceGuardian加密的PHP扩展安装过程

    记一次SourceGuardian加密的PHP扩展安装过程

    问题描述

     从网上下载了一套PHP聊天室源码,部署在Winserver中的Apache服务器,打开文件 common.inc.php,发现一串不知所云的代码:

    报错信息说的是 sg_load 函数没有找到,查找一番资料后,发现 sg_load 是 SourceGuardian 扩展提供的全局函数,运行环境需要这个扩展才能执行这段代码。SourceGuardian 提供 php 源码文件的加密,而运行这些加密 php 文件则需要安装扩展:SourceGuardian loader。

    SourceGuardian loader 安装过程

    浏览器中打开 https://www.sourceguardian.com/loaders/download.php,输入服务器上打印了 phpinfo 信息的 php 文件地址:

    在线的检查脚本会根据 phpinfo 的信息,提取到 php 版本信息和扩展路径,然后提示你需要的操作。例如它提示我 extension_dir 应设置为绝对路径而非相对路径:

    那就改呗,在 php.ini 中找到 extension_dir,将原来的相对路径 ./ext 改为绝对路径。重启 Apache 然后重新执行检测:

    到这一步它提示我需要:

    1. 下载 ixed.5.4.win
    2. 安装到 php 扩展的 ext 目录
    3. 在 php.ini 中加载它:extension=ixed.5.4.win
    4. 重启 Apache

    全部修改完成后,在 phpinfo 看到了 SourceGuardian 的相关信息,说明扩展已经生效了:

    满怀期待的再次访问网站。依旧报错,不过这一次不是找不到 sg_load 函数了,再次访问就一切OK~~

  • php正则判断是否携带http和https 是否绝对连接和相对连接

    php正则判断是否携带http和https 是否绝对连接和相对连接

    记录一下php正则检测网址是否包含http、https字符,代码如下

    $url = "https://www.baidu.com/";
    $preg = "/^http(s)?:\\/\\/.+/";
    if(preg_match($preg,$url)){
        echo 'Yes';
    }else{
        echo 'No';
    }
  • php 检测是否是 日文

    php 检测是否是 日文

    最近我们在做高考小语种的项目,需要检测到字符串中是否是日语,这里记录一下代码段

    <?php
    $word = "激安価格お買い得タイヨー脱腸帯·片側用 fm 腰回り";
    $word2 = "This is the english language";
    function isJapanese($lang) {
        return preg_match('/[\x{4E00}-\x{9FBF}\x{3040}-\x{309F}\x{30A0}-\x{30FF}]/u', $lang);
    }
    var_dump(isJapanese($word)); //returns 1
    var_dump(isJapanese($word2));//returns 0
  • php 多维数组 删除某个key键以及value值 代码实例

    php 多维数组 删除某个key键以及value值 代码实例

    在日常开发中,我们通常会遇到数组的操作。我们这里记录一下如何在一个多维不规则数组中,删除某个key键或value值。下面我们给出删除key键的函数方法。

        /**
         * 删除多维数组中指定key键以及value值
         * @param  array  $arr  数组
         * @param  string/int  $key  键(key)
         * @return array
         */
        function mulit_array_remove_by_key($arr, $key){
            $new_array = [];
            foreach($arr as $k => $v) {
                /* 判断当前元素的值是不是数组,是的话就递归下去,重复查找这个函数。 */
                if(is_array($v)) {
                    /* 使用新数组记录 */
                    $new_array[$k] = mulit_array_remove_by_key($v, $key);
                    continue;
                }elseif($k == $key) {
                    /* 找到元素后删除 */
                    unset($arr[$k]);
                    continue;
                }
                /* 没删除的继续保留下来存储在新数组 */
                $new_array[$k] = $arr[$k];
            }
            return $new_array;
        }

  • php 正则匹配html标签之间内容

    php 正则匹配html标签之间内容

    我们在日常php开发中,有时需要正则匹配某个html标签之间的内容,这里记录一下php方法

    
    /**
     * @param $string 需要匹配的字符串
     * @param $tag html标签
     */
    function getTagValue($string, $tag){
        $pattern = "/<{$tag}>(.*?)<\/{$tag}>/s";
        preg_match_all($pattern, $string, $matches);
        return isset($matches[1]) ? $matches[1] : '';
    }

  • Webhook各类网站的自动化部署校验脚本-PHP版本

    Webhook各类网站的自动化部署校验脚本-PHP版本

    我们记录一下 github、gitlab、gitee、coding 平台的php版接收webhook脚本

    github

    <?php
    //网站目录
    $www_file  = '/www/wwwroot/laoji.org/api';
    //日志文件路径
    $log_file = '/www/wwwroot/www.test.com/auto_hook.log';
    // GitHub项目 Settings/Webhooks 中的 Secret
    $client_secret = "laoji.org";
    //接收数据
    $vaild_data = json_decode(file_get_contents('php://input'), true);
    //传递的签名
    $vaild_signature = $_SERVER['HTTP_X_HUB_SIGNATURE'];
    
    $hash = "sha1=".hash_hmac('sha1', $vaild_data, $client_secret );
    if (strcmp($vaild_signature , $hash) == 0) {
        // sign sucess
        //执行shell命令并把返回信息写进日志
        shell_exec("cd {$www_file}  &&  git pull  origin dev  >  {$log_file}  2>&1 ");  
    }
    

    gitlab

    <?php
    //网站目录
    $www_file  = '/www/wwwroot/laoji.org/api';
    //日志文件路径
    $log_file = '/www/wwwroot/www.test.com/auto_hook.log';
        
    //作为接口传输的时候认证的密钥  
    $client_token = 'laoji.org';  
    
    //传递的token
    $vaild_token = $_SERVER['HTTP_X_GITLAB_TOKEN'];  
    //传递数据
    $vaild_data = json_decode(file_get_contents("php://input"), true);
    
    /* test token */
    if ($vaild_token !== $client_token )
    {
        echo "error 403";
        exit();
    }
    //若是dev分支且提交数大于0
    if ($vaild_data['ref']=='refs/heads/dev' && $vaild_data['total_commits_count']>0) {
       //执行shell命令并把返回信息写进日志
       shell_exec("cd ".$www_file."  &&  git pull  origin dev  >  ".$log_file."  2>&1 ");  
    }    
        

    gitee

    <?php
    //网站目录
    $www_file  = '/www/wwwroot/laoji.org/api';
    //日志文件路径
    $log_file = '/www/wwwroot/www.test.com/auto_hook.log';
    // 码云WebHooks中配置的密码
    $client_password = "laoji.org";
    $vaild_data = json_decode(file_get_contents('php://input'), true);
    // 判断密码
    if ($vaild_data ['password'] === $client_password ) {        
        //执行shell命令并把返回信息写进日志
        shell_exec("cd ".$www_file."  &&  git pull  origin dev  >  ".$log_file."  2>&1 ");
    }
     
    

    coding

    <?php
    //网站目录
    $www_file  = '/www/wwwroot/laoji.org/api';
    //日志文件路径
    $log_file = '/www/wwwroot/www.test.com/auto_hook.log';
    // 码云WebHooks中配置的密码
    $client_token = "laoji.org";
    // 接收Coding post传递的参数
    $vaild_data = file_get_contents('php://input');
    // 从请求头中获取签名
    $vaild_signature = $_SERVER['HTTP_X_CODING_SIGNATURE'];
    // 进行签名解析
    $sha1 = hash_hmac("sha1",$vaild_data,$client_token);
    $result_signature = 'sha1='. $sha1;
    // 进行身份验证
    if ($result_signature === $vaild_signature ) {
        //执行shell命令并把返回信息写进日志
        shell_exec("cd ".$www_file."  &&  git pull  origin dev  >  ".$log_file."  2>&1 ");
    }
    
  • PHPExcel导出导入”continue” targeting switch is equivalent to “break”.

    PHPExcel导出导入”continue” targeting switch is equivalent to “break”.

    问题描述

    continue” targeting switch is equivalent to “break”.Did you mean to use “continue 2”?

    解决办法

    在 php 7.3 的 switch 中使用 continue 会出现警告 1 2 3

    最好的方式是把 \PHPExcel\Shared\OLE.php 文件中的 continue; 改为 continue 2; 或 break;

WeChat
↑