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