PHP 回调函数内容


回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数


正文

软件模块之间总是存在着一定的接口,从调用方式上,可以把他们分为三类:同步调用、回调和异步调用。 同步调用是一种阻塞式调用,调用方要等待对方执行完毕才返回,它是一种单向调用; 回调是一种双向调用模式,也就是说,被调用方在接口被调用时也会调用对方的接口; 异步调用是一种类似消息或事件的机制,不过它的调用方向刚好相反,接口的服务在收到某种讯息或发生某种事件时,会主动通知客户方(即调用客户方的接口)。 回调和异步调用的关系非常紧密,通常我们使用回调来实现异步消息的注册,通过异步调用来实现消息的通知。

先看一下C语言里的回调函数:回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数, 当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用, 而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

其他语言里的回调函数的概念与之相似,只不过各种语言里回调函数的实现机制不一样,通俗的来说,回调函数是一个我们定义的函数, 但是不是我们直接来调用,而是通过另一个函数来调用,这个函数通过回调函数的名字和参数来实现对它的调用。

php中回调函数的实现

php提供了两个内置函数call_user_func()和call_user_func_array()提供对回调函数的支持。

call_user_func()
call_user_func_array()
/**
 * Call a user function given by the first parameter
 * @link https://php.net/manual/en/function.call-user-func.php
 * @param callback $function <p>
 * The function to be called. Class methods may also be invoked
 * statically using this function by passing
 * array($classname, $methodname) to this parameter.
 * Additionally class methods of an object instance may be called by passing
 * array($objectinstance, $methodname) to this parameter.
 * </p>
 * @param mixed $parameter [optional] <p>
 * Zero or more parameters to be passed to the function.
 * </p>
 * <p>
 * Note that the parameters for call_user_func are
 * not passed by reference.
 * call_user_func example and references
 * &example.outputs;
 * </p>
 * @return mixed the function result, or false on error.
 * @since 4.0
 * @since 5.0
 */
function call_user_func ($function, ...$parameter) {}

/**
 * Call a user function given with an array of parameters
 * @link https://php.net/manual/en/function.call-user-func-array.php
 * @param callback $function <p>
 * The function to be called.
 * </p>
 * @param array $param_arr <p>
 * The parameters to be passed to the function, as an indexed array.
 * </p>
 * @return mixed the function result, or false on error.
 * @since 4.0.4
 * @since 5.0
 */
function call_user_func_array ($function, array $param_arr) {}

call_user_func($callback,参数1,参数2,…)的参数个数根据回调函数的参数来确定的。

call_user_func_array (callable $callback ,array $param_arr),它只有两个参数。

普通函数回调使用:

function f1($arg1, $arg2)
{
  echo __FUNCTION__.' exec, the args is: '.$arg1.' '.$arg2;
  echo "<br/>";
}

// 通过call_user_func调用函数f1
call_user_func('f1', 'han', 'wen');

// 通过call_user_func_array调用函数
call_user_func_array('f1', array('han', 'wen'));

类函数回调使用:

class A
{
    public $name;

    public function show($arg1)
    {
        echo __METHOD__ .' the arg is:' .$arg1."<br/>";
        echo __METHOD__ .' my name is:' .$this->name;
        echo "<br/>";
    }

    public function show1($arg1, $arg2)
    {
        echo __METHOD__ .' exec, the args is: '.$arg1 .' ' .$arg2 ."<br/>";
    }

    public static function show2($arg1, $arg2)
    {
        echo __METHOD__ .' of class A exec, the args is: ' .$arg1 .' ' .$arg2 ."<br/>";
    }
}

// 调用类中非静态成员函数,该成员函数中有$this调用了对象中的成员
$a = new A;
$a->name = 'wen';
call_user_func(array($a,'show'), 'han!');

// 调用类中非静态成员函数,没有对象被创建,该成员函数中不能有$this,其实本质是以静态调用方式调用非静态函数,不建议这样使用
call_user_func(array('A','show1',), 'han!', 'wen');

// 调用类中静态成员函数
call_user_func(array('A','show2'), 'argument1', 'argument2');

// 调用类中非静态成员函数,该成员函数中有$this调用了对象中的成员
$a = new A;
$a->name = 'wen';
call_user_func_array(array($a,'show'), array('han!'));

// 调用类中非静态成员函数,没有对象被创建,该成员函数中不能有$this,其实本质是以静态调用方式调用非静态函数,不建议这样使用
call_user_func_array(array('A','show1'), array('han!', 'wen'));

// 调用类中静态成员函数
call_user_func_array(array('A','show2'), array('argument1', 'argument2'));

输出:

A::show the arg is:han!
A::show my name is:wen
A::show1 exec, the args is: han! wen
A::show2 of class A exec, the args is: argument1 argument2
A::show the arg is:han!
A::show my name is:wen
A::show1 exec, the args is: han! wen
A::show2 of class A exec, the args is: argument1 argument2

php异步调用实现

异步调用是一种类似消息或事件的机制,不过它的调用方向刚好相反,接口的服务在收到某种讯息或发生某种事件时,会主动通知客户方(即调用客户方的接口)。

异步调用要是非阻塞调用,不用等待返回。使用中可以用于事件解耦,把一些与主任务相关度不大的任务用异步调用的方式实现, 这样主进程的运行时长就会缩短,可以减少用户的等待时长,如用户发博文,主进程保存博文,并用异步方式调用邮件通知服务, 通知用户博文发帖成功,邮件有没有通知成功并不重要。

1.客户端页面采用AJAX技术请求服务器

优点:最简单,也最快,就是在返回给客户端的HTML代码中,嵌入AJAX调用,或者,嵌入一个img标签,src指向要执行的耗时脚本。

缺点:一般来说Ajax都应该在onLoad以后触发,也就是说,用户点开页面后,就关闭,那就不会触发我们的后台脚本了。 而使用img标签的话,这种方式不能称为严格意义上的异步执行。用户浏览器会长时间等待php脚本的执行完成,也就是用户浏览器的状态栏一直显示还在load。 当然,还可以使用其他的类似原理的方法,比如script标签等等。

$.get("doRequest.php", { name: "fdipzone"} );

<img src="doRequest.php?name=fdipzone">
2.popen()函数
/**
 * Opens process file pointer
 * @link https://php.net/manual/en/function.popen.php
 * @param string $command <p>
 * The command
 * </p>
 * @param string $mode <p>
 * The mode
 * </p>
 * @return bool|resource a file pointer identical to that returned by
 * fopen, except that it is unidirectional (may
 * only be used for reading or writing) and must be closed with
 * pclose. This pointer may be used with
 * fgets, fgetss, and
 * fwrite.
 * </p>
 * <p>
 * If an error occurs, returns false.
 * @since 4.0
 * @since 5.0
 */
function popen ($command, $mode) {}

打开一个指向进程的管道,该进程由派生给定的 command 命令执行而产生。所以可以通过调用它,但忽略它的输出。使用代码如下:

// popen — 打开进程文件指针  
resource popen (string $command , string $mode)

pclose(popen('php /home/fdipzone/doRequest.php &', 'r'));

优点:避免了第一个方法的缺点,并且也很快。

缺点:这种方法不能通过HTTP协议请求另外的一个WebService,只能执行本地的脚本文件。并且只能单向打开,无法传大量参数给被调用脚本。 并且如果,访问量很高的时候,会产生大量的进程。如果使用到了外部资源,还要自己考虑竞争。

3.CURL扩展

设置curl的超时时间 CURLOPT_TIMEOUT 为1 (最小为1),因此客户端需要等待1秒:

$ch = curl_init(); 
$curl_opt = array( 
  CURLOPT_URL, 'http://www.example.com/doRequest.php'
  CURLOPT_RETURNTRANSFER,1, 
  CURLOPT_TIMEOUT,1 
); 
curl_setopt_array($ch, $curl_opt); 
curl_exec($ch); 
curl_close($ch); 

缺点:如你问题中描述的一样,由于使用CURL需要设置CUROPT_TIMEOUT为1(最小为1)。也就是说,客户端至少必须等待1秒钟。

4.fscokopen()函数
/**
 * Open Internet or Unix domain socket connection
 * @link https://php.net/manual/en/function.fsockopen.php
 * @param string $hostname <p>
 * If you have compiled in OpenSSL support, you may prefix the
 * hostname with either ssl://
 * or tls:// to use an SSL or TLS client connection
 * over TCP/IP to connect to the remote host.
 * </p>
 * @param int $port [optional] <p>
 * The port number.
 * </p>
 * @param int &$errno [optional] <p>
 * If provided, holds the system level error number that occurred in the
 * system-level connect() call.
 * </p>
 * <p>
 * If the value returned in errno is
 * 0 and the function returned false, it is an
 * indication that the error occurred before the 
 * connect() call. This is most likely due to a
 * problem initializing the socket.
 * </p>
 * @param string &$errstr [optional] <p>
 * The error message as a string.
 * </p>
 * @param float $timeout [optional] <p>
 * The connection timeout, in seconds.
 * </p>
 * <p>
 * If you need to set a timeout for reading/writing data over the
 * socket, use stream_set_timeout, as the 
 * timeout parameter to
 * fsockopen only applies while connecting the
 * socket.
 * </p>
 * @return resource|false fsockopen returns a file pointer which may be used
 * together with the other file functions (such as
 * fgets, fgetss,
 * fwrite, fclose, and
 * feof). If the call fails, it will return false
 * @since 4.0
 * @since 5.0
 */
function fsockopen ($hostname, $port = null, &$errno = null, &$errstr = null, $timeout = null) {}

fsockopen,打开一个网络连接或者一个Unix套接字连接,初始化一个套接字连接到指定主机(hostname)。 这只是建立连接,建立连接后还需要向指定主机(hostname)发送内容,我们可以用fwrite() 、 fputs()来实现发送内容。

fsockopen是最好的,缺点是需要自己拼接header部分。

GET访问的例子:

$fp = fsockopen("www.example.com", 80, $errno, $errstr, 30);
if (!$fp) {
    echo "$errstr ($errno)<br />\n";
} else {
    $out = "GET / HTTP/1.1\r\n";
    $out .= "Host: www.example.com\r\n";
    $out .= "Connection: Close\r\n\r\n";
    fwrite($fp, $out);
    while (!feof($fp)) {
        echo fgets($fp, 128);
    }
    fclose($fp);
}

POST访问的例子:

$url = 'http://www.example.com/doRequest.php'; 

$param = array( 
  'name'=>'fdipzone', 
  'gender'=>'male', 
  'age'=>30 
); 

doRequest($url, $param); 

function doRequest($url, $param=array())
{ 
    $urlinfo = parse_url($url); 
    
    $host = $urlinfo['host']; 
    $path = $urlinfo['path']; 
    $query = isset($param)? http_build_query($param) : ''; 
    
    $port = 80; 
    $errno = 0; 
    $errstr = ''; 
    $timeout = 10; 
    
    $fp = fsockopen($host, $port, $errno, $errstr, $timeout); 
    
    $out = "POST ".$path." HTTP/1.1\r\n"; 
    $out .= "host:".$host."\r\n"; 
    $out .= "content-length:".strlen($query)."\r\n"; 
    $out .= "content-type:application/x-www-form-urlencoded\r\n"; 
    $out .= "connection:close\r\n\r\n"; 
    $out .= $query; 
    
    fputs($fp, $out); 
    fclose($fp); 
} 

注意:当执行过程中,客户端连接断开或连接超时,都会有可能造成执行不完整,因此需要加上

ignore_user_abort(true); // 忽略客户端断开 
set_time_limit(0);    // 设置执行不超时

上面的代码向页面 目标服务器地址 发送完请求就不管了,用不着等待请求页面的响应数据,实现了异步调用的目的。

缺点:需要自己拼接header部分,和popen 一样,并发非常多时会产生很多子进程,当达到服务器的连接限制数时,就会挂掉。

php官方全局函数

/**
 * Get the last occurred error
 * @link https://php.net/manual/en/function.error-get-last.php
 * @return array an associative array describing the last error with keys "type",
 * "message", "file" and "line". Returns &null; if there hasn't been an error
 * yet.
 * @since 5.2.0
 */
function error_get_last () {}

/**
 * Call a user function given by the first parameter
 * @link https://php.net/manual/en/function.call-user-func.php
 * @param callback $function <p>
 * The function to be called. Class methods may also be invoked
 * statically using this function by passing
 * array($classname, $methodname) to this parameter.
 * Additionally class methods of an object instance may be called by passing
 * array($objectinstance, $methodname) to this parameter.
 * </p>
 * @param mixed $parameter [optional] <p>
 * Zero or more parameters to be passed to the function.
 * </p>
 * <p>
 * Note that the parameters for call_user_func are
 * not passed by reference.
 * call_user_func example and references
 * &example.outputs;
 * </p>
 * @return mixed the function result, or false on error.
 * @since 4.0
 * @since 5.0
 */
function call_user_func ($function, ...$parameter) {}

/**
 * Call a user function given with an array of parameters
 * @link https://php.net/manual/en/function.call-user-func-array.php
 * @param callback $function <p>
 * The function to be called.
 * </p>
 * @param array $param_arr <p>
 * The parameters to be passed to the function, as an indexed array.
 * </p>
 * @return mixed the function result, or false on error.
 * @since 4.0.4
 * @since 5.0
 */
function call_user_func_array ($function, array $param_arr) {}

/**
 * Call a user method on an specific object
 * @link https://php.net/manual/en/function.call-user-method.php
 * @deprecated 5.3 use call_user_func() instead
 * @param string $method_name
 * @param object $obj 
 * @param mixed $parameter [optional] 
 * @param mixed $_ [optional] 
 * @return mixed 
 * @since 4.0
 * @since 5.0
 */
function call_user_method ($method_name, &$obj, $parameter = null, $_ = null) {}

/**
 * Call a user method given with an array of parameters
 * @link https://php.net/manual/en/function.call-user-method-array.php
 * @deprecated 5.3 use call_user_func_array() instead
 * @param string $method_name
 * @param object $obj 
 * @param array $params 
 * @return mixed 
 * @since 4.0.5
 * @since 5.0
 */
function call_user_method_array ($method_name, &$obj, array $params) {}

/**
 * Call a static method
 * @link https://php.net/manual/en/function.forward-static-call.php
 * @param callback $function <p>
 * The function or method to be called. This parameter may be an array,
 * with the name of the class, and the method, or a string, with a function
 * name.
 * </p>
 * @param mixed $parameter [optional] <p>
 * Zero or more parameters to be passed to the function.
 * </p>
 * @param mixed $_ [optional] 
 * @return mixed the function result, or false on error.
 * @since 5.3.0
 */
function forward_static_call ($function, $parameter = null, $_ = null) {}

/**
 * Call a static method and pass the arguments as array
 * @link https://php.net/manual/en/function.forward-static-call-array.php
 * @param callback $function <p>
 * The function or method to be called. This parameter may be an &array;,
 * with the name of the class, and the method, or a &string;, with a function
 * name.
 * </p>
 * @param array $parameters [optional] 
 * @return mixed the function result, or false on error.
 * @since 5.3.0
 */
function forward_static_call_array ($function, array $parameters = null) {}

/**
 * Generates a storable representation of a value
 * @link https://php.net/manual/en/function.serialize.php
 * @param mixed $value <p>
 * The value to be serialized. serialize
 * handles all types, except the resource-type.
 * You can even serialize arrays that contain
 * references to itself. Circular references inside the array/object you 
 * are serializing will also be stored. Any other 
 * reference will be lost.
 * </p>
 * <p>
 * When serializing objects, PHP will attempt to call the member function
 * __sleep prior to serialization. 
 * This is to allow the object to do any last minute clean-up, etc. prior 
 * to being serialized. Likewise, when the object is restored using 
 * unserialize the __wakeup member function is called.
 * </p>
 * <p>
 * Object's private members have the class name prepended to the member
 * name; protected members have a '*' prepended to the member name.
 * These prepended values have null bytes on either side.
 * </p>
 * @return string a string containing a byte-stream representation of 
 * value that can be stored anywhere.
 * @since 4.0
 * @since 5.0
 */
function serialize ($value) {}

/**
 * Creates a PHP value from a stored representation
 * @link https://php.net/manual/en/function.unserialize.php
 * @param string $str <p>
 * The serialized string.
 * </p>
 * <p>
 * If the variable being unserialized is an object, after successfully 
 * reconstructing the object PHP will automatically attempt to call the
 * __wakeup member function (if it exists).
 * </p>
 * <p>
 * unserialize_callback_func directive
 * <p>
 * It's possible to set a callback-function which will be called,
 * if an undefined class should be instantiated during unserializing.
 * (to prevent getting an incomplete object "__PHP_Incomplete_Class".)
 * Use your &php.ini;, ini_set or &htaccess; 
 * to define 'unserialize_callback_func'. Everytime an undefined class
 * should be instantiated, it'll be called. To disable this feature just
 * empty this setting.
 * </p>
 * @param mixed $options [optional]
 * <p>Any options to be provided to unserialize(), as an associative array.</p>
 * <p>
 * Either an array of class names which should be accepted, FALSE to
 * accept no classes, or TRUE to accept all classes. If this option is defined
 * and unserialize() encounters an object of a class that isn't to be accepted,
 * then the object will be instantiated as __PHP_Incomplete_Class instead.
 * Omitting this option is the same as defining it as TRUE: PHP will attempt
 * to instantiate objects of any class.
 * </p>
 * @return mixed The converted value is returned, and can be a boolean,
 * integer, float, string,
 * array or object.
 * </p>
 * <p>
 * In case the passed string is not unserializeable, false is returned and
 * E_NOTICE is issued.
 * @since 4.0
 * @since 5.0
 * @since 7.0
 */
function unserialize ($str, array $options = null) {}

php预定义常量

/**
 * The full path and filename of the file. If used inside an include,
 * the name of the included file is returned.
 * Since PHP 4.0.2, <b>__FILE__</b> always contains an
 * absolute path with symlinks resolved whereas in older versions it contained relative path
 * under some circumstances.
 * @link https://php.net/manual/en/language.constants.predefined.php
 */
define ('__FILE__', '', true);

/**
 * The current line number of the file.
 * @link https://php.net/manual/en/language.constants.predefined.php
 */
define ('__LINE__', 0, true);

/**
 * The class name. (Added in PHP 4.3.0) As of PHP 5 this constant
 * returns the class name as it was declared (case-sensitive). In PHP
 * 4 its value is always lowercased. The class name includes the namespace
 * it was declared in (e.g. Foo\Bar).
 * Note that as of PHP 5.4 __CLASS__ works also in traits. When used
 * in a trait method, __CLASS__ is the name of the class the trait
 * is used in.
 * @link https://php.net/manual/en/language.constants.predefined.php
 */
define ('__CLASS__', '', true);

/**
 * The function name. (Added in PHP 4.3.0) As of PHP 5 this constant
 * returns the function name as it was declared (case-sensitive). In
 * PHP 4 its value is always lowercased.
 * @link https://php.net/manual/en/language.constants.predefined.php
 */
define ('__FUNCTION__', '', true);

/**
 * The class method name. (Added in PHP 5.0.0) The method name is
 * returned as it was declared (case-sensitive).
 * @link https://php.net/manual/en/language.constants.predefined.php
 */
define ('__METHOD__', '', true);

/**
 * The trait name. (Added in PHP 5.4.0) As of PHP 5.4 this constant
 * returns the trait as it was declared (case-sensitive). The trait name includes the namespace
 * it was declared in (e.g. Foo\Bar).
 * @since 5.4.0
 * @link https://php.net/manual/en/language.constants.predefined.php
 */
define ('__TRAIT__', '', true);

/**
 * The directory of the file. If used inside an include,
 * the directory of the included file is returned. This is equivalent
 * to dirname(__FILE__). This directory name
 * does not have a trailing slash unless it is the root directory.
 * @since 5.3.0
 * @link https://php.net/manual/en/language.constants.predefined.php
 */
define ('__DIR__', '', true);

/**
 * The name of the current namespace (case-sensitive). This constant
 * is defined in compile-time (Added in PHP 5.3.0).
 * @since 5.3.0
 * @link https://php.net/manual/en/language.constants.predefined.php
 */
define ('__NAMESPACE__', '', true);






参考资料

PHP回调函数用法及分析 https://blog.csdn.net/demored/article/details/100515325

同步调用、回调和异步调用(callback_function) https://blog.csdn.net/jo_joe/article/details/79068093

php 不等待返回的实现方法(异步调用) https://www.cnblogs.com/fps2tao/p/7900677.html

PHP 手册 函数参考 其它服务 网络 网络 函数 fsockopen https://www.php.net/fsockopen/

PHP异步执行的几种常用方式 https://www.php.cn/php-weizijiaocheng-383499.html

回调函数的原理及PHP实例 https://blog.csdn.net/whq19890827/article/details/47702881

4种PHP回调函数风格 https://blog.csdn.net/qq_30002351/article/details/83035537

我入职阿里后,才知道原来简历这么写 https://blog.csdn.net/qing_gee/article/details/104839150


返回